1

i have implemented recyclerview and it works fine , but when i wanted to pass parameters by Bundle on the clicked row of the recyclerview to another fragment in the same activity there was an error of syntax in this line:

 getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragmentsContainer, f ).commit();

getActivity is showing error in syntax

//the full code of adapter:

package com.example.miniprojetandroid.adapters;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.miniprojetandroid.R;
import com.example.miniprojetandroid.models.Bike;
import com.example.miniprojetandroid.ui.fragments.DetailsFragment;


public class BikesAdapter  extends RecyclerView.Adapter<BikesAdapter.BikesViewHolder> {

    private final ArrayList<Bike> bikes;
    private Context mContext;
    private Callback mCallback;

public BikesAdapter(Context mContext, ArrayList<Bike> bikes) {
        this.mContext = mContext ;
        this.bikes = bikes;
        }

@NonNull
@Override
public BikesAdapter.BikesViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View mItemView = LayoutInflater.from(mContext).inflate(R.layout.bike_list_item, parent, false);
        return new BikesViewHolder(mItemView, this);
        }

@Override
public void onBindViewHolder(@NonNull BikesAdapter.BikesViewHolder holder, int position) {
final Bike singleItem = bikes.get(position);

        holder.BikeName.setText(singleItem.getModel());
        holder.BikeImage.setBackgroundResource(singleItem.getImage());
        holder.bikeView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallback.onItemClicked(singleItem);
            }
        });
        holder.BikeImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
        Toast.makeText(mContext,singleItem.getModel(),Toast.LENGTH_SHORT).show();
        }
        });
        }

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

public class BikesViewHolder extends RecyclerView.ViewHolder {

    public final TextView BikeName;
    public final ImageView BikeImage;
    public final Button bikeView;
    final BikesAdapter mAdapter;

    public BikesViewHolder(@NonNull View itemView, BikesAdapter mAdapter) {
        super(itemView);
        this.BikeName = itemView.findViewById(R.id.bikeName);
        this.BikeImage = itemView.findViewById(R.id.bikeImage);
        this.bikeView = itemView.findViewById(R.id.btn_view);
        this.mAdapter = mAdapter;
    }
}

    public void setCallback(Callback callback) {
        mCallback = callback;
    }

    public interface Callback {
        void onItemClicked(Bike bike);
    }

}

//My target Fragment

package com.example.miniprojetandroid.ui.fragments;

import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import com.example.miniprojetandroid.R;
import com.example.miniprojetandroid.models.Bike;


public class DetailsFragment extends Fragment {

    Button btnRent, btnFav;
    TextView bike_id,model,type,price;

    public DetailsFragment() {
        // Required empty public constructor
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {

        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.fragment_details, container, false);
        btnRent = v.findViewById(R.id.btnRent);
        btnFav = v.findViewById(R.id.btnFav);
        bike_id = v.findViewById(R.id.bike_id);
        model = v.findViewById(R.id.model);
        type = v.findViewById(R.id.type);
        price = v.findViewById(R.id.price);

        int id = getArguments().getInt("id");
        String model = getArguments().getString("model");
        String type = getArguments().getString("type");
        String price = getArguments().getString("price");
        int image = getArguments().getInt("image");
        Bike bike = new Bike(id,model,type,price,image);
        Log.e("ddddddddd",bike.toString());

        return v;
    }


}

what to edit in DetailsFragment to get the data of selected item?

//my recycler view Fragment

    package com.example.miniprojetandroid.ui.fragments;

import android.os.Bundle;
import androidx.fragment.app.Fragment;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.miniprojetandroid.R;

