1

I'm trying to figure out how to get data from a clicked item in a RecyclerView to a listview in a Fragment. I can't seem to figure out how to do this, as I can't get my bundle to work.

I've tried various solutions offered here on Stackoverflow, but none of them have worked. I have managed to get the info from the recyclerview, but I am stuck trying to figure out how I can pass it to the fragment. Can someone please help me?

The Adapter class:

public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.ViewHolder> {

    private Context context;

    ArrayList<FoodActivity> list;
    public FoodAdapter(ArrayList<FoodActivity> list){
        this.list = list;
    }


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

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
        holder.foods.setText(list.get(position).getName());
        holder.carbo.setText(list.get(position).getCarbohydrates());
        holder.protein.setText(list.get(position).getProtein());
        holder.fats.setText(list.get(position).getFats());

        //Get items from recyclerview when user clicks them. Then send them to FoodFragment
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String foods = list.get(position).getName();
                String carbo = list.get(position).getCarbohydrates();
                String protein = list.get(position).getProtein();
                String fats = list.get(position).getFats();

                Toast.makeText(v.getContext(), "test: " + foods, Toast.LENGTH_SHORT).show();


            }
        });
    }


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

    class ViewHolder extends RecyclerView.ViewHolder {
        TextView foods, carbo, fats, protein;

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

            carbo = itemView.findViewById(R.id.carbo);
            protein = itemView.findViewById(R.id.protein);
            fats = itemView.findViewById(R.id.fats);
            foods = itemView.findViewById(R.id.food);
        }
    }
}

The Fragment where the data comes from:

public class TrackingFragment extends Fragment {

    DatabaseReference databaseReference;
    ArrayList<FoodActivity> list;
    RecyclerView recyclerView;
    SearchView searchView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_tracking, container, false);

        databaseReference = FirebaseDatabase.getInstance().getReference().child("foods");
        recyclerView = view.findViewById(R.id.rv);
        searchView = view.findViewById(R.id.searchFood);

        return view;
    }

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

        if(databaseReference != null){
            databaseReference.addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                    if(dataSnapshot.exists()){
                        list = new ArrayList<>();
                        for(DataSnapshot ds : dataSnapshot.getChildren()){
                            list.add(ds.getValue(FoodActivity.class));
                        }
                        FoodAdapter adapter = new FoodAdapter(list);
                        recyclerView.setAdapter(adapter);


                    }
                }

                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) {
                    Toast.makeText(getActivity(), databaseError.getMessage(), Toast.LENGTH_SHORT).show();
                }
            });
        }
        if(searchView != null){
            searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) {
                    return false;
                }

                @Override
                public boolean onQueryTextChange(String newText) {
                    search(newText);
                    return true;
                }
            });
        }
    }

    private void search(String str){
        ArrayList<FoodActivity> searchList = new ArrayList<>();
        for(FoodActivity object : list){
            if(object.getName().toLowerCase().contains(str.toLowerCase())){
                searchList.add(object);
            }
        }
        FoodAdapter foodAdapter = new FoodAdapter(searchList);
        recyclerView.setAdapter(foodAdapter);
    }

}

And the class where it needs to go:

public class FoodFragment extends Fragment {

    ImageButton addFood;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_food, container, false);

        addFood = view.findViewById(R.id.addFood);

        addFood.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                FragmentTransaction fr = getFragmentManager().beginTransaction();
                fr.replace(R.id.fragment_container, new TrackingFragment());
                fr.addToBackStack(null).commit();
            }
        });

        return view;
    }

}
Flex
  • 33
  • 1
  • 8
  • Base on this code it's hard to say where are you using `FoodFragment`. Are both fragments are in the same Activity? Have you got `TrackingFragment` with `FoodAdapter`, right? – Boken Mar 28 '19 at 19:28
  • I'm not entirely sure what you mean. FoodFragment is called from a MenuActivity with a bottomNavigationView, and then TrackingFragment is called from the FoodFragment. FoodAdapter is connected to TrackingFrackment yes. – Flex Mar 28 '19 at 19:40
  • But how exactly is you layout designed? You have a MenuActivity, which displays the FoodFragment, and when you select something, you replace you food Fragment with the TrackingFragment, or do you open a new Activity, which displays the Tracking fragment? – glm9637 Mar 28 '19 at 20:14
  • In the MenuActivity I have a fragment_container in a FrameLayout. I simply replace the fragments in that container whenever I have to display a new fragment. – Flex Mar 28 '19 at 20:29

