RecyclerView 回收时出现问题

2024-01-25

我有一个我使用创建的项目列表RecyclerView。当用户单击其中之一时,我会更改所选项目的背景颜色。 问题是,当我滚动浏览我的项目并回收它们时,某些项目会获得所选项目的背景颜色(这是错误的)。 在这里你可以看到我的Adapter's code:

public class OrderAdapter extends RecyclerView.Adapter<OrderAdapter.ViewHolder> {

private static final String SELECTED_COLOR = "#ffedcc";

private List<OrderModel> mOrders;

public OrderAdapter() {
    this.mOrders = new ArrayList<>();
}

public void setOrders(List<OrderModel> orders) {
    mOrders = orders;
}

public void addOrders(List<OrderModel> orders) {
    mOrders.addAll(0, orders);
}

public void addOrder(OrderModel order) {
    mOrders.add(0, order);
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    Context context = parent.getContext();
    LayoutInflater inflater = LayoutInflater.from(context);

    // Inflate the custom layout
    View contactView = inflater.inflate(R.layout.order_main_item, parent, false);

    // Return a new holder instance
    ViewHolder viewHolder = new ViewHolder(contactView);
    return viewHolder;
}

@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
    final OrderModel orderModel = mOrders.get(position);

    // Set item views based on the data model
    TextView customerName = viewHolder.customerNameText;

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/dd/yyyy'   'HH:mm:ss:S");
    String time = simpleDateFormat.format(orderModel.getOrderTime());
    customerName.setText(time);

    TextView orderNumber = viewHolder.orderNumberText;
    orderNumber.setText("Order No: " + orderModel.getOrderNumber());

    Button button = viewHolder.acceptButton;
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            viewHolder.userActions.acceptButtonClicked(position);
        }
    });

    final LinearLayout orderItem = viewHolder.orderItem;
    orderItem.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            viewHolder.userActions.itemClicked(orderModel);
            viewHolder.orderItem.setBackgroundColor(Color.parseColor(SELECTED_COLOR));
        }
    });
}

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


public static class ViewHolder extends RecyclerView.ViewHolder implements OrderContract.View {

    public TextView customerNameText;
    public Button acceptButton;
    public TextView orderNumberText;
    public OrderContract.UserActions userActions;
    public LinearLayout orderItem;

    public ViewHolder(View itemView) {
        super(itemView);

        userActions = new OrderPresenter(this);

        customerNameText = (TextView) itemView.findViewById(R.id.customer_name);
        acceptButton = (Button) itemView.findViewById(R.id.accept_button);
        orderNumberText = (TextView) itemView.findViewById(R.id.order_number);
        orderItem = (LinearLayout) itemView.findViewById(R.id.order_item_selection);
    }

    @Override
    public void removeItem() {

    }
}

问题是recyclerView回收行为将您分配到屏幕外ViewHolder项目到即将在屏幕上显示的新项目。 我不建议你根据以下内容绑定你的逻辑ViewHolder与上述所有答案一样的对象。这确实会给你带来麻烦。 您应该根据数据对象的状态构建逻辑,而不是ViewHolder你永远不会知道它何时被回收的对象。

假设你保存了一个 状态布尔值是否被选中 in ViewHolder检查,但如果这是真的,那么当这个viewHolder将被回收。

执行上述操作的更好方法是在 DataModel 对象中保存任何状态。在你的情况下只是一个布尔值被选中。

示例示例如

package chhimwal.mahendra.multipleviewrecyclerproject;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.support.v7.widget.CardView;
import android.widget.TextView;

import java.util.List;

/**
 * Created by mahendra.chhimwal on 12/10/2015.
 */
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {

    private Context mContext;
    private List<DataModel> mRViewDataList;


    public MyRecyclerViewAdapter(Context context, List<DataModel> rViewDataList) {
        this.mContext = context;
        this.mRViewDataList = rViewDataList;
    }

    @Override
    public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.item_recycler_view, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.bindDataWithViewHolder(mRViewDataList.get(position));
    }

    @Override
    public int getItemCount() {
        return mRViewDataList != null ? mRViewDataList.size() : 0;
    }


    public class ViewHolder extends RecyclerView.ViewHolder {
        private TextView textView;
        private LinearLayout llView;
        private DataModel mDataItem=null;

        public ViewHolder(View itemView) {
            super(itemView);
            llView=(LinearLayout)itemView.findViewById(R.id.ll_root_view);
            textView = (TextView) itemView.findViewById(R.id.tvItemName);
            cvItemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                  // One should handle onclick of event here based on the dataItem i.e. mDataItem in this case.
                  // something like that..
                /* Intent intent = new Intent(mContext,ResultActivity.class);
                 intent.putExtra("MY_DATA",mDataItem);   //If you want to pass data.
                 intent.putExtra("CLICKED_ITEM_POSTION",getAdapterPosition()); // If one want to get selected item position
                 startActivity(intent);*/
                 Toast.makeText(mContext,"You clicked item number "+ViewHolder.this.getAdapterPosition(),Toast.LENTH_SHORT).show();
                }
            });
        }

        //This is clean method to bind data with viewHolder. Do all dirty things on View based on dataItem.
        //Must be called from onBindViewHolder(),with dataItem. In our case dataItem is String object.
        public void bindDataWithViewHolder(DataModel dataItem){
            this.mDataItem=dataItem;

            if(mDataItem.isSelected()){
                llView.setBackgroundColor(Color.ParseColor(SELCTED_COLOR);
            }else{
                llView.setBackgroundColor(Color.ParseColor(DEFAULT_COLOR);
            }
            //other View binding logics like setting text , loading image  etc.
            textView.setText(mDataItem);
        }
    }
}