import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.miniprojetandroid.Retrofit.BikeService;
import com.example.miniprojetandroid.Retrofit.RetrofitClient;
import com.example.miniprojetandroid.Retrofit.UserService;
import com.example.miniprojetandroid.adapters.BikesAdapter;
import com.example.miniprojetandroid.models.Bike;
import com.example.miniprojetandroid.models.User;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class FragmentOne extends Fragment implements BikesAdapter.Callback{

    private BikeService apiService;

    private RecyclerView recyclerView;
    private List<Bike> bikes = new ArrayList<Bike>();
    private  BikesAdapter mAdapter;
    List<Bike> result = new ArrayList<Bike>();



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

        View rootView = inflater.inflate(R.layout.fragment_one, container,false);

        recyclerView = rootView.findViewById(R.id.recycler_bikes);

        recyclerView.setHasFixedSize(true);
        recyclerView.setNestedScrollingEnabled(true);

        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(),
                LinearLayoutManager.VERTICAL, false));

        apiService = RetrofitClient.getClient().create(BikeService.class);
        fillData();
        mAdapter = new BikesAdapter(getActivity(), (ArrayList<Bike>) bikes);
        recyclerView.setAdapter(mAdapter);
        mAdapter.setCallback(this);



        return rootView;
    }


    public void fillData(){


        Call<List<Bike>> call = apiService.getBikes();
        call.enqueue(new Callback<List<Bike>>() {
            @Override
            public void onResponse(Call<List<Bike>> call, Response<List<Bike>> response) {
                if(response.isSuccessful()){
                    bikes.addAll(response.body());
                    for(Bike bike: bikes){
                        bike.setImage(R.drawable.ruebike);
                    }
                    Log.e("Bike LIST", bikes.toString());


                    //listView.setAdapter(new UserAdapter(MainActivity.this, R.layout.list_user, list));
                }
            }
            @Override
            public void onFailure(Call<List<Bike>> call, Throwable t) {
                Log.e("ERROR: ", t.getMessage());
            }
        });


        /*bikes.add(new Bike(1,"ECO", "RTT" , "44", R.drawable.ruebike ));
        bikes.add(new Bike(2,"AAA", "RTT" , "33",  R.drawable.ruebike ));
        bikes.add(new Bike(3,"BBB", "RUE" , "11",  R.drawable.ruebike ));
        bikes.add(new Bike(4,"EEE", "SPORT" , "25",  R.drawable.ruebike ));
        bikes.add(new Bike(5,"CCC", "SPORT" , "77", R.drawable.ruebike ));
        Log.e("USERS LIST", bikes.toString());*/
    }



    @Override
    public void onItemClicked(Bike bike) {
        // your codes implementation goes here
        Bundle bundle = new Bundle();
        bundle.putInt("bike_id",bike.getId());
        bundle.putString("model", bike.getModel());
        bundle.putString("type", bike.getType());
        bundle.putString("price", bike.getPrice());
        bundle.putInt("image", bike.getImage());
        DetailsFragment f = new DetailsFragment();
        f.setArguments(bundle);


        getActivity().getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.fragmentsContainer, f )
                .commit();
    }




}

after all the sugests i edited everything like this but now the recyclerview became empty , it is clear with Log.e method that fillData() method displays data late while recyclerview do not capture it early enough

Fares Ben Slama
  • 93
  • 4
  • 15

2 Answers2

0

You cannot use getSupportFragmentManager in your recycler view adapter.

Edit your adapter class and container activity like this

AdapterClass

public class BikesAdapter extends RecyclerView.Adapter<BikesAdapter.BikesViewHolder> {
private final ArrayList<Bike> bikes;
private Context mContext;
private OnItemClickListener listener;

public BikesAdapter(ArrayList<Bike> bikes, Context mContext, OnItemClickListener listener) {
    this.bikes = bikes;
    this.mContext = mContext;
    this.listener = listener;
}

@NonNull
@Override
public BikesViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View mItemView = LayoutInflater.from(mContext).inflate(R.layout.bike_list_item, parent, false);
    return new BikesViewHolder(mItemView);
}


