Home > Net >  Fragment in a Adapter of RecyclerView JAVA
Fragment in a Adapter of RecyclerView JAVA

Time:09-04

I have a fragment Users which has 3 other fragments in it (tabs). For one tab ( called Friends2Fragment ) I made a recycler View and made an adapter for it. In each item of RecyclerView I have a button "Add friend" and I want to call it from Friends2Fragment, not to call it from the adapter because I can't use Firestore Database properly.

RecyclerViewInterface:

public interface RecyclerViewInterface {
    void onItemClick(int position, String button_pressed);
}

Friends2Fragment.java :

public void onStart(){
        super.onStart();

        recyclerView = (RecyclerView) v.findViewById(R.id.recycler);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        friendslist = new ArrayList<>();
        myAdapter = new MyAdapter(friendslist,v.getContext());
        recyclerView.setAdapter(myAdapter);
          ------ Firestore operations ------
}
    @Override
    public void onItemClick(int position, String button_pressed) {
        switch ( button_pressed ){
            case "ADD_FRIEND":
             Log.d(TAG, "item clicked: "   friendslist.get(position).username);

        }
    }

MyAdapter.java :

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {

    Context context;
    public ArrayList<User> userArrayList;

    public MyAdapter(ArrayList<User> userArrayList, Context context)  {
        this.userArrayList = userArrayList;
        this.context = context;
    }

    public Context getContext() {
        return context;
    }

    public ArrayList<User> getUserArrayList() {
        return userArrayList;
    }

    @NonNull
    @Override
    public MyAdapter.myViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        MyAdapter.myViewHolder myViewHolder = new MyAdapter.myViewHolder(v);
        myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                ((Friends2Fragment)context).onItemClick(myViewHolder.getAdapterPosition(),"ADD_FRIEND");
            }
        });
        return myViewHolder;
}
@Override
    public void onBindViewHolder(@NonNull MyAdapter.myViewHolder holder, int position) {

        User user = userArrayList.get(position);

        holder.usernamerecycle.setText(user.username);
    }

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

    public void filterList(List<User> filteredList){
        userArrayList = (ArrayList<User>) filteredList;
        notifyDataSetChanged();

    }

    public class myViewHolder extends RecyclerView.ViewHolder{

        TextView usernamerecycle;
        Button addbutton;
        View rootview;

        public myViewHolder(@NonNull View itemView) {
            super(itemView);
            rootview = itemView;
            usernamerecycle = itemView.findViewById(R.id.usernamerecycler);
            addbutton = itemView.findViewById(R.id.addfriendbutton);
    }
  }
}

The problem is at this line : ((Friends2Fragment)context).onItemClick(myViewHolder.getAdapterPosition(),"ADD_FRIEND"); in onCreateViewHolder method in MyAdapter.

I have this error : Inconvertible types; cannot cast 'android.content.Context' to 'com.example.birthday.Fragments.Friends2Fragment'

Please help me ..

CodePudding user response:

A Fragment isn't a Context (that's not one of its supertypes) so that cast is impossible, that's why you're getting the error.

I think you should organise it like this: your Adapter holds a bunch of User objects, right? It displays those, and you have a click listener on each ViewHolder that knows which index in the User list it's currently displaying, and it wants to inform some listener when it's clicked. That index is an internal detail really, it would make more sense to look up the actual User, and provide that to the listener.


The simplest way is to just provide your fragment as a listener. First store it in your adapter:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {

    // store a reference to your fragment
    private Friends2Fragment listener;

    // add a function to provide that fragment
    public void setListener(Friends2Fragment: listener) {
        this.listener = listener
    }

    ...

    public MyAdapter.myViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ...
        myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener != null) {
                   // look up the actual user
                   User user = userArrayList.get(myViewHolder.getAdapterPosition());
                   // call a function on your fragment
                   listener.onItemClick(user, "ADD_FRIEND");
                }
            }
        });
    }

Then add the callback function your adapter uses, and also set your fragment on the adapter as a listener:

// Friends2Fragment

// You should REALLY be doing this in onViewCreated or something, so this setup happens once.
// You're losing all your state by creating a new adapter whenever the user returns to the app
public void onStart(){
    ...
    myAdapter = new MyAdapter(friendslist,v.getContext());
    // set the fragment as the listener
    myAdapter.setListener(this);
    recyclerView.setAdapter(myAdapter);
}

// now add the function the adapter calls
private void onItemClick(User user, String someString) {
    // handle the clicked user
}


A better way is to create an interface with all the events that need to be handled, and make your Fragment implement those. It breaks the hard association with the Fragment since you could pass any object that implements those functions, and it's also clearer because the interface kinda documents all the data the adapter produces, and that a listener needs to be able to handle. Something like this:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {
    // the listener is now something that implements the Callbacks interface
    private Callbacks listener;
    ...
    // nesting it inside MyAdapter makes the path MyAdapter.Callbacks, which makes it clear
    // exactly what it is and what it relates to, and kinda gives the Adapter "ownership"
    interface Callbacks {
        void addFriend(User user)
    }

And then you just make the Fragment implement that interface

public class Friends2Fragment() extends Fragment implements MyAdapter.Callbacks {
    ...
    // implement all the callbacks you need to handle
    override public void addFriend(User user) {
        // do the thing
    }

    // set it in the same way, since this Fragment implements MyAdapter.Callbacks
    myAdapter.setListener(this);
    

Which is a bit neater and cleaner, I think - but slightly more work. Also if you notice, I renamed the callback function from the generic handleItemClick to the more specific addFriend - so instead of having to pass a String saying what kind of click it is, you just have a function for each event you want to handle, and you can name them appropriately

  • Related