Reputation: 95
i'm stuck in this problem and i don't know what's wrong with this. I'm trying using RecyclerView and populate it with the data from the Firebase Realtime Database. I've set my adapter but the problem is onBindViewHolder and onCreateViewHolder is not called apparently. Here's the code
WorkoutAdapter.java
public class WorkoutAdapter extends RecyclerView.Adapter<WorkoutAdapter.WorkoutViewHolder> {
private List<Workout> workoutList;
public WorkoutAdapter(List<Workout> workoutList) {
this.workoutList = workoutList;
}
public WorkoutAdapter() {
}
public List<Workout> getWorkoutList() {
return workoutList;
}
public void setWorkoutList(List<Workout> workoutList) {
this.workoutList = workoutList;
}
@NonNull
@Override
public WorkoutViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Log.i("DEBUG MENU", "onCreateViewHolder");
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
ItemWorkoutBinding binding = ItemWorkoutBinding.inflate(layoutInflater, parent, false);
return new WorkoutViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull WorkoutViewHolder holder, int position) {
Log.i("DEBUG MENU", "onBindViewHolder");
Workout workout = workoutList.get(position);
holder.bind(workout);
}
@Override
public int getItemCount() {
return workoutList.size();
}
class WorkoutViewHolder extends RecyclerView.ViewHolder {
private ItemWorkoutBinding binding;
public WorkoutViewHolder(ItemWorkoutBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(Workout workout) {
binding.setWorkout(workout);
binding.executePendingBindings();
}
}
WorkoutFragment.java
public class WorkoutPlanFragment extends Fragment {
private WorkoutPlanViewModel mViewModel;
private WorkoutPlanFragmentBinding binding;
private DatabaseReference mDatabase;
Plan plan;
List<Workout> workoutList;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDatabase = FirebaseDatabase.getInstance().getReference();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
WorkoutPlanViewModelFactory workoutPlanViewModelFactory = new WorkoutPlanViewModelFactory(workoutList);
mViewModel = new ViewModelProvider(this, workoutPlanViewModelFactory).get(WorkoutPlanViewModel.class);
binding = DataBindingUtil.inflate(inflater, R.layout.workout_plan_fragment, container, false);
binding.setViewmodel(mViewModel);
View view = binding.getRoot();
assert getArguments() != null;
plan = WorkoutPlanFragmentArgs.fromBundle(getArguments()).getPlan();
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(plan.getPlan_name());
Glide.with(binding.getRoot().getContext()).load(plan.getUri()).into(binding.imageView2);
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.setPlan(plan);
setupRvWorkout();
}
public void setupRvWorkout() {
RecyclerView recyclerView = binding.workoutRV;
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
mDatabase.child("exercise_workout").child("data").orderByChild("plan_id").equalTo(plan.getPlan_key()).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
workoutList = new ArrayList<>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String workout_id = snapshot.child("workout_id").getValue(String.class);
mDatabase.child("workout").child(workout_id).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
Workout workout = snapshot.getValue(Workout.class);
workout.setWorkout_id(workout_id);
Log.d("GET", "workout_name: " + workout.getWorkout_name() + " " + workout.getWorkout_id());
workoutList.add(workout);
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
}
WorkoutAdapter adapter = new WorkoutAdapter();
recyclerView.setAdapter(adapter);
adapter.setWorkoutList(workoutList);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
}
that's all and thank you in advance!
EDIT: the data from the firebase is added into the workoutList, here's the proof:
item_workout.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/txt_workout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/roboto_medium"
android:text="@{workout.workout_name}"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
<data>
<variable
name="workout"
type="com.example.zahfitclient.model.Workout" />
</data>
</layout>
workout_plan_fragment.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/frameLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.workoutplan.WorkoutPlanFragment">
<ImageView
android:id="@+id/imageView2"
android:layout_width="0dp"
android:layout_height="180dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<TextView
android:id="@+id/txt_plannameWO"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/roboto_slab_bold"
android:text="@{plan.plan_name}"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
<TextView
android:id="@+id/txt_planlevelWO"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:fontFamily="@font/roboto_medium"
android:text="@{plan.level_name}"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintEnd_toStartOf="@+id/view"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txt_plannameWO" />
<TextView
android:id="@+id/txt_plantypeWO"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:fontFamily="@font/roboto_medium"
android:text="@{plan.type_name}"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/view"
app:layout_constraintTop_toBottomOf="@+id/txt_plannameWO" />
<View
android:id="@+id/view"
android:layout_width="2dp"
android:layout_height="20dp"
android:layout_marginStart="204dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="204dp"
android:background="@color/black"
android:backgroundTint="@color/material_on_surface_emphasis_medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txt_plannameWO" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/workoutRV"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view" />
</androidx.constraintlayout.widget.ConstraintLayout>
<data>
<variable
name="plan"
type="com.example.zahfitclient.model.Plan" />
<variable
name="viewmodel"
type="com.example.zahfitclient.ui.workoutplan.WorkoutPlanViewModel" />
</data>
</layout>
Upvotes: 0
Views: 67
Reputation: 1017
@Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { workoutList = new ArrayList < > (); for (DataSnapshot snapshot: dataSnapshot.getChildren()) { String workout_id = snapshot.child("workout_id").getValue(String.class); mDatabase.child("workout").child(workout_id). addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { Workout workout = snapshot.getValue(Workout.class); workout.setWorkout_id(workout_id); Log.d("GET", "workout_name: " + workout.getWorkout_name() + " " + workout.getWorkout_id()); workoutList.add(workout); } @Override public void onCancelled(@NonNull DatabaseError error) {} }); } WorkoutAdapter adapter = new WorkoutAdapter(); recyclerView.setAdapter(adapter); adapter.setWorkoutList(workoutList); adapter.notifyDataSetChanged();
In for each loop
you fill list in asynchronously way and after foreach
you immediately called adapter.setWorkoutList(workoutList)
in the time that you called adapter.setWorkoutList(workoutList)
the list has no data because you fill list in asynchronously way .you must wait until data fetched or using synchronously way to fetch data and after all data fetched compeletelly call
adapter.setWorkoutList(workoutList);
adapter.notifyDataSetChanged();
for achieve this you can do this :
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
workoutList = new ArrayList < > ();
WorkoutAdapter adapter = new WorkoutAdapter();
recyclerView.setAdapter(adapter);
int[] num= {0};
for (DataSnapshot snapshot: dataSnapshot.getChildren()) {
String workout_id = snapshot.child("workout_id").getValue(String.class);
mDatabase.child("workout").child(workout_id).
addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
Workout workout = snapshot.getValue(Workout.class);
workout.setWorkout_id(workout_id);
Log.d("GET", "workout_name: " + workout.getWorkout_name() +
" " + workout.getWorkout_id());
workoutList.add(workout);
num[0]=num[0]++;
if(num[0]==dataSnapshot.getChildren().size(){
adapter.setWorkoutList(workoutList);
adapter.notifyDataSetChanged();
}
}
@Override
public void onCancelled(@NonNull DatabaseError error) {}
});
}
Upvotes: 0
Reputation: 598708
The call to notifyDataSetChanged
will need to be inside the inner onDataSetChanged
as that is where you add items to the list that the view shows with workoutList.add(workout)
:
mDatabase.child("exercise_workout").child("data").orderByChild("plan_id").equalTo(plan.getPlan_key()).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
workoutList = new ArrayList<>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String workout_id = snapshot.child("workout_id").getValue(String.class);
mDatabase.child("workout").child(workout_id).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
Workout workout = snapshot.getValue(Workout.class);
workout.setWorkout_id(workout_id);
Log.d("GET", "workout_name: " + workout.getWorkout_name() + " " + workout.getWorkout_id());
workoutList.add(workout);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
throw error.toException(); // don't ignore errors
}
});
}
WorkoutAdapter adapter = new WorkoutAdapter();
recyclerView.setAdapter(adapter);
adapter.setWorkoutList(workoutList);
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
throw error.toException(); // don't ignore errors
}
});
You can simplify this a bit, by only constructing the adapter once, outside of all data loading:
WorkoutAdapter adapter = new WorkoutAdapter();
recyclerView.setAdapter(adapter);
workoutList = new ArrayList<>();
adapter.setWorkoutList(workoutList);
mDatabase.child("exercise_workout").child("data").orderByChild("plan_id").equalTo(plan.getPlan_key()).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
workoutList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String workout_id = snapshot.child("workout_id").getValue(String.class);
mDatabase.child("workout").child(workout_id).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
Workout workout = snapshot.getValue(Workout.class);
workout.setWorkout_id(workout_id);
Log.d("GET", "workout_name: " + workout.getWorkout_name() + " " + workout.getWorkout_id());
workoutList.add(workout);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
throw error.toException(); // don't ignore errors
}
});
}
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
throw error.toException(); // don't ignore errors
}
});
This makes it clearer that you can just manipulate the workoutList
when you have data updates, as long as you call notifyDataSetChanged()
whenever the UI needs to be updated.
Also see:
Upvotes: 1
Reputation: 96
you need to change this:
WorkoutAdapter adapter = new WorkoutAdapter();
recyclerView.setAdapter(adapter);
adapter.setWorkoutList(workoutList);
adapter.notifyDataSetChanged();
to this:
WorkoutAdapter adapter = new WorkoutAdapter();
adapter.setWorkoutList(workoutList);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
and call this inside onDataChange method
hope it will work.
Upvotes: 1