正如@Gabriel 在评论中问道,

如果想一次选择一个项目怎么办?

在这种情况下,再次不应将所选项目状态保存在ViewHolder对象,同样它会被回收并给你带来问题。更好的方法是拥有一个字段int selectedItemPosition in Adapter类不ViewHolder。 下面的代码片段展示了它。

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {



        private Context mContext;
        private List<DataModel> mRViewDataList;

        //variable to hold selected Item position
        private int mSelectedItemPosition = -1;


        public MyRecyclerViewAdapter(Context context, List<DataModel> rViewDataList) {
            this.mContext = context;
            this.mRViewDataList = rViewDataList;
        }

        @Override
        public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View view = inflater.inflate(R.layout.item_recycler_view, parent, false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            holder.bindDataWithViewHolder(mRViewDataList.get(position),position);
        }

        @Override
        public int getItemCount() {
            return mRViewDataList != null ? mRViewDataList.size() : 0;
        }


        public class ViewHolder extends RecyclerView.ViewHolder {
            private TextView textView;
            private LinearLayout llView;
            private DataModel mDataItem=null;

            public ViewHolder(View itemView) {
                super(itemView);
                llView=(LinearLayout)itemView.findViewById(R.id.ll_root_view);
                textView = (TextView) itemView.findViewById(R.id.tvItemName);
                cvItemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //Handling for background selection state changed
                        int previousSelectState=mSelectedItemPosition;
                        mSelectedItemPosition = getAdapterPosition();
                        //notify previous selected item
                        notifyItemChanged(previousSelectState);
                        //notify new selected Item
                        notifyItemChanged(mSelectedItemPosition);

                        //Your other handling in onclick

                    }
                });
            }

            //This is clean method to bind data with viewHolder. Do all dirty things on View based on dataItem.
            //Must be called from onBindViewHolder(),with dataItem. In our case dataItem is String object.
            public void bindDataWithViewHolder(DataModel dataItem, int currentPosition){
                this.mDataItem=dataItem;
                //Handle selection  state in object View.
                if(currentPosition == mSelectedItemPosition){
                    llView.setBackgroundColor(Color.ParseColor(SELCTED_COLOR);
                }else{
                    llView.setBackgroundColor(Color.ParseColor(DEFAULT_COLOR);
                }
                //other View binding logics like setting text , loading image  etc.
                textView.setText(mDataItem);
            }
        }
    }

