1

I have a recycler service adapter and the view is some sort of complex button which is created from some xml file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/services_container"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="20dp"
    android:layout_marginRight="20dp"
    android:layout_marginStart="20dp"
    android:layout_marginTop="15dp"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/services_place_holder"
        android:layout_width="@dimen/viewPlaceHolderWidth_services"
        android:layout_height="@dimen/viewPlaceHolderHeight_services"
        android:layout_marginTop="10dp"
        android:background="@drawable/rounded_view_selector_services"
        android:focusable="false"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        app:layout_constraintTop_toTopOf="@id/services_container">

        <TextView
            android:id="@+id/service_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:background="@drawable/rounded_text_view_for_services"
            android:paddingBottom="2dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:paddingTop="2dp"
            android:text="۲۳۲۲۲۳۵۶۶"
            android:textAlignment="center"
            android:textColor="@color/colorWhite"
            android:textSize="12sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/service_image"
            android:layout_width="38dp"
            android:layout_height="38dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="6dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/service_price"
            app:srcCompat="@drawable/placeholder_blue" />

        <me.grantland.widget.AutofitTextView
            android:id="@+id/service_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="11dp"
            android:layout_marginStart="11dp"
            android:layout_marginTop="2dp"
            android:layout_marginBottom="2dp"
            android:paddingLeft="8dp"
            android:paddingRight="8dp"
            android:maxLines="1"
            android:text="پرستار"
            android:textColor="@color/colorElahiyeBlue"
            android:textSize="18sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/service_image"
            app:minTextSize="6sp" />
    </android.support.constraint.ConstraintLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="-120dp"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/services_gender_image"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:src="@drawable/available_gender" />

        <ImageView
            android:id="@+id/services_prescription_image"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_marginLeft="7dp"
            android:layout_marginStart="7dp"
            android:src="@drawable/available_picture" />

        <ImageView
            android:id="@+id/services_info_image"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_marginLeft="7dp"
            android:layout_marginStart="7dp"
            android:src="@drawable/info" />
    </LinearLayout>
</LinearLayout>

And I have a service adapter that handles the click event inside it, the data set is loaded from a web service using retrofit and there is no problem with that but my problem is I want on click on each item the background and some other UI and position of them to be changed and stored , when the data set is lower than 4 everything works fine but when the data set is about 6 or more then some weird behaviors appear like: when I click on one item the other item is clicked , the way I managed the click is by isFocusable of my view Layout "services_container" or "services_place_holder" , the adapter code is here:

public class ServicesAdapter extends RecyclerView.Adapter<ServicesAdapter.ViewHolder> {

    private List<Integer> selectedServices = new ArrayList<>();
    private LayoutInflater mInflater;
    private ItemClickListener mClickListener;
    private Context mContext;
    private int row_index = -1;

    // data is passed into the constructor
//    public FieldOfEmploymentAdapter(Context context, List<Integer> colors, List<String> servicesDataItemsList) {
    ServicesAdapter(Context context, List<ServicesDataItems> servicesDataItemsList) {
        mContext = context;
        this.mInflater = LayoutInflater.from(context);
//        this.mViewColors = colors;
        this.servicesDataItemsList = servicesDataItemsList;
    }

    // inflates the row layout from xml when needed
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.services_items, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }

    // binds the data to the view and textview in each row
    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
