android - 这是在 Android 中实现 RecyclerView 的错误方法吗?

标签 android android-recyclerview android-adapter android-viewholder

我正在尝试在我的应用中实现 RecyclerView。这是我的适配器类:

public class AdapterItemsList extends RecyclerView.Adapter<AdapterItemsList.ViewHolderItems>
{
    private ArrayList<CItem> items;

    public AdapterItemsList(ArrayList<CItem> items)
    {
        this.items = items;
    }

    @NonNull
    @Override
    public ViewHolderItems onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
    {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_counter, null, false);

        return new ViewHolderItems(view);
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolderItems holder, final int position)
    {
        holder.asignarDatos(items.get(holder.getAdapterPosition()));

        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {

                //deleteSelectedItem() method gonna be here

                Toast.makeText(view.getContext(), String.valueOf(holder.getAdapterPosition())
                        , Toast.LENGTH_SHORT).show();
                return false;
            }
        });

        holder.counterAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
                auxInteger += 1;
                holder.itemNumber.setText(auxInteger.toString());
                items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
            }
        });

        holder.counterSubtract.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
                if (auxInteger > 0)
                {
                    auxInteger -= 1;
                    holder.itemNumber.setText(auxInteger.toString());
                    items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
                }
            }
        });
    }

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

    public class ViewHolderItems extends RecyclerView.ViewHolder
    {
        TextView itemName;
        TextView itemNumber;
        Button counterAdd;
        Button counterSubtract;

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

            itemName = (TextView) itemView.findViewById(R.id.textViewItemCounterName);
            itemNumber = (TextView) itemView.findViewById(R.id.textViewItemCounterNumber);
            counterAdd = (Button) itemView.findViewById(R.id.buttonItemCounterAdd);
            counterSubtract = (Button) itemView.findViewById(R.id.buttonItemCounterSubtract);
        }

        public void asignarDatos(CItem item)
        {
            itemName.setText(item.getObjectName());
            itemNumber.setText(item.getObjectNumber().toString());
        }
    }
}

“创建”适配器的代码:

private void CreateAdapter() {
    recyclerViewItems.setAdapter(null);
    items.clear();

    //I dont post the code, but this just gets data from a SQLite database and populate "items".
    LoadClientsList();

    final AdapterItemsList adapter = new AdapterItemsList(items);

    recyclerViewItems.setAdapter(adapter);
}

如您所见,我从不使用“implements View.OnLongClickListener”或类似的东西,但我只是在 onBindViewHolder 中设置 OnClickListener。现在里面有一个“ toast ”(当我测试应用程序时它看起来很好),但稍后,应该有我的“删除项目”对话框,它会让用户更改为接受或取消(一个通常的对话框) .如果用户按下“接受”,那么我将从列表中删除该项目。

我承认,我不是 Android 或 Java 忍者,所以我知道可能会出现可怕的错误。但问题是,该应用程序现在正在运行(当我长按一个项目时 Toast 正确显示),但事实上我从不使用“工具”,而我看到每个人都在与 RecyclerViews 和适配器相关的 StackOverflow 问题中这样做,让我觉得我犯了(几个)严重的错误。

我可以承认,因为我不是 Java 基础知识的专家(我已经使用 COBOL 工作了几年,我几乎记得 OOP 是 NULL),所以我看不出我的错误在哪里,但仍然非常感谢任何帮助。

谢谢!


更新: 好的,我正在更改我的代码,现在如下所示:

public class AdapterItemsList extends
RecyclerView.Adapter<AdapterItemsList.ViewHolderItems> implements
View.OnClickListener {
private ArrayList<CItem> items;

/* Added this */
private View.OnClickListener listener;

public AdapterItemsList(ArrayList<CItem> items)
{
    this.items = items;
}

@NonNull
@Override
public ViewHolderItems onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
    View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item_counter, null, false);

  /* Added this */
    view.setOnClickListener(this);

    return new ViewHolderItems(view);
}

@Override
public void onBindViewHolder(@NonNull final ViewHolderItems holder, final int position)
{
    holder.asignarDatos(items.get(holder.getAdapterPosition()));

    holder.counterAdd.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
            auxInteger += 1;
            holder.itemNumber.setText(auxInteger.toString());
            items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
        }
    });

    holder.counterSubtract.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
            if (auxInteger > 0)
            {
                auxInteger -= 1;
                holder.itemNumber.setText(auxInteger.toString());
                items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
            }
        }
    });
}

@Override
public int getItemCount() { [...] }

/* Added this */
public void setOnClickListener(View.OnClickListener listener)
{
    this.listener = listener;
}

/* Added this */
@Override
public void onClick(View view) {
    if (listener != null)
    {
        listener.onClick(view);
    }
}

public class ViewHolderItems extends RecyclerView.ViewHolder { [...] } }

我创建适配器的代码:

