RecyclerView使用onBindViewHolder时,同一个Item总是有两个viewholder对象

RecyclerView中有两个Api来绑定数据:onBindViewHolder( RecyclerView.ViewHolder holder, int position)和带参数onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads)局部绑定/刷新数据。

场景:RecyclerView使用viewHolder保存数据时,同一个Item总是有两个viewHolder对象。比如保存上一次的下载时间,百分比等
比如需要在viewHolder中保存上次刷新的数据时,同一个item有两个viewHolder对象,肯定时不行的,这时只需要在mRecycler中设置setSupportsChangeAnimations()false即可。

原因: RecyclerView默认情况下会创建ViewHolder的另一个副本,以便将视图相互淡化。

另外,当时用带参数的 onBindViewHolder时,因为旧的ViewHolder获取payloads,但新的ViewHolder没有获取到payloads,也需要配置如下代码。

((SimpleItemAnimator)mRecycler.getItemAnimator()).setSupportsChangeAnimations(false);

RecyclerView源码:ItemAnimator 决定使用同一个ViewHolder 还是重新创建ViewHolder,canReuseUpdatedViewHolder()默认返回为true。若为true,则使用同一个ViewHolder。
SimpleItemAnimator继承RecyclerView,通过setSupportsChangeAnimations(),给全局变量mSupportsChangeAnimations (默认为true)设置为false;那么canReuseUpdatedViewHolder()返回为true。
DefaultItemAnimator继承SimpleItemAnimator:canReuseUpdatedViewHolder()中调用super.canReuseUpdatedViewHolder(viewHolder, payloads);即SimpleItemAnimator中的canReuseUpdatedViewHolder()。

RecyclerView源码:

/**
         * When an item is changed, ItemAnimator can decide whether it wants to re-use
         * the same ViewHolder for animations or RecyclerView should create a copy of the
         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
         * <p>
         * Note that this method will only be called if the {@link ViewHolder} still has the same
         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
         * both {@link ViewHolder}s in the
         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
         *
         * @param viewHolder The ViewHolder which represents the changed item's old content.
         * @param payloads A non-null list of merged payloads that were sent with change
         *                 notifications. Can be empty if the adapter is invalidated via
         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
         *                 payloads will be passed into
         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
         *                 method <b>if</b> this method returns <code>true</code>.
         *
         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
         *         ItemAnimator to animate. Default implementation calls
         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
         *
         * @see #canReuseUpdatedViewHolder(ViewHolder)
         */
        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
                @NonNull List<Object> payloads) {
            return canReuseUpdatedViewHolder(viewHolder);
        }


     public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder            viewHolder) {
            return true;
        }

SimpleItemAnimator源码:

boolean mSupportsChangeAnimations = true;

    /**
     * Returns whether this ItemAnimator supports animations of change events.
     *
     * @return true if change animations are supported, false otherwise
     */
    @SuppressWarnings("unused")
    public boolean getSupportsChangeAnimations() {
        return mSupportsChangeAnimations;
    }

    /**
     * Sets whether this ItemAnimator supports animations of item change events.
     * If you set this property to false, actions on the data set which change the
     * contents of items will not be animated. What those animations do is left
     * up to the discretion of the ItemAnimator subclass, in its
     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} implementation.
     * The value of this property is true by default.
     *
     * @param supportsChangeAnimations true if change animations are supported by
     *                                 this ItemAnimator, false otherwise. If the property is false,
     *                                 the ItemAnimator
     *                                 will not receive a call to
     *                                 {@link #animateChange(ViewHolder, ViewHolder, int, int, int,
     *                                 int)} when changes occur.
     * @see Adapter#notifyItemChanged(int)
     * @see Adapter#notifyItemRangeChanged(int, int)
     */
    public void setSupportsChangeAnimations(boolean supportsChangeAnimations) {
        mSupportsChangeAnimations = supportsChangeAnimations;
    }

    /**
     * {@inheritDoc}
     *
     * @return True if change animations are not supported or the ViewHolder is invalid,
     * false otherwise.
     *
     * @see #setSupportsChangeAnimations(boolean)
     */
    @Override
    public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
        return !mSupportsChangeAnimations || viewHolder.isInvalid();
    }

DefaultItemAnimator源码:

 /**
     * {@inheritDoc}
     * <p>
     * If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
     * When this is the case:
     * <ul>
     * <li>If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both
     * ViewHolder arguments will be the same instance.
     * </li>
     * <li>
     * If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)},
     * then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and
     * run a move animation instead.
     * </li>
     * </ul>
     */
    @Override
    public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
            @NonNull List<Object> payloads) {
        return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
    }
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一个在RecyclerView使用有六个TextView每行显示两个的示例代码: 首先,在你的布局文件中定义RecyclerView: ```xml <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 接下来,在你的Activity或Fragment中初始化RecyclerView和Adapter: ```java RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new GridLayoutManager(this, 2)); recyclerView.setAdapter(new MyAdapter()); ``` 注意,这里使用了GridLayoutManager来实现每行显示两个TextView的效果。 最后,定义自己的Adapter,例如: ```java public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private List<String> mData = new ArrayList<>(); public MyAdapter() { for (int i = 0; i < 6; i++) { mData.add("TextView " + i); } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_textview, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.textView.setText(mData.get(position)); } @Override public int getItemCount() { return mData.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { TextView textView; public ViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.text_view); } } } ``` 在这个示例中,我们使用了一个包含六个TextView的数据集合mData,通过Adapter将其绑定到RecyclerView中。在ViewHolder中,我们将TextView绑定到布局文件中定义的R.id.text_view上。 最后,布局文件item_textview.xml中可以定义如下: ```xml <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:padding="8dp" /> ``` 这个布局文件中只定义了一个TextView,其他的TextView会通过RecyclerView的复用机制动态生成。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值