@Override
public void onBindViewHolder(@NonNull BikesViewHolder holder, int position) {
    final Bike singleItem = bikes.get(position);

    holder.BikeName.setText(singleItem.getModel());
    holder.BikeImage.setBackgroundResource(singleItem.getImage());
}

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


public class BikesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    TextView BikeName;
    ImageView BikeImage;
    Button bikeView;
    WeakReference<OnItemClickListener> listenerReference;

    public BikesViewHolder(@NonNull View itemView) {
        super(itemView);
        BikeName = itemView.findViewById(R.id.bikeName);
        BikeImage = itemView.findViewById(R.id.bikeImage);
        bikeView = itemView.findViewById(R.id.btn_view);

        listenerReference = new WeakReference<>(listener);

        bikeView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v == bikeView){
            listenerReference.get().onClick(bikes.get(getAdapterPosition()));
        }
    }
}

public interface OnItemClickListener {
    void onClick(Bike bike);
}

}

Then you should call this adapter in your container activity like below

BikesAdapter adapter = new BikesAdapter(bikeArrayList, this, new BikesAdapter.OnItemClickListener() {
        @Override
        public void onClick(Bike bike) {
            Bundle bundle = new Bundle();
            bundle.putInt("bikeId", bike.getId());
            /*
            .
            .
            .
            Do your stuff here with getSupportFragmentManager()
             */
        }
    });

Then set this adapter object to your recycler view

S.Grain
  • 182
  • 12
0

(option 1) You can either pass your activity in your adapter when instantiating like this:

public BikesAdapter(Activity mActivity, Context mContext, ArrayList<Bike> bikes) {
        this.mActivity = mActivity ;
        this.mContext = mContext ;
        this.bikes = bikes;
        }

you can even do this(you can get your context from your activity):

public BikesAdapter(Activity mActivity, ArrayList<Bike> bikes) {
        this.mActivity = mActivity ;
        this.mContext = mActivity ;
        this.bikes = bikes;
        }

(option 2) or simply write an interface callback for your onClickListeners inside your adapter and call them inside the activity to your implementing the adapter. Works better and perfectly. here is a sample below:

public class BikesAdapter  extends RecyclerView.Adapter<BikesAdapter.BikesViewHolder> {
    private Callback mCallback;
    private final ArrayList<Bike> bikes;
    private Context mContext;
    public BikesAdapter(Context mContext, ArrayList<Bike> bikes) {
        this.mContext = mContext ;
        this.bikes = bikes;
    }

    @Override
    public void onBindViewHolder(@NonNull BikesAdapter.BikesViewHolder holder, int position) {
    final Bike singleItem = bikes.get(position);
    holder.bikeView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {   
         mCallback.onItemClicked(singleItem));
    });
    ...

    public void setCallback(Callback callback) {
        mCallback = callback;
    }

    public interface Callback {
        void onItemClicked(Bike bike);
    }
}

Inside your activity(fragment) where you have your adapter implemented you implement the interface and set the callback, like this and override the method:

public class FragmentOne extends Fragment implements BikesAdapter.Callback {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragment_one, container,false);
        mAdapter = new BikesAdapter(getActivity(), (ArrayList<Bike>) bikes);
        mAdapter.setCallback(this);
    }

    @Override
    public void onItemClicked(Bike bike) {
        // your codes implementation goes here
    getActivity().getSupportFragmentManager()
        .beginTransaction()
        .replace(R.id.fragmentsContainer, f )
        .commit();
    }

}

-------------------------------UPDATE BELOW-------------------------------

Make those changes on your fragment. Updates:

  • take fillData(); method below the adapter definition and
  • add adapter.notifyDataSetChanged(); inside the API response so your adapter will get notified to the data changes in the newly items fetched from the API.

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

        View rootView = inflater.inflate(R.layout.fragment_one, container,false);

        recyclerView = rootView.findViewById(R.id.recycler_bikes);

        recyclerView.setHasFixedSize(true);
        recyclerView.setNestedScrollingEnabled(true);

        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(),
                LinearLayoutManager.VERTICAL, false));

        apiService = RetrofitClient.getClient().create(BikeService.class);
        
        mAdapter = new BikesAdapter(getActivity(), (ArrayList<Bike>) bikes);
        recyclerView.setAdapter(mAdapter);
        mAdapter.setCallback(this);
        fillData();


        return rootView;
    }