4 Answers4

1

The most simple is to add variables you need to your activity, set their values with onClick() and then retrieve data in other fragment:

  • in activity:

    String foods;
    
    public String getFoods() {
        return foods;
    }
    
    public void setFoods(String foods) {
        this.foods = foods;
    }
    
  • in adapter:

       holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               ((YourActivity)getActivity()).setFoods("Any value");
            }
        });
    
  • in destination fragment:

    String foods = ((YourActivity)getActivity()).getFoods();

1

Add an interface to your adapter as follow

public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.ViewHolder> {

interface OnClickListener {
    void onClick(FoodActivity clickedItem);
}

private OnClickListener mCallback; 
private ArrayList<FoodActivity> list;

public FoodAdapter(ArrayList<FoodActivity> list){
    this.list = list;
}

public void setOnClickListener(OnClickListener callback) {
    mCallback = callback;
}


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

    return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
    holder.foods.setText(list.get(position).getName());
    holder.carbo.setText(list.get(position).getCarbohydrates());
    holder.protein.setText(list.get(position).getProtein());
    holder.fats.setText(list.get(position).getFats());

    //Get items from recyclerview when user clicks them. Then send them to FoodFragment
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mCallback != null)
                mCallback.onClick(list.get(position));
        }
    });
}


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

class ViewHolder extends RecyclerView.ViewHolder {
    TextView foods, carbo, fats, protein;

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

        carbo = itemView.findViewById(R.id.carbo);
        protein = itemView.findViewById(R.id.protein);
        fats = itemView.findViewById(R.id.fats);
        foods = itemView.findViewById(R.id.food);
    }
}
}
Juan Hurtado
  • 358
  • 1
  • 7
  • Thank you for taking your time to answer. I've tried getting the data in the FoodFragment, but can't seem to get it to work (I haven't worked with interfaces before and the things I've tried hasn't worked). Would you mind showing me how to retrieve that data also? – Flex Mar 29 '19 at 09:50
0

Android sometimes is a little complicated, when it involves the full stack:

01 RecyclerView Adapter

/** 01. Some Adapter */
public class SomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
    private WeakReference<Context> mContext;

    /** Constructor */
    public SomeAdapter(@NonNull Context context) {
        this.mContext = new WeakReference<>(context);
    }

    @NonNull
    protected Context getContext() {
        return this.mContext.get();
    }

    /** Call to Activity from within the adapter. */
    private void someMethod() {
        SomeActivity activity = (SomeActivity) getContext();
        synchronized(activity) {activity.showLoginDialog();}
    }
}

02 AppCompatActivity

/** 02. Some Activity */
public class SomeActivity extends AppCompatActivity {

    @Nullable
    private BaseFragment currentFragment = null;

    /** Constructor */
    public SomeActivity() {}

    public void setCurrentFragment(Fragment fragment) {
        this.currentFragment = fragment;
    }

    /** Call to the current Fragment. */
    public void someMethod() {
        if (currentFragment != null) {
            currentFragment.someMethod();
        }
    }
}

03 Fragment

/** Some Fragment */
public class SomeFragment extends Fragment {

    /** Constructor */
    public SomeFragment() {}

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        ((BaseActivity) context).setCurrentFragment(this);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        ((BaseActivity) getActivity()).setCurrentFragment(this);
    }
}
Martin Zeitler
  • 1
  • 19
  • 155
  • 216
-1

To get your data from FoodAdapter to TrackingFragment, you can add TrackingFragment (or an interface it implements, ideally) as a parameter to the FoodAdapter constructor, then use that instance to forward your ViewHolder clicks to TrackingFragment for handling.

To get your data from TrackingFragment to FoodFragment, use the setTargetFragment/onActivityResult pattern for fragment <-> fragment communication. This answer has an example: https://stackoverflow.com/a/13733914/3238938

Damien Diehl
  • 383
  • 4
  • 13