//        int color = mViewColors.get(position);
        final ServicesDataItems servicesDataItems = servicesDataItemsList.get(position);
        holder.linLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                row_index = position;
                notifyDataSetChanged();
            }
        });

        if (row_index == position) {
            Toast.makeText(mContext, "Pos: " + position + " , GetAdapterPos: " + holder.getAdapterPosition(), Toast.LENGTH_SHORT).show();

            //                Toast.makeText(mContext, "SSSS" + MainActivity.servicesNumber.getText().toString(), Toast.LENGTH_SHORT).show();
            int servicesNumber = Integer.parseInt(MainActivity.servicesNumber.getText().toString());


//                Toast.makeText(mContext, "is instance of BitmapDrawable= " + holder.constraintLayout.isFocusable() + "    ======     ", Toast.LENGTH_SHORT).show();
            if (holder.linLayout.isSelected()){
                Toast.makeText(mContext, "IsSelected ", Toast.LENGTH_SHORT).show();
            }
            if (holder.linLayout.isFocusable()) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    holder.constraintLayout.setBackground(ContextCompat.getDrawable(mContext, R.drawable.rounded_view_selector_services));
                    holder.linLayout.setFocusable(false);
                    MainActivity.servicesNumber.setText(String.valueOf(--servicesNumber));
                    removeSelectedService(position);

                } else {
                    holder.constraintLayout.setBackgroundDrawable(ContextCompat.getDrawable(mContext, R.drawable.rounded_view_selector_services));
                    holder.linLayout.setFocusable(false);
                    MainActivity.servicesNumber.setText(String.valueOf(--servicesNumber));
                    removeSelectedService(position);
                }
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    holder.constraintLayout.setBackground(ContextCompat.getDrawable(mContext, R.drawable.rounded_view_selector_services_selected));
                    holder.linLayout.setFocusable(true);
                    MainActivity.servicesNumber.setText(String.valueOf(++servicesNumber));
                    selectedServices.add(position);
                } else {
                    holder.constraintLayout.setBackgroundDrawable(ContextCompat.getDrawable(mContext, R.drawable.rounded_view_selector_services_selected));
                    holder.linLayout.setFocusable(true);
                    MainActivity.servicesNumber.setText(String.valueOf(++servicesNumber));
                    selectedServices.add(position);
                }
            }

            if (servicesNumber > 0) {
                Drawable selectLocation = ContextCompat.getDrawable(mContext, R.drawable.rbtn_location_active);
                ColorStateList colorStateList = ContextCompat.getColorStateList(mContext, R.color.rbtn_text_color_button);
                new EnableLocationSelectButton(selectLocation, colorStateList).invoke();
            }

            if (servicesNumber == 0) {
                Drawable selectLocation = ContextCompat.getDrawable(mContext, R.drawable.rbtn_location_dimmed);
                ColorStateList colorStateList = ContextCompat.getColorStateList(mContext, R.color.ElahiyeGray);
                new DisableLocationSelectButton(selectLocation, colorStateList).invoke();
            }

            boolean isPrescriptionImageVisible = false;
            boolean isGenderImageVisible = false;

            for (int selected : selectedServices) {
                if (servicesDataItemsList.get(selected).has_prescriptions_image()) {
                    isPrescriptionImageVisible = true;
                    if (MainActivity.prescriptionImageHolder.getVisibility() == View.INVISIBLE) {
                        MainActivity.gender_prescription_selection.setVisibility(View.VISIBLE);
                        MainActivity.gender_prescription_separator.setVisibility(View.VISIBLE);
                        MainActivity.doctorPrescription.setVisibility(View.VISIBLE);
                    }
                }

                if (servicesDataItemsList.get(selected).hasGender()) {
                    isGenderImageVisible = true;
                    MainActivity.gender_prescription_selection.setVisibility(View.VISIBLE);
                    MainActivity.gender_prescription_separator.setVisibility(View.VISIBLE);
                    MainActivity.genderSelection.setVisibility(View.VISIBLE);
                }
            }

            if (!isPrescriptionImageVisible) {
                MainActivity.doctorPrescription.setVisibility(View.INVISIBLE);
                MainActivity.prescriptionImageHolder.setVisibility(View.INVISIBLE);
            }

            if (!isGenderImageVisible) {
                MainActivity.genderSelection.setVisibility(View.INVISIBLE);
            }

            if (!isPrescriptionImageVisible && !isGenderImageVisible) {

                MainActivity.gender_prescription_selection.setVisibility(View.INVISIBLE);
                MainActivity.gender_prescription_separator.setVisibility(View.INVISIBLE);
            }
        }
        holder.service_name.setText(servicesDataItems.getServices_name());
        holder.service_price.setText(servicesDataItems.getServices_price());
    }

    private void removeSelectedService(int position) {
        int index = 0;
        for (int selected : selectedServices) {
            if (selected == position) {
                selectedServices.remove(index);
                return;
            }
            index++;
        }
    }

    // total number of rows
    @Override
    public int getItemCount() {
        return servicesDataItemsList.size();
    }

    // stores and recycles views as they are scrolled off screen
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        //        ImageView myImageView;
        TextView service_name;
        TextView service_price;
        ConstraintLayout constraintLayout;
        LinearLayout linLayout;
        ImageView serviceGender;
        ImageView servicePrescription;

        ViewHolder(View itemView) {
            super(itemView);
//            myImageView = itemView.findViewById(R.id.service_image);
            service_name = itemView.findViewById(R.id.service_name);
            service_price = itemView.findViewById(R.id.service_price);
            constraintLayout = itemView.findViewById(R.id.services_place_holder);
            linLayout = itemView.findViewById(R.id.services_container);
            serviceGender = itemView.findViewById(R.id.services_gender_image);
            servicePrescription = itemView.findViewById(R.id.services_prescription_image);
            itemView.setOnClickListener(this);
        }
}

