【问题标题】:How can I return data from a RecyclerView adapter?如何从 RecyclerView 适配器返回数据?
【发布时间】:2016-08-12 12:40:27
【问题描述】:

我有一个RecyclerView,其中RecyclerView 的每个对象中都有一个复选框。每次按下其中一个复选框时,我都想在适配器上插入数据。有一次我完成(按下片段上将Adapter 设置为RecyclerView 的按钮)必须返回数据。

RecyclerViewAdapter 的代码(简化)是:

public List<CarItem> data; 
public class MyCustomAdapter extends RecyclerView.Adapter<MyCustomAdapter.MyCustomViewHolder>  {

    public MyCustomAdapter(List<CarItem> data) {
        this.data=data;
    }

    public MyCustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout_car_item, parent, false);
        return new MyCustomViewHolder(view);
    }

    public void onBindViewHolder(final MyCustomViewHolder holder, final int position) {
        holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                  @Override
                  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                       //Here I add the data that I want to store
                       holder.getItem();
                  }
            });
    }

    public class MyCustomViewHolder extends RecyclerView.ViewHolder{
          public MyCustomViewHolder (View itemView) {
              super(itemView);
              //Here I make reference to all the elements of the layout of the Adapter
          }

          public void getItem(){
              Log.d("prove", "" + data.size());
          }
    }
}

我创建了getItem() 方法来获取MyCustomViewHolder 中的数据,但我不知道如何将数据返回到Fragment,其中我将Adapter 设置为RecyclerView

有没有办法将数据从RecyclerViewAdapter 返回到我设置它的Fragment

提前致谢!

【问题讨论】:

  • 如果你简化了 getItem() 方法,也许我能更好地理解你的问题。您是从这里的数据库中检索数据吗?
  • @Ardock 是的,我正在从数据库中检索数据。
  • 我现在明白了,我认为从数据库中加载数据并且不求助于任何额外的数据结构来保存状态而不是查看器,这不是最好的方法,但我会尝试改进我的回复符合您的要求。我会创建一个额外的列表来保存检索到的数据或使用 caritems 列表。您是否出于任何原因放弃此选项?
  • @Ardock 因为项目的结构是由另一个人完成的,我试图在检查列表中的每个项目时获取列表中显示的数据(按复选框)。我必须尽可能少地修改代码。
  • 您是否正在从数据库中检索 carItems?如果你简化 getItem() 方法,我会改进我的响应。

标签: android android-recyclerview


【解决方案1】:

更新回复:

使用 CarItems 列表的替代方法:

@OnClick(...)
public void onButtonClicked() {
    int size = ((MyCustomAdapter) mRecyclerView.getAdapter()).getItemCount();
    for (int i = 0; i < size; i++) {
        if (((MyCustomAdapter) mRecyclerView.getAdapter()).isItemChecked(i)) {
            // Get each selected item
            CarItem carItem = (MyCustomAdapter) mRecyclerView.getAdapter()).getItem(i);
            // Do something with the item like save it to a selected items array.
        }
    }
}

您还可以向适配器添加getCheckedItems() 方法:

public List<CarItem> getCheckedItems() {
    List<CarItem> checkedItems = new ArrayList<>();
    for (int i = 0; i < getItemCount(); i++) {
        if (isItemChecked(i)) {
            checkedItems.add(getItem(i));
        }
    }
    return checkedItems;
}

并像这样从RecyclerView 使用它:

((MyCustomAdapter) mRecyclerView.getAdapter()).getCheckedItems();

有没有办法从RecyclerView的Adapter返回数据 到我设置它的片段?

一般来说,操作片段中的数据:

将以下方法添加到适配器并通过 recyclerView 从片段中调用它们,并将其转换为您的自定义适配器:((MyCustomAdapter) mRecyclerView.getAdapter()).getItem(...);

public void setListItems(List<CarItem> data) {
    this.data = data;
}

public List getListItems() {
    return data;
}

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

public CarItem getItem(int position) {
    return data.get(position);
}

public void setItem(CarItem item, int position) {
    data.set(position, item);
}

我有一个 RecyclerView 在 RecyclerView 的每个对象中我 有一个复选框。每次按下其中一个复选框时,我都会 喜欢在Adapter上插入数据。

管理多选状态和保存状态

阅读this explanation并检查this sample app for multichoice

This library 还使用 SparseBooleanArray 扩展了用于选择的适配器以保存所选项目。

或者使用这个来保存选中状态,稍后在按下按钮时使用它来只访问选中项的数据:

public class MyCustomAdapter extends RecyclerView.Adapter<MyCustomAdapter.MyCustomViewHolder>  {
    private ArrayList<CarItem> data;
    private SparseBooleanArray checkedState = new SparseBooleanArray();

    public void setCheckedState(int position, boolean checked) {
        checkedState.append(position, checked);
    }

    public boolean isItemChecked(int position) {
        return checkedState.get(position);
    }

    public SparseBooleanArray getCheckedState() {
        return checkedState;
    }

    public void onBindViewHolder(final MyCustomViewHolder holder, final int position) {
        holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                  @Override
                  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                       setCheckedState(holder.getAdapterPosition(), isChecked);
                       //Here I add the data that I want to store
                       if (isChecked) {
                           data.set(holder.getAdapterPosition(), holder.getItem());
                       } else {
                           data.set(holder.getAdapterPosition(), null);
                       }

              }
        });
}

Reason to use adapter position on this related Google I/O 2016 video:


