4

I have a fragment that displays a RecyclerView. I am currently handling the onclick event in my holder class like this:

public class CategoryHolder extends RecyclerView.ViewHolder implements View.OnClickListener  {

    public ImageView categoryImageView;
    public TextView categoryNameTextView;
    public TextView categoryAmountTextView;
    ArrayList<Category> categoriesArrayList = new ArrayList<>();
    Context context;


    public CategoryHolder(View itemView, Context context, ArrayList<Category> categories) {
        super(itemView);
        this.categoriesArrayList = categories;
        this.context = context;

        itemView.setOnClickListener(this);
        this.categoryImageView = (ImageView) itemView.findViewById(R.id.categoryImageView);
        this.categoryNameTextView = (TextView) itemView.findViewById(R.id.categoryNameTextView);
        this.categoryAmountTextView = (TextView) itemView.findViewById(R.id.categoryAmountTextView);
    }


    @Override
    public void onClick(View v) {

        int position = getAdapterPosition();
        Category category = this.categoriesArrayList.get(position);
        Toast.makeText(context, ""+category.getCategoryName() + position, 


    }
}

As you can see I have implemented OnClickListener in the holder class and then setTheOnItemClickListner in the Holder.

In my Adapter I pass the holder class the the arraylist with the data like this:

 Context context;
    ArrayList<Category> categories;


    public CategoriesAdapter(Context context, ArrayList<Category> categories) {
        this.context = context;
        this.categories = categories;
    }

    @Override
    public CategoryHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.categorylist_singlecell, parent, false);
        CategoryHolder holder = new CategoryHolder(v, context, categories);
        return holder;
    }

I am passing the data when creating the new holder.

This works fine and I can toast the position and any of the data from the ArrayList that is used for the RecyclerView.

What I want to do though is use that in the main fragment where I initialise the RecyclerView and adapter.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_nested_youtube_video_selection, container, false);

    //////////////////////////// CATEGORIES RECYCLER /////////////////////////////////////

    // Initialise the categories Recylerview
    LinearLayoutManager categoriesLayoutManger = new LinearLayoutManager(getActivity());
    categoriesLayoutManger.setOrientation(LinearLayoutManager.HORIZONTAL);
    categoriesRecyclerView = (RecyclerView) view.findViewById(R.id.youtubeAlarmCategoryRecyclerView);
    categoriesRecyclerView.setLayoutManager(categoriesLayoutManger);

    // Get the data for the categories RecyclerView
    categoryArraylist = CategoryCollection.getCategoryArrayList();

    // Initialse the categories RecyclerView Adapter
    categoriesAdapter = new CategoriesAdapter(getActivity(), categoryArraylist);

    // Bind the categories Adapter to the categories RecyclerView
    categoriesRecyclerView.setAdapter(categoriesAdapter);

I am thinking I need an interface but I am not sure how to do that or if that is even the best way to do it.

musica
  • 1,373
  • 3
  • 15
  • 34
Nicholas Muir
  • 2,897
  • 8
  • 39
  • 89

2 Answers2

8

Following should work:

  • create an interface
  • create an instance of this interface in your fragment and pass it on to the adapter
  • let the adapter pass this interface on to the view holder
  • let the view holder define an onClickListener that passes the event on to the interface

Here's an example:

Interface

public interface ItemClickListener {

    void onItemClicked(ViewHolder vh, Object item, int pos);
}

public interface GenericItemClickListener<T, VH extends ViewHolder> {

    void onItemClicked(VH vh, T item, int pos);
}

ViewHolder

public class CategoryHolder extends RecyclerView.ViewHolder {

    public CategoryHolder(View itemView, Context context) {
        super(itemView);
        this.context = context;

        this.categoryImageView = (ImageView) itemView.findViewById(R.id.categoryImageView);
        this.categoryNameTextView = (TextView) itemView.findViewById(R.id.categoryNameTextView);
        this.categoryAmountTextView = (TextView) itemView.findViewById(R.id.categoryAmountTextView);
    }
}

Adapter

Context context;
ArrayList<Category> categories;
ItemClickListener itemClickListener;

public CategoriesAdapter(Context context, ArrayList<Category> categories, ItemClickListener itemClickListener) {
    this.context = context;
    this.categories = categories;
    this.itemClickListener = itemClickListener;
}

// DON'T PASS YOUR DATA HERE, just create a ViewHolder
@Override
public CategoryHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.categorylist_singlecell, parent, false);
    CategoryHolder holder = new CategoryHolder(v, context);
    return holder;
}

//HERE you bind one item of your list to the view holder
@Override
public void onBindViewHolder(final CategoryHolder vh, final int i) {
    final Category category = categories.get(i);

    // set click listener
    vh.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            itemClickListener.onItemClicked(vh, category, i);
        }
    });

   // update images and texts...
}

Edit

Updated the code to show you, that you should not pass a array of items to every view holder... The view holder gets created and reused, only update the view holder in onBindViewHolder and bind the view holder instance to the correct data there...

Btw, I would make a generic interface instead, that's even more beautiful... My example is just a simple solution...

Edt 2 - How to create the interface instance

ItemClickListener listener = new ItemClickListener(){
    @Override
    public void onItemClicked(RecyclerView.ViewHolder vh, Object item, int pos){
        Toast.makeText(getActivity(), "Item clicked: " + pos, Toast.LENGTH_SHORT).show();
    }
};
prom85
  • 16,896
  • 17
  • 122
  • 242
  • thanks ver much for your help. It seems to all work fine untile I put in this line itemClickListener.onItemClicked(holder, category, position); It says that category and holder need to be declared final. Would that cause an issue if the value is changing? – Nicholas Muir Oct 05 '16 at 09:09
  • because `category` and `i` are not final I suppose. Just make them final and they can be used in the inner class as well – prom85 Oct 05 '16 at 09:10
  • I'm not sure how to create a an instance of the interface and pass it to the adapter. Ie how I receive the onclick in my fragment. – Nicholas Muir Oct 05 '16 at 09:15
  • You can remove the click listener in `onViewRecycled`... But the `RecyclerView` will just reuse the `ViewHolder` and rebind them to a new position, so the click listeners will be updated as well – prom85 Oct 05 '16 at 09:17
  • That all works great! Thanks. One last thing in the interface the item comes through as an Object do I just have to cast back to a my Category type? – Nicholas Muir Oct 05 '16 at 09:30
  • If you use this interface in more places, make it generic, if you only use it with category, change the interface to category instead of object. I'll add an generic variant to my answer... – prom85 Oct 05 '16 at 09:41
  • Don't bind clickListener on onBindView – user2783798 Sep 27 '18 at 18:46
0

abstract YOURAdapter class then add method like

void onLikeClicked(Object object, int position); void onItemClicked();

call it in bindView as simply like

holder.btnLike.setOnclickListner(new OnClickListner){
onLikeClicked(list.get(position), position);
}

In Activity Class now it will automatically implements all methods of adapter

YOURAdapter adapter=new YOURAdapter(){
    void onLikeClicked(Object object, int position){

    }
    void onItemClicked(){

    }
}
Rajesh
  • 2,618
  • 19
  • 25