如果您只需维护选定的 Item 状态,我强烈建议不要使用通知数据集更改()RecyclerView 等 Adapter 类的方法为这些情况提供了更多的灵活性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RecyclerView 回收时出现问题 的相关文章

  • BottomNavigationView - 如何获取选定的菜单项?

    我使用BottomNavigationView来切换片段 如何获取当前选定的菜单项 以防止重新打开片段 BottomNavigationView bottomNavigationView BottomNavigationView findV
  • 在应用程序简历中隐藏软键盘

    我有一个 Android 应用程序 使用 Xamarin 用 C 编写 我已将应用程序简化为包含 TextView 和用于横幅广告的 Google admod AdView 的 LinearLayout 我不希望软键盘出现在应用程序中 这不
  • 使用 Android 前台服务为 MediaPlayer 创建通知

    问题就在这里 我目前正在开发一个应用程序 该应用程序必须提供 A 广播播放器 来自 URL 的 AAC 直播 还有一个播客播放器 来自 URL 的 MP3 流 该应用程序必须能够在后台运行 Android 服务 并通过以下方式向用户公开持续
  • Android 应用程序在后台运行时保存数据

    目前我正在开发 xmmp 客户端 当应用程序位于前台时 该客户端工作得很好 但由于事实上 当应用程序处于后台时 我在 Application 类中保存了大量数据 复杂的 ArrayList 字符串和布尔值作为公共静态 每个字段都被垃圾收集
  • 菜单未显示在应用程序中

    由于某种原因 我的操作菜单在我的 Android Studio 应用程序中消失了 我正在按照教程学习如何创建 Android 应用程序 但最终遇到了这个问题 我正在使用 atm 的教程 http www raywenderlich com
  • RxJava、Proguard 和 sun.misc.Unsafe

    我有以下问题RxJava 1 1 0 使用时Proguard 我没有更改 RxJava 版本或其 pro文件 但更新后OkHttp我无法编译使用Proguard因为我有关于sun misc Unsafe不在场 rxJava pro keep
  • 设置从 Facebook 登录获取用户电子邮件 ID 的权限

    我在用着Facebook 3 0 SDK对于安卓 我必须实施Facebook登录 我正在访问用户的基本信息 例如姓名 用户 ID 但我也想访问用户的电子邮件 我浏览了很多博客和论坛 但不知道该怎么做 我正在使用我自己的 android 按钮
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • 更新到材质 1.2.0 后,材质按钮上缺少圆角半径属性

    这是我的材质按钮代码
  • Firebase:如何在Android应用程序中设置默认通知渠道?

    如何设置default通知渠道通知消息当应用程序在后台运行时会出现什么情况 默认情况下 这些消息使用 杂项 通道 如你看到的在官方文档中 https firebase google com docs cloud messaging andr
  • Android 版 Robotium - solo.searchText () 不起作用

    我在使用 Robotium 时遇到 searchText 函数问题 我正在寻找这个字符串
  • CookieManager.getInstance().removeAllCookie();不删除所有cookie

    我在应用程序的 onCreate 中调用 CookieManager getInstance removeAllCookie 我遇到了一个奇怪的问题 我看到 GET 请求中传递了意外的 cookie 值 事实上 cookie 值是一个非常非
  • 带有自定义阵列适配器的微调器不允许选择项目

    我使用自定义阵列适配器作为微调器 但是 当在下拉列表中选择一个项目时 下拉列表保留在那里 并且微调器不会更新 这是错误行为 与使用带有字符串的通用数组适配器相比 这是自定义类 我错过了什么吗 谢谢 public class Calendar
  • Android Studio:无法启动守护进程

    当我尝试在 Android Studio 中导入 gradle 项目时 遇到以下错误 Unable to start the daemon process This problem might be caused by incorrect
  • 将 JSON 参数从 java 发布到 sinatra 服务

    我有一个 Android 应用程序发布到我的 sinatra 服务 早些时候 我无法读取 sinatra 服务上的参数 但是 在我将内容类型设置为 x www form urlencoded 之后 我能够看到参数 但不完全是我想要的 我在
  • SharedFlow 和 StateFlow 的主要区别

    两者有什么区别共享流 and 状态流 以及如何使用这些MVI建筑学 使用简单更好吗Flow或者这些作为状态和事件 Flow 是冷的 意味着它仅在收集数据时才发出数据 另外Flow不能保存数据 可以把它看成是水在里面流动的管道 Flow中的数
  • Android:有没有办法以毫安为单位获取设备的电池容量?

    我想获取设备的电池容量来进行一些电池消耗计算 是否可以以某种方式获取它 例如 三星 Galaxy Note 2 的电池容量为 3100mAh 谢谢你的帮助 知道了 在 SDK 中无法直接找到任何内容 但可以使用反射来完成 这是工作代码 pu
  • 用于推送通知的设备令牌

    我正在实施推送通知服务 我需要创建一个数据库来存储 4 个移动平台的所有设备令牌 我想根据他们的平台 iOS Android BlackBerry WP7 来组织它们 但是有什么方法可以区分平台 这样如果我只想向 Android 用户发送消
  • 如何删除因 Google Fitness API 7.5.0 添加的权限

    将我的 play services fitness api 从 7 0 0 更新到 7 5 0 后 我注意到当我将新版本上传到 PlayStore 时 它 告诉我正在添加一个新权限和 2 个新功能 我没有这样做 有没有搞错 在做了一些研究来
  • 在 Google 地图上绘制线条/路径

    我很长一段时间都在忙于寻找如何在 HelloMapView 中的地图上的两个 GPS 点之间画一条线 但没有运气 谁能告诉我该怎么做 假设我使用扩展 MapView 的 HelloMapView 我需要使用叠加层吗 如果是这样 我是否必须重

