rizkyputrapb
rizkyputrapb

Reputation: 95

RecyclerView not displayed even after setting the adapter

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:

data added to the list

exercise_plan collection

exercise_workout collection

workout collection

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

Answers (3)

mostafa3dmax
mostafa3dmax

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

Frank van Puffelen
Frank van Puffelen

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

Ammar
Ammar

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

Related Questions