I have a bottomsheet fragment and I have implemented two recyclerviews in it. The bottomsheet takes the argument of a arraylist of a custom datatype "UserMedicalData". This custom type has an argument of type boolean .If the boolean is true it should be added to the dataset of recyclerview b and should be check marked in recyclerview A. if we click on an item in recyclerview A , it should change it's boolean status . If the boolean is true it should be added to the recyclerview B anmd should be marked checked in recyclerview A. If it was already marked checked , it should tutn its boolean status to false, uncheck itself and remove itself from the recyclerview B. Also if the item is clicked in recyclerview B ,it should turn its boolean status to false and delete itself from recyclerview B's dataset and uncheck itslef from the recyclerview A. In simple terms , if the item's boolean is true it should be added to recyclerview B , iof not it should be removed from recyclerview B. In all cases, the boolean status should be reflected in the recyclerview A. I have implemented a soultion , please help me optimize it .Here are the files
The bottomsheet fragment:
public class DiseasesBottomSheetFragment extends BottomSheetDialogFragment implements SelectDiseaseBottomSheetView, View.OnTouchListener, TextWatcher, View.OnClickListener {
private SelectDiseaseBottomSheetViewHolder holder;
private SelectDiseaseBottomSheetPresenter presenter;
private AllDiseasesAdapter adapter;
private SelectedDiseaseBottomSheetAdapter selectedDiseasesAdapter;
private DiseaseFragmentCallback callback;
private BottomSheetBehavior<View> bottomSheetBehavior;
public DiseasesBottomSheetFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
WindowManager windowManager = requireActivity().getWindowManager();
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
int screenHeight = displayMetrics.heightPixels;
int desiredHeight = (int) (screenHeight * 0.94);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) view.getParent()).getLayoutParams();
bottomSheetBehavior = (BottomSheetBehavior<View>) params.getBehavior();
assert bottomSheetBehavior != null;
bottomSheetBehavior.setPeekHeight(desiredHeight);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_diseases_bottom_sheet, container, false);
initGui(view);
initVariables();
recieveArguments();
setItemsForAllDiseasesRV();
setItemsForSelectedDiseasesRV();
setListeners();
return view;
}
private void setItemsForSelectedDiseasesRV() {
if (!getSelectedItems().isEmpty()){
setSelectedDiseasesRV(presenter.getItems());
}
}
private void setItemsForAllDiseasesRV() {
if (Objects.requireNonNull(holder.getSearchEditText().getText()).toString().isEmpty()){
if (!presenter.getAllDiseasesList().isEmpty()){
setAllDiseasesRV(presenter.getAllDiseasesList());
}
else{
Toast.makeText(getContext(), "Please retry again later.", Toast.LENGTH_SHORT).show();
}
}
else{
setUpFilteredRV();
}
}
private void setUpFilteredRV() {
}
private void recieveArguments() {
// Retrieve arguments
Bundle args = getArguments();
if (args != null) {
presenter.ProcessArguments(args);
}
}
@Override
public void addToSelectedItems(int position) {
if (!getSelectedItems().contains(getAllItems().get(position))){
getSelectedItems().add(getAllItems().get(position));
}
}
private void setListeners() {
holder.getDoneButton().setOnClickListener(this);
holder.getSearchEditText().setOnTouchListener(this);
holder.getSearchEditText().addTextChangedListener(this);
}
private void initGui(View view) {
holder = new SelectDiseaseBottomSheetViewHolder(view);
}
private void initVariables() {presenter = new SelectDiseaseBottomSheetPresenter( this, getContext());}
private AdpaterViewItemClickedListener adpaterViewItemClickedListener = new AdpaterViewItemClickedListener() {
@Override
public void onAdatviewItemClicked(int position) {}
@Override
public void onAdatviewItemClicked(int position, int requestID) {
if (requestID == AppConstants.DISEASE_CLICKED_TO_ADD_TO_LIST) {
getAllItems().get(position).setSelected(true);
if (!getSelectedItems().contains(getAllItems().get(position))){
addToSelectedItems(position);
if (!getSelectedItems().isEmpty()){
setItemsForSelectedDiseasesRV();
selectedDiseasesAdapter.notifyDataSetChanged();
}
}
}
if (requestID == AppConstants.DISEASE_CLICKED_TO_REMOVE_FROM_LIST) {
getAllItems().get(position).setSelected(false);
if (!getSelectedItems().isEmpty() && getSelectedItems().contains(getAllItems().get(position))){
getSelectedItems().remove(getAllItems().get(position));
selectedDiseasesAdapter.notifyDataSetChanged();
}
}
if (requestID == AppConstants.SELECTED_DISEASE_CLICKED) {
try {
if(getSelectedItems() != null && getSelectedItems().size() > 0) {
uncheckRemovedItemFromAllItemList(getSelectedItems().get(position).getId());
}
getSelectedItems().remove(getSelectedItems().get(position));
selectedDiseasesAdapter.notifyItemRemoved(position);
} catch (IndexOutOfBoundsException e) {
System.out.println("Disease already deleted");
}
}
}
public void uncheckRemovedItemFromAllItemList(int removedItemId) {
for(int i=0; i<getAllItems().size(); i++) {
if(getAllItems().get(i).getId() == removedItemId) {
getAllItems().get(i).setSelected(false);
adapter.notifyItemChanged(i);
break;
}
}
}
@Override
public void onAdatviewItemClicked(int position, int requestID, String s) {}
@Override
public void onAdatviewItemClicked(UserMedicalData userMedicalData, int requestID) {
if (requestID == AppConstants.DISEASE_CLICKED_TO_ADD_TO_LIST) {
for (int i=0; i<=getAllItems().size();i++){
if (getAllItems().get(i).getId()==userMedicalData.getId()){
getAllItems().get(i).setSelected(true);
addToSelectedItems(i);
if (!getSelectedItems().isEmpty()){
setItemsForSelectedDiseasesRV();
selectedDiseasesAdapter.notifyDataSetChanged();
}
break;
}
}
}
if (requestID == AppConstants.DISEASE_CLICKED_TO_REMOVE_FROM_LIST) {
for (int i=0; i<=getAllItems().size();i++){
if (getAllItems().get(i).getId()==userMedicalData.getId()){
getAllItems().get(i).setSelected(false);
if (!getSelectedItems().isEmpty() && getSelectedItems().contains(getAllItems().get(i))){
getSelectedItems().remove(getAllItems().get(i));
selectedDiseasesAdapter.notifyDataSetChanged();
}
break;
}
}
}
}
};
@Override
public void filterList() {
if (presenter.getFilteredArrayList() != null) {
if (adapter != null) {
adapter.filter(presenter.getFilteredArrayList());
}
}
}
@Override
public void setNoRecordsVisibility(boolean isVisible) {
if (isVisible){
holder.getNoRecordsFound().setVisibility(View.VISIBLE);
}else {
holder.getNoRecordsFound().setVisibility(View.GONE);
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.search_text_view:
break;
case R.id.done_button:
sendDataToActivity();
dismiss();
break;
}
}
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
Bundle args = new Bundle();
args.putParcelableArrayList("selectedArray",new ArrayList<>(getSelectedItems()));
setArguments(args);
super.onDismiss(dialog);
}
@Override
public List<UserMedicalData> getAllItems() {return presenter.getAllDiseasesList();}
@Override
public List<UserMedicalData> getSelectedItems() {return presenter.getItems();}
@Override
public void setAllDiseasesRV(List<UserMedicalData> data) {
if (!data.isEmpty()) {
adapter = new AllDiseasesAdapter(getContext(),adpaterViewItemClickedListener, data);
holder.getAllDiseasesRV().setAdapter(adapter);
}
}
@Override
public void setSelectedDiseasesRV(List<UserMedicalData> items) {
if (!items.isEmpty()) {
selectedDiseasesAdapter = new SelectedDiseaseBottomSheetAdapter(getContext(), items,adpaterViewItemClickedListener);
holder.getSelectedDiseasesRV().setAdapter(selectedDiseasesAdapter);
}
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
view.setFocusable(true);
view.setFocusableInTouchMode(true);
return false;
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void afterTextChanged(Editable editable) {
if (editable != null) {
presenter.filter(editable.toString());
}
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.toggleSoftInput(0, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof DiseaseFragmentCallback) {
callback = (DiseaseFragmentCallback) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement FragmentCallback");
}
}
private void sendDataToActivity() {
if (callback != null) {
callback.onDataReceived(presenter.getItems());
}
}
}
It's presenter:
public class SelectDiseaseBottomSheetPresenter implements ServerConnectListener {
private SelectDiseaseBottomSheetView view;
private Context context;
private List<UserMedicalData> allDiseasesList;
private List<UserMedicalData> items = new ArrayList<>();
private List<UserMedicalData> filteredArrayList = new ArrayList<>();
public SelectDiseaseBottomSheetPresenter(SelectDiseaseBottomSheetView view, Context context) {
this.view = view;
this.context = context;
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public List<UserMedicalData> getAllDiseasesList() {
return allDiseasesList;
}
public void setAllDiseasesList(List<UserMedicalData> allDiseasesList) {
this.allDiseasesList = allDiseasesList;
}
public List<UserMedicalData> getItems() {
return items;
}
public void setItems(List<UserMedicalData> items) {
this.items = items;
}
public List<UserMedicalData> getFilteredArrayList() {
return filteredArrayList;
}
public void setFilteredArrayList(List<UserMedicalData> filteredArrayList) {
this.filteredArrayList = filteredArrayList;
}
public void filter(String toString) {
filteredArrayList = new ArrayList<>();
for (UserMedicalData item : view.getAllItems()) {
if (item.getTitle().toLowerCase().contains(toString.toLowerCase())) {
view.setNoRecordsVisibility(false);
filteredArrayList.add(item);
}
}
if (filteredArrayList.size() == 0) {
view.setNoRecordsVisibility(true);
}else {
view.setNoRecordsVisibility(false);
}
view.filterList();
}
public void ProcessArguments(Bundle args){
if (args.containsKey("all_items_array")){
allDiseasesList = args.getParcelableArrayList("all_items_array");
for (int i = 0; i < allDiseasesList.size(); i++){
if (allDiseasesList.get(i).getSelected()){
view.addToSelectedItems(i);
}
}
}
}
@Override
public void onSuccess(ServerResponse response) {
}
@Override
public void onFailure(ServerResponse response) {
}
@Override
public void onSessionExpiry(ServerResponse response) {
}
}
It's view interface:
public interface SelectDiseaseBottomSheetView {
List<UserMedicalData> getAllItems();
List<UserMedicalData> getSelectedItems();
void setAllDiseasesRV(List<UserMedicalData> items);
void setSelectedDiseasesRV(List<UserMedicalData> items);
void filterList();
void setNoRecordsVisibility(boolean isVisible);
void addToSelectedItems(int position);
}
The adatViewClickListener interface:
public interface AdpaterViewItemClickedListener {
void onAdatviewItemClicked(int position);
void onAdatviewItemClicked(int position, int requestID);
void onAdatviewItemClicked(int position, int requestID, String s);
default void onAdatviewItemClicked(UserMedicalData userMedicalData, int requestID){
}
}
The AllDiseases Adapter for recyclerview A:
public class AllDiseasesAdapter extends RecyclerView.Adapter<AllDiseasesViewHolder>{
private Context context;
private AdpaterViewItemClickedListener listener;
private List<UserMedicalData> data;
public AllDiseasesAdapter(Context context, AdpaterViewItemClickedListener listener, List<UserMedicalData> data) {
this.context = context;
this.listener = listener;
this.data = data;
}
@NonNull
@Override
public AllDiseasesViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.items_with_checkbox_layout, parent, false);
return new AllDiseasesViewHolder(view, listener, context);
}
@Override
public void onBindViewHolder(@NonNull AllDiseasesViewHolder holder, int position) {
holder.getItemText().setText(data.get(position).getTitle());
holder.bindData(data.get(position));
if (data.get(position).getSelected()){
holder.getCheckBox().setChecked(data.get(position).getSelected());
}else{
holder.getCheckBox().setChecked(false);
}
}
public void filter(List<UserMedicalData> filteredArrayList) {
data = new ArrayList<>();
if (filteredArrayList != null) {
data = filteredArrayList;
notifyDataSetChanged();
}
}
@Override
public int getItemCount() {
return data.size();
}
}
It's viewholder:
public class AllDiseasesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private AdpaterViewItemClickedListener listener;
private Context context;
public ConstraintLayout itemLayout;
public TextView itemText;
public CheckBox checkBox;
public UserMedicalData userMedicalData;
public AllDiseasesViewHolder(@NonNull View view, AdpaterViewItemClickedListener listener, Context context) {
super(view);
initGui(view);
initVariables(listener, context);
setListeners();
}
private void initVariables(AdpaterViewItemClickedListener listener, Context context) {
this.listener = listener;
this.context = context;
}
public TextView getItemText() {
return itemText;
}
public CheckBox getCheckBox() {
return checkBox;
}
private void initGui(View view) {
itemLayout = view.findViewById(R.id.item_layout);
itemText = view.findViewById(R.id.text);
checkBox = view.findViewById(R.id.checkbox);
}
private void setListeners() {
itemLayout.setOnClickListener(this);
}
public void bindData(UserMedicalData userMedicalData){
this.userMedicalData=userMedicalData;
}
@Override
public void onClick(View view) {
int viewId = view.getId();
if (viewId == itemLayout.getId()) {
if (getCheckBox().isChecked()){
listener.onAdatviewItemClicked(userMedicalData, AppConstants.DISEASE_CLICKED_TO_REMOVE_FROM_LIST);
getCheckBox().setChecked(false);
}
else{
listener.onAdatviewItemClicked(userMedicalData, AppConstants.DISEASE_CLICKED_TO_ADD_TO_LIST);
getCheckBox().setChecked(true);
}
}
}
}
The selecteddisease Adapter for recyclerview B:
public class SelectedDiseaseBottomSheetAdapter extends RecyclerView.Adapter<SelectedDiseaseViewHolder> {
private List<UserMedicalData> data;
private Context context;
private AdpaterViewItemClickedListener listener;
public SelectedDiseaseBottomSheetAdapter(Context context, List<UserMedicalData> data, AdpaterViewItemClickedListener listener) {
this.context = context;
this.data = data;
this.listener = listener;
}
@NonNull
@Override
public SelectedDiseaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.selected_disease_bottomsheet_layout, parent, false);
return new SelectedDiseaseViewHolder(view, listener, context);
}
@Override
public void onBindViewHolder(@NonNull SelectedDiseaseViewHolder holder, int position) {
holder.getItemText().setText(data.get(position).getTitle());
}
@Override
public int getItemCount() {
return data.size();
}
}
And it's viewholder:
public class SelectedDiseaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView itemText;
private AdpaterViewItemClickedListener listener;
private Context context;
public SelectedDiseaseViewHolder(@NonNull View view, AdpaterViewItemClickedListener listener, Context context) {
super(view);
initGui(view);
initVariables(listener, context);
setListeners();
}
private void initVariables(AdpaterViewItemClickedListener listener, Context context) {
this.listener = listener;
this.context = context;
}
public TextView getItemText() {
return itemText;
}
private void initGui(View view) {
itemText = view.findViewById(R.id.text);
}
private void setListeners() {
itemText.setOnClickListener(this);
}
@Override
public void onClick(View view) {
int viewId = view.getId();
if (viewId == itemText.getId()) {
listener.onAdatviewItemClicked(getAbsoluteAdapterPosition(), AppConstants.SELECTED_DISEASE_CLICKED);
}
}
}
Lastly here is the datamodel for the items of the arraylist set as dataset of these recyclerviews:
@Keep
public class UserMedicalData implements Parcelable {
private int id;
private String title;
private String type;
private boolean selected;
protected UserMedicalData(Parcel in) {
id = in.readInt();
title = in.readString();
type = in.readString();
selected = in.readByte() != 0;
}
public static final Creator<UserMedicalData> CREATOR = new Creator<UserMedicalData>() {
@Override
public UserMedicalData createFromParcel(Parcel in) {
return new UserMedicalData(in);
}
@Override
public UserMedicalData[] newArray(int size) {
return new UserMedicalData[size];
}
};
public int getId () {return id;}
public void setId (int id) {this.id = id;}
public String getTitle () {return title;}
public void setTitle (String title) {this.title = title;}
public String getType () {return type;}
public void setType (String type) {this.type = type;}
public boolean getSelected () {return selected;}
public void setSelected (boolean selected) {this.selected = selected;}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(id);
parcel.writeString(title);
parcel.writeString(type);
parcel.writeByte((byte) (selected ? 1 : 0));
}
}
This solution works fine for toggling the views and Booleans , but I want to know if there is a simpler more concise method to do so. Sometimes ,using the filter on recyclerview A and toggling between data , triggers some data's booleans to turn true automatically . I have tried various ways to debug this issue and handle it but it's to no avail working for me. Please let me know if I am doing something wrong or if anything can be improved for the best .
I have tried toggling the Booleans and updating dataset using "notifyDataSetChanged" and "notifyItemRemoved" and "notifyItemAdded" methods to update the recyclerviews but during robust testing , the main recyclerview shows weird behavior that I am unable to debug or resolve.