getLayoutPosition() 和 getAdapterPosition() 有什么区别?

时间:2021-03-30 22:10:07

标签: java android

getAdapterPosition()getLayoutPosition 有什么区别?

https://proandroiddev.com/difference-between-position-getadapterposition-and-getlayoutposition-in-recyclerview-80279a2711d1。我已经阅读了这篇文章,但不是很了解关于 getLayoutPosition()

的部分

请用简单的术语解释它及其背后的过程。

您在构建应用时会在什么时候使用其中之一?

谢谢。

3 个答案:

答案 0 :(得分:2)

简答

getAdapterPosition() 在任何 ViewHolder 调用之后立即返回当前 notify*() 对象的更新位置。

getLayoutPosition() 在调度新布局后稍后返回更新的值。

长答案

getAdapterPosition()

getAdapterPosition() 中的数据源发生变化时,notify* 返回的适配器位置将在您进行的 Adapter 次调用之一中立即更新:

notifyItemInserted(...)
notifyItemRemoved(...)
...
notifyDataSetChanged()

例如,如果我们查看 notifyItemInserted() 然后它会调用:

notifyItemInserted(int position)
              |
              v
             ...
              |
              v  
public void onItemRangeInserted(int positionStart, int itemCount) {
    assertNotInLayoutOrScroll(null);
    if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
        triggerUpdateProcessor();
    }
}

mAdapterHelper.onItemRangeInserted(positionStart, itemCount) 基本上会更新用于检索当前适配器位置的 mPendingUpdates。下一次调用 triggerUpdateProcessor() 将请求布局更新,但在下一节中会有更多内容。

现在,如果您之后立即为您的 getAdapterPosition() 调用 ViewHolder,它将返回更新后的位置。但是,getLayoutPosition() 仍将返回旧值。

getLayoutPosition()

triggerUpdateProcessor 将调用 requestLayout() 并返回。这将安排 RecyclerView 的布局更新(稍后将调用 onLayout 来表示布局更改):

    public void onItemRangeInserted(int positionStart, int itemCount) {
        assertNotInLayoutOrScroll(null);
        if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
            triggerUpdateProcessor();
        }
    }

不过,这需要一些时间,但在触发 onLayout 回调并调用 dispatchLayout() 后,更新将成为可能:

protected void onLayout(boolean changed, int l, int t, int r, int b) {
    ...
    dispatchLayout();
    ...
}

基本上,onLayout 返回后,布局位置已经更新。

插图

这可以用一个简单的 RecyclerView 来说明,其中在列表顶部添加新元素时调用 notifyInserted()

...
        public void addElementToHead() {
            elements.add(0, "New Element");
            notifyItemInserted(0);
            Toast.makeText(context, "Adapter position : " + currentFirstHolder.getAdapterPosition() +
                    ", Layout position : " + currentFirstHolder.getLayoutPosition(), Toast.LENGTH_LONG).show();
        }
...

在这种情况下,currentFirstHolder 是一个 ViewHolder,对应于适配器中的初始第一个元素。但是,在添加新元素作为第一个元素后,currentFirstHolder 的位置必须更改为 1。因此,getAdapterPosition()1,而 getLayoutPosition() 仍然是 0,因为 RecyclerView 尚未完成布局更新。

enter image description here

这里我记录了 ViewHolder 的内容,以显示在 onLayout 最终被调用后它是如何变化的:

...
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);

    ViewHolder viewHolder = findViewHolderForAdapterPosition(0);
    Toast.makeText(getContext(), "From RecyclerView#onLayout - Adapter position : " +
            viewHolder.getAdapterPosition() + ", Layout position : " + viewHolder.getLayoutPosition(), Toast.LENGTH_LONG).show();
}
...

因此,它将如下所示(它将在第一个 toast 消失后立即显示):

enter image description here

现在,可以在下面看到完整的相关代码。为简洁起见,我没有包含布局和其他文件。

MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        RecyclerViewAdapter adapter = new RecyclerViewAdapter(this);
        MyRecyclerView recyclerView = findViewById(R.id.recycler_view);
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        Button button = findViewById(R.id.add_element_button);
        button.setOnClickListener(v -> adapter.addElementToHead());
    }

    private static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {
        private final Context context;
        private final List<String> elements = new ArrayList<>();

        private RecyclerViewAdapter(Context context) {
            this.context = context;

            for (int i = 0; i < 1; i++) {
                elements.add("Element " + i);
            }
        }

        public void addElementToHead() {
            elements.add(0, "New Element");
            notifyItemInserted(0);
            Toast.makeText(context, "Adapter position : " + currentFirstHolder.getAdapterPosition() +
                    ", Layout position : " + currentFirstHolder.getLayoutPosition(), Toast.LENGTH_LONG).show();
        }

        private RecyclerViewHolder currentFirstHolder;

        @NonNull
        @Override
        public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View item = LayoutInflater.from(context).
                    inflate(R.layout.list_item, parent, false);
            return new RecyclerViewHolder(item);
        }

        @Override
        public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
            holder.textView.setText(elements.get(position));

            if (position == 0) {
                currentFirstHolder = holder;
            }
        }

        @Override
        public int getItemCount() {
            return elements.size();
        }

        public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
            private final TextView textView;

            public RecyclerViewHolder(@NonNull View itemView) {
                super(itemView);

                this.textView = itemView.findViewById(R.id.element_view);
            }
        }
    }
}

MyRecyclerView.java

public class MyRecyclerView extends RecyclerView {
    public MyRecyclerView(@NonNull Context context) {
        super(context);
    }

    public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);

        ViewHolder viewHolder = findViewHolderForAdapterPosition(0);
        Toast.makeText(getContext(), "From RecyclerView#onLayout - Adapter position : " +
                viewHolder.getAdapterPosition() + ", Layout position : " + viewHolder.getLayoutPosition(), Toast.LENGTH_LONG).show();
    }
}

答案 1 :(得分:0)

getLayoutPosition() 函数根据最新的布局传递返回 ViewHolder 的位置,但 getAdapterPosition() 返回由此 ViewHolder 表示的项目的 Adapter 位置,这可能会有所不同如果有待处理的适配器更新但尚未发生新的布局传递,则比 getLayoutPosition() 更。

答案 2 :(得分:0)

getAdapterPosition() 已弃用,新方法为 getBindingAdapterPosition()

返回此 ViewHolder 表示的项目相对于绑定它的 RecyclerView.Adapter 的适配器位置。


getLayoutPosition()

根据最新的布局传递返回 ViewHolder 的位置。

此位置主要由 RecyclerView 组件使用,以在 RecyclerView 延迟处理适配器更新时保持一致。


Official doc ref

Medium ref

相关问题