随机推荐

  • Hive 无法手动设置减速器数量

    我有以下配置单元查询 select count distinct id as total from mytable 它会自动生成 1408 制图员1 减速机 我需要手动设置减速器的数量 我尝试了以下方法 set mapred reduce
  • 具有不透明度的重叠元素并处理这些元素上的“悬停”

    这是一个关于如何使用 JS 解决方案处理重叠元素的不透明度并使其在悬停时保持一致的问答 要求 要求是开发两个元素 透明且重叠 如下图两个红框 这些需要是透明的 以便背景内容可见 现在 当鼠标悬停在任何这些元素上时 特定元素应该变得不透明 如
  • 如何解析某些标签中带有冒号的 XML?

    我一直在阅读一些教程XmlPullParser在Android中如何解析XML数据 更具体地说 我使用的 XMLhttps gdata youtube com feeds api standardfeeds top rated 在这里 我简
  • 跳过 JavaScript 数组的 take 方法

    是否有方法可以跳过特定数量的对象并从 JavaScript 数组中获取一定数量的对象 基本上我正在寻找的模式是这样的 假设我有一个包含 8 个对象的数组 第一个循环 从数组中返回索引 0 到 3 处的对象 第二个循环 从数组中返回索引 4
  • 为什么golang RGBA.RGBA()方法使用|和<<?

    在golang color包中 有一个方法可以从an中获取r g b a值RGBA object func c RGBA RGBA r g b a uint32 r uint32 c R r r lt lt 8 g uint32 c G g
  • Apache CXF 和 servlet 映射

    我正在尝试学习 Apache CXF 的一些基础知识以及有关 servlet 映射的一般知识 我在这里修改了代码 https subversion assembla com svn pablo examples spring cxf exa
  • 使用 lodash 的 isEqual() 在比较中排除一些属性

    我在用 是平等的 https lodash com docs isEqual它比较 2 个对象数组 例如 每个对象 10 个属性 并且工作正常 现在有 2 个属性 创建和删除 我不需要成为比较的一部分 Example var obj1 na
  • 如何将第一个孩子移到最后?

    我在 div 容器中有一些 div 项目 我想连续地为它们设置动画 我知道如何在无限循环中运行我的函数 但是选择第一个 div 对其进行动画处理并在完成动画后将其移动到末尾存在问题 我的函数如下所示 function MoveItems c
  • 将 XAML 行为附加到相同类型的所有控件

    我有一个InvokeCommandAction我所拥有的附加到GotFocus的事件TextBox像这样
  • Laravel 将文件上传到项目目录之外的不同存储

    我正在创建一个 CMS 其中我可以使用 Laravel 的文件上传来上传文件 照片 pdf 等 我所做的不同之处在于 我想将文件存储在 CMS 项目目录之外 比如说我网站的存储文件夹 顺便说一句 我正在创建两个不同的项目 Laravel 文
  • CFG 在 NLTK 中使用 POS 标签 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 针对特定包的自定义意图

    假设我的应用程序是 A 我使用应用程序 A 中的自定义意图启动应用程序 B 的活动 它工作得很好 正如我所希望的那样 我使用的代码是在应用程序 B 的清单中
  • 使用 printf 格式化输出:截断或填充

    我想产生以下输出 gt Avril Stewart 99 54 gt Sally Kinghorn 170 60 gt John Young 195 120 gt Yutte Schim 250 40 如您所见 短于 14 个字符的名称会用
  • 接收 zip 文件,angularJs

    当我想从 Rest api 下载 zip 文件时遇到问题 当 zip 文件从我的服务器 带有球衣 传输时 我收到的文件已损坏 我已经尝试过responseType arraybuffer 在我的 http 请求上 但它没有解决任何问题 这是
  • 类似 XPath 的嵌套 Python 字典查询

    有没有办法为嵌套 python 字典定义 XPath 类型查询 像这样的事情 foo spam eggs morefoo bar soap morebar bacon foobar print foo select morefoo more
  • “constexpr if”与“if”的优化 - 为什么需要“constexpr”?

    C 1z 将引入 constexpr if 根据条件删除一个分支的 if 看起来合理且有用 但是 没有 constexpr 关键字就不可能了吗 我认为在编译期间 编译器应该知道编译期间是否已知条件 如果是的话 即使是最基本的优化级别也应该删
  • 更改 jquery 工具提示箭头的位置

    我正在尝试将箭头的位置更改为文本框附近的左侧 我怎样才能解决这个问题 我已经尝试过这个 工作示例链接 http jsfiddle net b8fcg http jsfiddle net b8fcg HTML
  • Java EE 6 发布日期 [已关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions Java
  • 用于操作 S-Record 和 Intel HEX 16 文件的库

    是否有可用的开源库 用 python 或 java 开发 用于操作 Motorola S Record 文件和 Intel HEX 16 文件 例如从一种格式转换为另一种格式 我正在寻找一个 纯 java或python库 而不仅仅是一组ja
  • RecyclerView 回收时出现问题

    我有一个我使用创建的项目列表RecyclerView 当用户单击其中之一时 我会更改所选项目的背景颜色 问题是 当我滚动浏览我的项目并回收它们时 某些项目会获得所选项目的背景颜色 这是错误的 在这里你可以看到我的Adapter s code