Reputation: 1
I am new to android studio and I am dealing with this problem for few hours and I cannot figure it out. Hear me out. I am making and android application and inside of a main activity I have multiple fragments navigable with a bottom navigation bar. In one of the fragments, I have a recycler view which contains one or multiple items. `
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
The item:
<TextView
android:id="@+id/activeWorkoutNameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Workout Name"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
When an item is clicked, it should open a new fragment.
I made it work but the problem is that i am not using Mvvm arhitecture with oberserver pattern. Because of this, if I rotate the phone or navigate back and then click on the same item in the list, the state of the fragment is lost and any data that I write in there is lost. How I made it work:
public class ActiveWorkoutsDetailFragment extends Fragment {
private static final String ARG_WORKOUT = "workout";
private WorkoutFromAPI workout;
private LinearLayout fieldsContainer;
private int setCount = 0;
public static ActiveWorkoutsDetailFragment newInstance(WorkoutFromAPI workout) {
ActiveWorkoutsDetailFragment fragment = new ActiveWorkoutsDetailFragment();
Bundle args = new Bundle();
args.putSerializable(ARG_WORKOUT, workout);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
workout = (WorkoutFromAPI) getArguments().getSerializable(ARG_WORKOUT);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_active_workouts_detail, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView nameTextView = view.findViewById(R.id.NameTextView);
TextView equipmentTextView = view.findViewById(R.id.EquipmentTextView);
TextView instructionsTextView = view.findViewById(R.id.InstructionsTextView);
Button addButton = view.findViewById(R.id.addButton);
fieldsContainer = view.findViewById(R.id.fieldsContainer);
if (workout != null) {
nameTextView.setText(workout.getName());
equipmentTextView.setText(workout.getEquipment());
instructionsTextView.setText(workout.getInstructions());
} else {
// Log or toast an error message if workout is null
Toast.makeText(getContext(), "Workout data is missing", Toast.LENGTH_SHORT).show();
}
addButton.setOnClickListener(v -> addSetFields());
}
private void addSetFields() {
LayoutInflater inflater = LayoutInflater.from(getContext());
View setFields = inflater.inflate(R.layout.item_set_fields_for_active_workouts, fieldsContainer, false);
TextView setLabel = setFields.findViewById(R.id.setLabel);
setCount++;
setLabel.setText("Set " + setCount);
fieldsContainer.addView(setFields);
}
}
a class for the big frament
public class ActiveWorkoutFragment extends Fragment {
private RecyclerView recyclerView;
private ActiveWorkoutAdapter adapter;
private List<WorkoutFromAPI> activeWorkouts = new ArrayList<>();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
setRetainInstance(true);
return inflater.inflate(R.layout.fragment_active_workouts, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new ActiveWorkoutAdapter(activeWorkouts, new ActiveWorkoutAdapter.OnItemClickListener() {
@Override
public void onItemClick(WorkoutFromAPI workout) {
showWorkoutDetailFragment(workout);
}
});
recyclerView.setAdapter(adapter);
}
public void addWorkout(WorkoutFromAPI workout) {
activeWorkouts.add(workout);
adapter.notifyItemInserted(activeWorkouts.size() - 1);
Toast.makeText(getContext(), "Workout added to today's list", Toast.LENGTH_SHORT).show();
}
private void showWorkoutDetailFragment(WorkoutFromAPI workout) {
ActiveWorkoutsDetailFragment fragment = ActiveWorkoutsDetailFragment.newInstance(workout);
FragmentTransaction transaction = requireActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
and a class for the detailed fragment:
public class ActiveWorkoutsDetailFragment extends Fragment {
private static final String ARG_WORKOUT = "workout";
private WorkoutFromAPI workout;
private LinearLayout fieldsContainer;
private int setCount = 0;
public static ActiveWorkoutsDetailFragment newInstance(WorkoutFromAPI workout) {
ActiveWorkoutsDetailFragment fragment = new ActiveWorkoutsDetailFragment();
Bundle args = new Bundle();
args.putSerializable(ARG_WORKOUT, workout);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
workout = (WorkoutFromAPI) getArguments().getSerializable(ARG_WORKOUT);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_active_workouts_detail, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView nameTextView = view.findViewById(R.id.NameTextView);
TextView equipmentTextView = view.findViewById(R.id.EquipmentTextView);
TextView instructionsTextView = view.findViewById(R.id.InstructionsTextView);
Button addButton = view.findViewById(R.id.addButton);
fieldsContainer = view.findViewById(R.id.fieldsContainer);
if (workout != null) {
nameTextView.setText(workout.getName());
equipmentTextView.setText(workout.getEquipment());
instructionsTextView.setText(workout.getInstructions());
} else {
// toast an error message if workout is null
Toast.makeText(getContext(), "Workout data is missing", Toast.LENGTH_SHORT).show();
}
addButton.setOnClickListener(v -> addSetFields());
}
private void addSetFields() {
LayoutInflater inflater = LayoutInflater.from(getContext());
View setFields = inflater.inflate(R.layout.item_set_fields_for_active_workouts, fieldsContainer, false);
TextView setLabel = setFields.findViewById(R.id.setLabel);
setCount++;
setLabel.setText("Set " + setCount);
fieldsContainer.addView(setFields);
}
}
The elements that I add to the ActiveWorkoutFragment are coming from an API request.
Now, I tried to change it to mvvm and observer. I created ViewModel and Model classes, and then changed the adapter to match the changes. But when I click on the element on the list, it enters the detailed fragment(ActiveWorkoutsDetailFragment), but nothing is shown. I can navigate out, which means that somehow it open the fragment, but nothing is shown? Code for the mvvm and observer pattern:
public class ActiveWorkoutsDetailViewModel extends ViewModel {
private final MutableLiveData<ActiveWorkoutModel> workout = new MutableLiveData<>();
private final MutableLiveData<Integer> setCount = new MutableLiveData<>(0);
public LiveData<ActiveWorkoutModel> getWorkout() {
return workout;
}
public void setWorkout(ActiveWorkoutModel workout) {
this.workout.setValue(workout);
}
public LiveData<Integer> getSetCount() {
return setCount;
}
public void incrementSetCount() {
setCount.setValue(setCount.getValue() + 1);
}
public void addSet(Set set) {
ActiveWorkoutModel currentWorkout = workout.getValue();
if (currentWorkout != null) {
currentWorkout.getSets().add(set);
workout.setValue(currentWorkout);
}
}
}
public class ActiveWorkoutsDetailFragment extends Fragment {
private static final String ARG_WORKOUT = "workout";
private ActiveWorkoutsDetailViewModel viewModel;
private LinearLayout fieldsContainer;
private Button addButton;
private TextView nameTextView;
private TextView equipmentTextView;
private TextView instructionsTextView;
public static ActiveWorkoutsDetailFragment newInstance(ActiveWorkoutModel workout) {
ActiveWorkoutsDetailFragment fragment = new ActiveWorkoutsDetailFragment();
Bundle args = new Bundle();
args.putSerializable(ARG_WORKOUT, workout);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ActiveWorkoutsDetailViewModel.class);
if (getArguments() != null) {
ActiveWorkoutModel workout = (ActiveWorkoutModel) getArguments().getSerializable(ARG_WORKOUT);
viewModel.setWorkout(workout);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_active_workouts_detail, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
nameTextView = view.findViewById(R.id.NameTextView);
equipmentTextView = view.findViewById(R.id.EquipmentTextView);
instructionsTextView = view.findViewById(R.id.InstructionsTextView);
addButton = view.findViewById(R.id.addButton);
fieldsContainer = view.findViewById(R.id.fieldsContainer);
viewModel.getWorkout().observe(getViewLifecycleOwner(), workout -> {
if (workout != null) {
nameTextView.setText(workout.getName());
equipmentTextView.setText(workout.getEquipment());
instructionsTextView.setText(workout.getInstructions());
}
});
viewModel.getSetCount().observe(getViewLifecycleOwner(), count -> {
fieldsContainer.removeAllViews();
for (int i = 0; i < count; i++) {
addSetFields(i + 1);
}
});
addButton.setOnClickListener(v -> {
viewModel.incrementSetCount();
Set newSet = new Set(viewModel.getSetCount().getValue(), 0, 0); // Replace with actual data if available
viewModel.addSet(newSet);
});
}
private void addSetFields(int setNumber) {
LayoutInflater inflater = LayoutInflater.from(getContext());
View setFields = inflater.inflate(R.layout.item_set_fields_for_active_workouts, fieldsContainer, false);
TextView setLabel = setFields.findViewById(R.id.setLabel);
setLabel.setText("Set " + setNumber);
fieldsContainer.addView(setFields);
}
}
public class ActiveWorkoutFragment extends Fragment {
private RecyclerView recyclerView;
private ActiveWorkoutAdapter adapter;
private List<ActiveWorkoutModel> activeWorkouts = new ArrayList<>();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
setRetainInstance(true); // Retain this fragment instance across configuration changes
return inflater.inflate(R.layout.fragment_active_workouts, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new ActiveWorkoutAdapter(activeWorkouts, new ActiveWorkoutAdapter.OnItemClickListener() {
@Override
public void onItemClick(ActiveWorkoutModel workout) {
showWorkoutDetailFragment(workout);
}
});
recyclerView.setAdapter(adapter);
}
public void addWorkout(ActiveWorkoutModel workout) {
activeWorkouts.add(workout);
adapter.notifyItemInserted(activeWorkouts.size() - 1);
Toast.makeText(getContext(), "Workout added to today's list", Toast.LENGTH_SHORT).show();
}
private void showWorkoutDetailFragment(ActiveWorkoutModel workout) {
ActiveWorkoutsDetailFragment fragment = ActiveWorkoutsDetailFragment.newInstance(workout);
FragmentTransaction transaction = requireActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
private ActiveWorkoutModel convertToActiveWorkoutModel(WorkoutFromAPI workoutFromAPI) {
return new ActiveWorkoutModel(
workoutFromAPI.getName(),
workoutFromAPI.getEquipment(),
workoutFromAPI.getInstructions(),
new ArrayList<>()
);
}
}
public class ActiveWorkoutAdapter extends RecyclerView.Adapter<ActiveWorkoutAdapter.HomeWorkoutViewHolder> {
private List<ActiveWorkoutModel> workoutList;
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(ActiveWorkoutModel workout);
}
public ActiveWorkoutAdapter(List<ActiveWorkoutModel> workoutList, OnItemClickListener listener) {
this.workoutList = workoutList;
this.listener = listener;
}
@NonNull
@Override
public HomeWorkoutViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_active_workout, parent, false);
return new HomeWorkoutViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull HomeWorkoutViewHolder holder, int position) {
ActiveWorkoutModel workout = workoutList.get(position);
holder.bind(workout, listener);
}
@Override
public int getItemCount() {
return workoutList != null ? workoutList.size() : 0;
}
public void updateWorkoutList(List<ActiveWorkoutModel> newList) {
workoutList = newList;
notifyDataSetChanged();
}
public static class HomeWorkoutViewHolder extends RecyclerView.ViewHolder {
private final TextView workoutName;
public HomeWorkoutViewHolder(View itemView) {
super(itemView);
workoutName = itemView.findViewById(R.id.homeWorkoutNameTextView);
}
public void bind(ActiveWorkoutModel workout, OnItemClickListener listener) {
workoutName.setText(workout.getName());
itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onItemClick(workout);
}
});
}
}
}
Any ideas, tips are more than welcome.
Upvotes: 0
Views: 27