Please tell me how can manage OnClick listener on each item properly ? I also read all the other topics but none of them helped me.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Javad
  • 213
  • 2
  • 9
  • Please clean up your code. The code is hard to read. Please remove the code which is commented out and hence not necessary. – Reaz Murshed Mar 31 '18 at 19:37
  • allright ill do it right now – Javad Mar 31 '18 at 19:48
  • I removed all the unnecessary codes! – Javad Mar 31 '18 at 19:53
  • Without trying to be too critical, this code is not well encapsulated. You are making calls to your `MainActivity` with stuff like `MainActivity.prescriptionImageHolder`. This is not a good model. You are directly binding `ServicesAdapter` to `MainActivity`! Meaning that `ServicesAdapter` is not re-usable. – Barns Mar 31 '18 at 19:57

1 Answers1

1

I have gone through your code and I think I found the problem already. You have a condition where you check if the row_index is equal to position. However, I do not see any else part of this statement.

It works when your dataset is small, as small as you do not have to scroll it. However, when your dataset is large and you have to scroll it, it should have some random behaviour as you have not specified the else part of the statement. The views are recycled each time you scroll and you need to tell exactly what to be loaded in each row when you are implementing your onBindViewHolder function.

So I would like to suggest you to have the assignment for the holder items even if the condition is not met (i.e. else part).

if (row_index == position) {
    // Your current implementation
} else {
    // Set the views accordingly when the above condition is not met.
}

Update

If you want to have an implementation for multiple item selection in your RecyclerView then you might need to keep a separate array to keep the track of the selected items and then recycle the items according to the track that you kept in your array. For example, let us consider an array to keep the track of the clicks.

public int[] clickTrack = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

Now when you are clicking an item, just keep the track of the click into your array.

@override 
public void onClick(View v) {
    if(clickTrack[position] == 0) clickTrack[position] = 1;
    else clickTrack[position] = 0;

    notifyDataSetChanged();
}

Now the array will be looking something like this when you have clicked some items already.

{0, 1, 0, 1, 1, 0, 1, 0, 1, 0}

In your onBindViewHolder, just expand the items based on the value that you have found in that array where you kept the track of the click.

if(clickTrack[position] == 1) someView.expand();
else someView.collapse();

This is a sample implementation. I hope you get the idea. Here's a nice answer which implements the same. You might consider having a look there as well.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
  • thanks a lot for trying to help me I'll try that now and tell you the result! – Javad Mar 31 '18 at 19:56
  • You are welcome. Please let me know if it was helpful. – Reaz Murshed Mar 31 '18 at 19:58
  • I think your answer might be true, I tried your solution but it didn't work , I don't know why it behaves in an uncontrolled way??? everything just changes randomly!!! – Javad Mar 31 '18 at 20:19
  • You have too much logic here in your `onBindViewHolder`. So it is difficult to debug. However, I think the problem is with your `else` part missing somewhere else as well which I could not find out. Please check if all cases are covered in your `onBindViewHolder` function. – Reaz Murshed Mar 31 '18 at 20:21
  • I added the else clause but I don't know why it doesn't work again! – Javad Mar 31 '18 at 20:22
  • I there any proper way of setting on click listener for recycler adapter that give access to items layout properties? – Javad Mar 31 '18 at 20:23
  • Please check for the other part of your code as well. I think there are several places where the else part is missing. – Reaz Murshed Mar 31 '18 at 20:23
  • The setting of `onClickListener` is fine. – Reaz Murshed Mar 31 '18 at 20:24
  • I think I found the problem sort of! but my problem isn't solved I think I now know what should I ask for help: is there any way to have a RecyclerAdaper for performing multi select items? meaning selecting each item will make background blue and click again make it gray on each item!!!! – Javad Mar 31 '18 at 20:56
  • Great to know that you could make it work. You are most welcome. :) – Reaz Murshed Apr 02 '18 at 02:15