private void CreateAdapter() {
    recyclerViewItems.setAdapter(null);
    items.clear();
    LoadClientsList();

    final AdapterItemsList adapter = new AdapterItemsList(items);

    /* Added this */
    adapter.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(getApplicationContext(), String.valueOf(recyclerViewItems.getChildAdapterPosition(view)), Toast.LENGTH_SHORT).show();
        }
    });

    recyclerViewItems.setAdapter(adapter);
}

“recyclerViewItems.getChildAdapterPosition(view)”终于解决了我的问题(之前没有提到,但这是我将“setOnClick ...”放在onBindViewHolder方法中的原因,只是因为我需要的位置按下的项目,我无法弄清楚到底如何访问它)。 所以现在我在创建适配器时设置监听器,而不是在绑定(bind) View 持有者时设置监听器,我想这样更好。 不过,我没有更改“holder.counterAdd.setOnClickListener”和“holder.counterSubtract.setOnClickListener”行,因为我认为它们必须在 onBindViewHolder 方法中,但我想我还是错了。

最佳答案

我不同意对您问题的大部分评论,而且我认为您编写适配器的方式非常好。它并不完美,但是手动将 View.OnClickListener 设置为您的 ViewHolder 中的一些(或许多) View 并没有错。事实上,这是 Google 推荐的做事方式!

不过,我会做出一些改变。第一种方法是将点击监听器的声明移动到 onCreateViewHolder() 方法中(通过将它们放在 ViewHolder 的构造函数中)。第二种方法是添加对 NO_POSITION 的检查,以确保您处理从适配器中删除项目但尚未重新计算布局的情况。

这是我的建议:

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

    itemName = (TextView) itemView.findViewById(R.id.textViewItemCounterName);
    itemNumber = (TextView) itemView.findViewById(R.id.textViewItemCounterNumber);
    counterAdd = (Button) itemView.findViewById(R.id.buttonItemCounterAdd);
    counterSubtract = (Button) itemView.findViewById(R.id.buttonItemCounterSubtract);

    itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View view) {
            deleteItem();
            return true;
        }
    });

    counterAdd.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            itemAdd();
        }
    });

    counterSubtract.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            itemSubtract();
        }
    });
}

private void deleteItem() {
    int position = getAdapterPosition();

    if (position != RecyclerView.NO_POSITION) {
        items.remove(position);
        adapter.notifyItemRemoved(position);
    }
}

private void itemAdd() {
    int position = getAdapterPosition();

    if (position != RecyclerView.NO_POSITION) {
        CItem item = items.get(position);
        item.setObjectNumber(item.getObjectNumber() + 1);
        adapter.notifyItemChanged(position);
    }
}

private void itemSubtract() {
    int position = getAdapterPosition();

    if (position != RecyclerView.NO_POSITION) {
        CItem item = items.get(position);

        if (item.getObjectNumber() > 0) {
            item.setObjectNumber(item.getObjectNumber() - 1);
            adapter.notifyItemChanged(position);
        }
    }
}

然后你的 onBindViewHolder() 非常简单:

@Override
public void onBindViewHolder(@NonNull ViewHolderItems holder, int position)
{
    holder.asignarDatos(items.get(position));
}

那么,让我们谈谈我在这里做什么。

首先,我将所有监听器设置推送到 ViewHolder 的构造函数中。这意味着监听器都被分配了一次。为了在 ViewHolders 被回收并附加到不同项目的世界中仍然发挥作用,我们必须使用 getAdapterPosition() 来确保我们始终与列表中的正确项目进行交互.

我们必须检查 NO_POSITION 因为 RecyclerView 布局操作是异步的:当你从列表中删除一个项目时,有一个间隙,该项目不再存在于你的适配器中但仍然存在显示在屏幕上。在这个间隙中,用户可能会点击一个按钮!在这些情况下,我们只是忽略用户输入。

我还更改了更新项目值的方式以及显示这些更新的方式。我不是获取文本并解析它然后重新设置它,而是总是通过适配器。请记住,您的 RecyclerView 实现的目标是能够正确显示列表中的任何元素。执行此操作的最佳方法是更新数据集中的元素,然后通知 RecyclerView 该元素已更改,并让 onBindViewHolder() 处理所需的更新。

关于android - 这是在 Android 中实现 RecyclerView 的错误方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58919337/

相关文章:

java - 如何从另一个类及其上下文调用函数(android,java)

android - 上下文菜单中的复选框关闭菜单。有什么办法让它一直开着?

android - 从另一个类更新 RecyclerView

android - 如何在android中的RecyclerView中填充数据?

android - 将参数传递给 RequestListener Glide

android - 自定义适配器 : get item number of clicked item in inflated listview

android - RecyclerView 适配器在滚动期间采用错误的值

android - 在开始另一个 Activity 之前恢复动画

Android - 在测量前根据 Activity 计算尺寸

java - 在 Android 中使用带有 post 参数的 HttpClient 和 HttpPost