我创建了 getItem() 方法来获取里面的数据 MyCustomViewHolder 但我不知道如何将数据返回到 我将 Adapter 设置为 RecyclerView 的片段。

向/从视图持有者设置/获取数据

这些方法也可用于获取数据,可能使用 setTag()/getTag() 机制。

viewHolder.itemView.setTag(yourNewData);

/**
 * Returns this view's tag.
 *
 * @return the Object stored in this view as a tag, or {@code null} if not
 *         set
 *
 * @see #setTag(Object)
 * @see #getTag(int)
 */
@ViewDebug.ExportedProperty
public Object getTag() {
    return mTag;
}

/**
 * Sets the tag associated with this view. A tag can be used to mark
 * a view in its hierarchy and does not have to be unique within the
 * hierarchy. Tags can also be used to store data within a view without
 * resorting to another data structure.
 *
 * @param tag an Object to tag the view with
 *
 * @see #getTag()
 * @see #setTag(int, Object)
 */
public void setTag(final Object tag) {
    mTag = tag;
}

有一次我完成(按 将适配器设置为 RecyclerView 的 Fragment 上的按钮) 必须返回数据。

我需要额外的信息,了解您在返回数据时尝试执行的操作以及您在此处再次将适配器设置为 recyclerview 的原因。

findViewHolderForAdapterPosition(int)

/**
 * Return the ViewHolder for the item in the given position of the data set. Unlike
 * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
 * adapter changes that may not be reflected to the layout yet. On the other hand, if
 * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
 * calculated yet, this method will return <code>null</code> since the new positions of views
 * are unknown until the layout is calculated.
 * <p>
 * This method checks only the children of RecyclerView. If the item at the given
 * <code>position</code> is not laid out, it <em>will not</em> create a new one.
 *
 * @param position The position of the item in the data set of the adapter
 * @return The ViewHolder at <code>position</code> or null if there is no such item
 */
public ViewHolder findViewHolderForAdapterPosition(int position) {
    if (mDataSetHasChangedAfterLayout) {
        return null;
    }
    final int childCount = mChildHelper.getUnfilteredChildCount();
    for (int i = 0; i < childCount; i++) {
        final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
        if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) {
            return holder;
        }
    }
    return null;
}

@OnClick(...)
public void onButtonClicked() {
    int size = ((MyCustomAdapter) mRecyclerView.getAdapter()).getItemCount();
    for (int i = 0; i < size; i++) {
        ViewHolder vh = (MyCustomViewHolder) findViewHolderForAdapterPosition(i);
        if (vh != null && ((MyCustomAdapter) mRecyclerView.getAdapter()).isItemChecked(i)) {
            Object obj = vh.itemView.getTag();
            // Manipulate the data contained in this view holder
            // but perhaps would be better to save the data in the CarItem
            // and don't manipulate the VH from outside the adapter
        }
    }
}

【讨论】:

  • 非常感谢您的扩展回答!。我很困惑,因为我认为onBindViewHolder 方法是独立的,但最后我把getItem() 方法放在onBindViewHolder 方法之外,然后把它放在MyCustomAdapter 中。当然,我必须创建一个在onBindViewHolder 中使用的setter 方法来放置元素,然后再从我的Fragment 中检索它。你的第二部分让我理清思路。
【解决方案2】:

最简单的方法是使用EventBus https://github.com/greenrobot/EventBus

只需在您的片段中注册它并设置@Subscribe 方法。 使用 EventBus.getDefault().post(new YourClass(yourData)); 构建简单的类并将数据从适配器传递到片段;

另一种方法是使用 intarfaces。

小心!

当您在适配器中使用复选框时,您必须记住复选框中的旧值,并设置滚动列表的时间。那么你可能会感兴趣 SparseBooleanArray

【讨论】:

  • 我没有用过 EventBus,但我用过 Otto 并且事情是异步发生的。而且我会变得非常混乱。因此,在使用公共汽车时要小心。我更喜欢使用接口。
【解决方案3】:

不要使用RecyclerView。它不是为此类目的而设计的。 RecyclerViewListView 在您希望在它们的行被回收时显示只读数据时很有用。使用这些视图从用户那里获取输入是很危险的,因为在滚动后回收行时,可能会丢失已经输入的数据。请改用LinearLayout。这样,您将可以直接访问其中的视图。如果要显示的raws数量不固定,可以动态添加Views到容器中。

【讨论】:

  • 我正在使用RecyclerView,因为我不知道从数据库中检索它们时会有多少项目。我不知道是否可以使用LinearLayout
  • @Kiril,有什么相关的吗?我可以想象 RecyclerView/Listview 对于某些用途会非常有用。
  • 有可能。您只需要膨胀视图(行)并将其放入 LinearLayout 作为孩子 View
  • Error404 我认为 RecyclerView 是多用途的,这个答案不正确。我从来没有将它用于您检查的要求,我只将它用于只读情况(所以我不会对此投反对票)但是如果您在不知道物品数量的情况下采用这种方法,您将失去回收收益,我认为所有可能的问题都可以管理。否则 Recyclerview 将不存在。
  • @adrock,我已经尝试过了,它可以工作。为了保持滚动功能,应该将 ListView 放在 ScrollView 中(这对其他 ViewGroup 容器也有效)
猜你喜欢
  • 1970-01-01
  • 2017-08-18
  • 1970-01-01
  • 2018-02-19
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
  • 2013-05-28
相关资源
最近更新 更多