(option 1) add adapter.notifyDataSetChanged(); inside the API response.

public void fillData(){


        Call<List<Bike>> call = apiService.getBikes();
        call.enqueue(new Callback<List<Bike>>() {
            @Override
            public void onResponse(Call<List<Bike>> call, Response<List<Bike>> response) {
                if(response.isSuccessful()){
                    bikes.addAll(response.body());
                    for(Bike bike: bikes){
                        bike.setImage(R.drawable.ruebike);
                    }
                    Log.e("Bike LIST", bikes.toString());

                    adapter.notifyDataSetChanged();
                   
                }
            }
            @Override
            public void onFailure(Call<List<Bike>> call, Throwable t) {
                Log.e("ERROR: ", t.getMessage());
            }
        });
    }

(option 2) define a method inside your adapter

package com.example.miniprojetandroid.adapters;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.miniprojetandroid.R;
import com.example.miniprojetandroid.models.Bike;
import com.example.miniprojetandroid.ui.fragments.DetailsFragment;


public class BikesAdapter  extends RecyclerView.Adapter<BikesAdapter.BikesViewHolder> {
    
   .......

   public void addItems(ArrayList<Bike> bikeList) {
        this.bikes = bikeList;
        notifyDataSetChanged();        
    }

     
}

Then simply call this method inside your API response instead of just calling notifyDataSetChanged(); like this

public void fillData(){


        Call<List<Bike>> call = apiService.getBikes();
        call.enqueue(new Callback<List<Bike>>() {
            @Override
            public void onResponse(Call<List<Bike>> call, Response<List<Bike>> response) {
                if(response.isSuccessful()){
                    bikes.addAll(response.body());
                    for(Bike bike: bikes){
                        bike.setImage(R.drawable.ruebike);
                    }
                    Log.e("Bike LIST", bikes.toString());

                    adapter.addItems(bikes);
                   
                }
            }
            @Override
            public void onFailure(Call<List<Bike>> call, Throwable t) {
                Log.e("ERROR: ", t.getMessage());
            }
        });
    }

either ways you will notify your adapter for the new items from the API. and you can have your list being displayed again.

for the question

what to edit in DetailsFragment to get the data of selected item?

there are two ways.

(option 1) simply extracting the arguments in the new fragment inside onCreate of DetailsFragment according to your implementation as follow.

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            Bundle args = getArguments();
            args.getInt("bike_id", null);
            args.getString("model", null);
            args.getString("type", null);
            args.getString("price", null);
            args.getInt("image", null);
        }
    }

(option 2) or simply define a static method for instantiating your fragment by simply passing the data as follow. simply defined here

private static Bike bike;

public static DetailsFragment newInstance(Bike bikeArg) {
    bike = bikeArg;
    return new DetailsFragment();
}

and you can all it like this when adding the fragment instance


@Override
    public void onItemClicked(Bike bike) {
        getActivity().getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.fragmentsContainer, 
                  DetailsFragment.newInstance(Bike bike))
                .commit();
    }

then you can get all your data from bike variable on the new DetailFragment. I hope it works out well.

Kidus Tekeste
  • 651
  • 2
  • 10
  • 28
  • 1
    i followed your second solution but now the recyclerview became empty and it seems that filldata method displays the data late , i will edit the code again to show what i have changed – Fares Ben Slama Dec 02 '20 at 13:55
  • You did really good. But Please take a look at the updates I made to my answer. Then everything is going to work well. @FaresBenSlama – Kidus Tekeste Dec 02 '20 at 21:09