Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I return data from a RecyclerView adapter?

I have a RecyclerView in which in each object of the RecyclerView I have a checkbox. Each time one of the checkboxes is pressed I would like to insert data on the Adapter. One time I finished (press a button on the Fragment that sets the Adapter to the RecyclerView) the data have to be returned.

My code(simplified) of the Adapter of the RecyclerView is:

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());
          }
    }
}

I have created getItem() method to get the data inside MyCustomViewHolder but I do not know how to return the data to the Fragment in which I sets the Adapter to the RecyclerView.

Is there some way to return data from the Adapter of the RecyclerView to the Fragment in which I set it?

Thanks in advance!

like image 295
Francisco Romero Avatar asked Aug 12 '16 12:08

Francisco Romero


People also ask

Can you use a RecyclerView to display data from a database?

With RecyclerView you can display a table of data, display items in a grid or if you want you can also do a Staggered layout as Pinterest does it with every item being a different size. We will show, what you need for a RecyclerView, with a small app that will show a list of cities.

What does RecyclerView adapter do?

Adapter: Provides a binding from your app's data set (which is specific to your app) to item views that are displayed within the RecyclerView . The adapter knows how to associate each item-view position in the RecyclerView to a specific location in the data source.


1 Answers

Updated response:

Alternative using the CarItems list:

@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.
        }
    }
}

You also can add a getCheckedItems() method to the adapter:

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;
}

and use it from the RecyclerView like this:

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

Is there some way to return data from the Adapter of the RecyclerView to the Fragment in which I set it?

In general to manipulate the data from your fragment:

Add the methods below to the adapter and call them from the fragment via the recyclerView casting it to your custom adapter: ((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);
}

I have a RecyclerView in which in each object of the RecyclerView I have a checkbox. Each time one of the checkboxes is pressed I would like to insert data on the Adapter.

Manage multichoice state and saved state

Read this explanation and check this sample app for multichoice.

This library also extends adapter for selection using SparseBooleanArray to save selected items.

Or use this to save the checked state and use it later when button is pressed to only access to the data of the checked items:

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:

enter image description here


I have created getItem() method to get the data inside MyCustomViewHolder but I do not know how to return the data to the Fragment in which I sets the Adapter to the RecyclerView.

Set/get data to/from the view holder

Also these methods can be useful to get the data, perhaps using the setTag()/getTag() mechanism.

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;
}

One time I finished (press a button on the Fragment that sets the Adapter to the RecyclerView) the data have to be returned.

I would need extra info about what are you trying to do when the data is returned and the reason you set the adapter to the recyclerview another time here.

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
        }
    }
}
like image 68
albodelu Avatar answered Oct 19 '22 22:10

albodelu