8

I have a RecyclerView with data from local JSON in CardView. I need to implement on selected items when one or some item clicked(change background the item selected or highlight) (like edit in Line App ) but without Button or longpress. But I don't want to use StateListDrawableor (using XML) because I have some JSON data which need to process later.

I need a state in my Activity like a boolean value or something to save every item which I clicked but I don't have any solution again. I have read and try some tutorial but it's not working. This is below my Activity now :

adapter.setOnRecyclerViewClickedListener(new Adapter.OnRecyclerViewItemClickedListener() {
        @Override
        public void OnRecyclerViewItemClicked(int position) {
            boolean selectedItem = false;

            adapter.setOnRecyclerViewClickedListener(new Adapter.OnRecyclerViewItemClickedListener() {

        @Override
        public void OnRecyclerViewItemClicked(int position)  {

    /* ------ boolean variabel for adapter but still not work ----- */
            JSONObject filteredtableList= null;

            try {
                filteredtableList= new JSONObject("response").getJSONObject("tTisch");
            }
            catch (JSONException e) {
                e.printStackTrace();
            }

                if (recyclerView == null) {
                    try {
                        filteredtableList.has("true");
                        filteredtableList.put(status, true);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
                else {
                    try {
                        filteredtableList.put(status, true);

                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            adapter.updateData(filteredTableList);

//----------------------------------------------------
            try {
                Toast.makeText(TableActivity.this, filteredTableList.getJSONObject(position).getString("tischnr"), Toast.LENGTH_SHORT).show();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    });

Adapter.java

public int lastCheckedPosition = -1;
.........
.........
.........
    @Override
public void onBindViewHolder(ViewHolder holder, int position) {

    if (position == lastCheckedPosition) {
        holder.itemView.setBackgroundResource(R.color.colorRedTableOcc);
    } else {
        holder.itemView.setBackgroundResource(R.color.colorTableGreen);
    }

    try {
        currItem = list.getJSONObject(position);
        holder.txt_no_table.setText(currItem.getString("tischnr"));

    } catch (JSONException e) {
        e.printStackTrace();
    }
}


@Override
public int getItemCount() {
    return list.length();
}

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    public TextView txt_no_table;

    public ViewHolder(View itemView) {
        super(itemView);
        itemView.setOnClickListener(this);

        txt_no_table = (TextView) itemView.findViewById(R.id.txt_no_table_empty);
    }

    @Override
    public void onClick(View itemView) {

        recyclerViewItemClickedListener.OnRecyclerViewItemClicked(getAdapterPosition());

        lastCheckedPosition = getAdapterPosition();
        notifyItemRangeChanged(0,list.length());
    }
}

JSON.json

..............
"t-tisch": [
            {
                "tischnr": 1,
                "departement": 1,
                "normalbeleg": 0,
                "kellner-nr": 0,
                "bezeich": "TABLE 01",
                "roomcharge": false,
                "betriebsnr": 0
            },
            {
                "tischnr": 2,
                "departement": 1,
                "normalbeleg": 0,
                "kellner-nr": 0,
                "bezeich": "TABLE 02",
                "roomcharge": false,
                "betriebsnr": 0
            },
............

This is my Activity looks like

enter image description here

Update

Output now:

enter image description here

It's now highlight when I click the item (Thanks to @Burhanuddin Rashid) but its still half for my case. I only can select one item (I can't selected multiple/more items). I need to unselect again when I click the highlight item.

EDITED : I try to write new object and key in my JSON (JSON code above). In my logic, it will make a flag for every item is selected or not, but it still not work.

EDITED : this is my log error :

                  --------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
              Process: com.example.development_laptop.vhp_restotemp, PID: 3590
              java.lang.NullPointerException: Attempt to invoke virtual method 'org.json.JSONObject org.json.JSONObject.put(java.lang.String, boolean)' on a null object reference
                  at com.example.development_laptop.vhp_restotemp.TableActivity$1.OnRecyclerViewItemClicked(TableActivity.java:74)
                  at com.example.development_laptop.vhp_restotemp.com.example.development_laptop.vhp_restotemp.recyclerview.source.Adapter$ViewHolder.onClick(Adapter.java:97)
                  at android.view.View.performClick(View.java:5198)
                  at android.view.View$PerformClick.run(View.java:21147)
                  at android.os.Handler.handleCallback(Handler.java:739)
                  at android.os.Handler.dispatchMessage(Handler.java:95)
                  at android.os.Looper.loop(Looper.java:148)
                  at android.app.ActivityThread.main(ActivityThread.java:5417)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
halfer
  • 19,824
  • 17
  • 99
  • 186
MrX
  • 953
  • 2
  • 16
  • 42
  • For Kotlin users, the following article might be useful [How to delete multiple records from Firestore using RecyclerView multi-selection?](https://medium.com/firebase-tips-tricks/how-to-delete-multiple-records-from-firestore-using-recyclerview-multi-selection-96108e4c6166). – Alex Mamo Jan 04 '21 at 17:11

6 Answers6

3

You can apply setOnClickListener like this:-

View itemView;
    Context context;


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.card_view, parent, false);
        this.context=parent.getContext();
        return new ViewHolder(itemView);
    }
    @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {

            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        holder.linearLayout.setBackgroundColor(context.getResources().getColor(R.color.colorAccent,null));
                    }
                    else {
                        holder.linearLayout.setBackgroundColor(context.getResources().getColor(R.color.colorAccent));

                    }
                }
            });
        }
Nainal
  • 1,728
  • 14
  • 27
  • Do you want to just highlight the selected number and unhighlight it when user removes the finger from that number. – Nainal Dec 20 '16 at 06:31
  • no, i want make a highlight when i choose one or more CardView. and when i click the selected item its make unhighlight again. is my question not clear? i will edit again – MrX Dec 20 '16 at 06:35
3

Try this:

onBindViewHolder:

if (position == lastCheckedPosition) {
            holder.itemView.setBackgroundResource(R.drawable.ic_item_selection_background);
        } else {
            holder.itemView.setBackgroundResource(R.drawable.ic_item_deselection_background));
        }

And this on ViewHolder:

    itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        lastCheckedPosition = getAdapterPosition();
                         notifyItemRangeChanged(0,getItemCount());
                     }
                });

Create lastCheckedPosition as global variable

Burhanuddin Rashid
  • 5,260
  • 6
  • 34
  • 51
  • thanks for answer, its work but its make my CardView become small. And the selected state always show at first – MrX Dec 20 '16 at 06:05
  • 1
    Try modifying this code `notifyItemRangeChanged(0,yourList.size());` – Burhanuddin Rashid Dec 20 '16 at 06:10
  • i was declare `lastCheckedPosition` as `int lastCheckedPosition = 0;` . Its still same :( – MrX Dec 20 '16 at 06:12
  • i'm using `JSONArray` so i change into this `notifyItemRangeChanged(0,list.length());` . But its still same. wait i will edit my question – MrX Dec 20 '16 at 06:16
  • 1
    `myBackground` is this a card view ? – Burhanuddin Rashid Dec 20 '16 at 06:18
  • yes , i want to change my colour of cardview or something, your code is work but its make my cardview have small size than before – MrX Dec 20 '16 at 06:24
  • 1
    Dont change cardview color change the color of the view inside cardview – Burhanuddin Rashid Dec 20 '16 at 06:41
  • 1
    I have provided this code for single selection only...you have to implement diffrent logic for multiple selection – Burhanuddin Rashid Dec 20 '16 at 08:49
  • @BuhanuddinRashid what do you mean this code cant make an mutiple selection? – MrX Dec 20 '16 at 08:54
  • Actually, its still fix half of my problems, i think your code can make a selections for one or more items. I think i will accept this if you help me for another half , but i vote +1 – MrX Dec 20 '16 at 09:27
  • You can refer [this](https://bignerdranch.github.io/recyclerview-multiselect/) [this](http://stackoverflow.com/questions/28570788/select-items-in-recyclerview) – Burhanuddin Rashid Dec 20 '16 at 09:54
  • first refer its not work for me, i'll try the second. Thanks again , i will update if i get something – MrX Dec 20 '16 at 10:25
2

Follow Below Code :

Put this code in your onBindViewHolder() Method.

  @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
    final DataModel dataModel = arraylist.get(position);
            final boolean isSelected = dataModel.isSelected();
            if (isSelected) {
               llParentLayout.setBackgroundColor(Color.Black);
            } else {
               llParentLayout.setBackgroundColor(Color.Green);            
            }
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    public TextView txt_no_table;
    public ViewHolder(View itemView) {
        super(itemView);
        itemView.setOnClickListener(this);

        txt_no_table = (TextView) itemView.findViewById(R.id.txt_no_table_empty);
    }

    @Override
    public void onClick(View itemView) {
final DataModel dataModel = arraylist.get(getAdapterPosition());
        recyclerViewItemClickedListener.OnRecyclerViewItemClicked(dataModel);

    }
}

In your Activity :

adapter.setOnRecyclerViewClickedListener(new Adapter.OnRecyclerViewItemClickedListener() {
        @Override
        public void OnRecyclerViewItemClicked(DataModel dataModel) {

dataModel.setSelected(!dataModel.isSelected());
adapter.notifyDataSetChanged();


        }
    });
Mavya Soni
  • 932
  • 7
  • 15
  • `cannot resolve symbol Data Model` , what's the means of Data Model? – MrX Dec 20 '16 at 05:56
  • You have to set the data in Arraylist with Pojo/Model class and pass it to adapter. DataModel is pojo/Model class – Mavya Soni Dec 20 '16 at 06:04
  • i dont have that class. I only using adapter and activity. maybe there is another solution ? – MrX Dec 20 '16 at 06:17
  • Generally, we have multiple datatype in one position so we should create one model class and set it that model class data and add it in arraylist then pass that arraylist in our adapter. – Mavya Soni Dec 20 '16 at 06:26
  • I get all my data directly from my JSON in asset folder. I only use `getString` or `setString` to process my data. So i dont have another class like setter and getter or etc – MrX Dec 20 '16 at 06:30
  • How to display the data directly Json file to adapter, Have you parsed the data json right? then you have store the data in string but you have to create one Model class for changing the row selected or not for that you have to add on boolean parameter. sorry for my bad english. – Mavya Soni Dec 20 '16 at 06:52
1

I totally change my code but its now work powerfully, here my code looks like :

  1. I change my Adapter into generic adapter. Its not process any data, but its throw to my activity.

    public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
    public int lastCheckedPosition = -1;
    
    private static OnRecyclerViewItemClickedListener recyclerViewItemClickedListener;
    
    public void setOnRecyclerViewClickedListener (OnRecyclerViewItemClickedListener l) {
    recyclerViewItemClickedListener = l;
    }
    
    public interface OnRecyclerViewItemClickedListener {
      void OnRecyclerViewItemClicked(int position);
      void OnRecyclerViewItemBind(ViewHolder holder, int position);
      int OnRecyclerViewItemCount();
    
    }
    public Adapter() {
      super();
    } 
    
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
      View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.table_item_empty, parent, false);
      return new ViewHolder(v);
    }
    
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
    recyclerViewItemClickedListener.OnRecyclerViewItemBind(holder,position);
    }
    
    @Override
    public int getItemCount() {
      return recyclerViewItemClickedListener.OnRecyclerViewItemCount();
    }
    
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    
    public TextView txt_no_table;
    
    public ViewHolder(View itemView) {
        super(itemView);
        itemView.setOnClickListener(this);
    
        txt_no_table = (TextView) itemView.findViewById(R.id.txt_no_table_empty);
    }
    
    @Override
    public void onClick(View itemView) {
    
        recyclerViewItemClickedListener.OnRecyclerViewItemClicked(getAdapterPosition());
         }
       }
    }
    
  2. And My Activity become like this :

    /*Add Click Listener*/
    adapter.setOnRecyclerViewClickedListener(new Adapter.OnRecyclerViewItemClickedListener() {
    
        @Override
        public void OnRecyclerViewItemClicked(int position)  {
            try {
                JSONObject currTable = filteredTableList.getJSONObject(position);
    
                if (currTable.has("selected")) {
                    currTable.put("selected", !currTable.getBoolean("selected"));
                } else {
                    currTable.put("selected",true);
                }
    
                adapter.notifyItemChanged(position);
            } catch (JSONException e) {
                e.printStackTrace();
            }
    
            try
            {
                Toast.makeText(TableActivity.this, filteredTableList.getJSONObject(position).getString("tischnr"), Toast.LENGTH_SHORT).show();
            }
            catch (JSONException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void OnRecyclerViewItemBind(Adapter.ViewHolder holder, int position) {
            try {
                JSONObject currTable = filteredTableList.getJSONObject(position);
    
                holder.txt_no_table.setText(currTable.getString("tischnr"));
    
                int queasy33Index = ProgramMethod.getJSONArrayIndex(queasy33List,"number2", currTable.getInt("tischnr"));
    
                if (queasy33Index >= 0) {
                    holder.txt_no_table.setText(holder.txt_no_table.getText() + "-" +  queasy33List.getJSONObject(queasy33Index).getString("key"));
                }
    
                if (currTable.has("selected") && currTable.getBoolean("selected")) {
                    holder.itemView.setBackgroundResource(R.color.colorRedTableOcc);
                } else {
                    holder.itemView.setBackgroundResource(R.color.colorTableGreen);
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    
  3. Now the click listener save any state and save into boolean value in my JSON file like below :

Multple Selecion

The click state saved into boolean value in JSON

Thanks to everyone with suggest and great answer :)

MrX
  • 953
  • 2
  • 16
  • 42
0

Try below code

Inside

public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

Put

View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false);
v.setOnClickListener(new MyOnClickListener());

And create this class anywhere you want it

class MyOnClickListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
       int itemPosition = recyclerView.indexOfChild(v);
       Log.e("Clicked and Position is ",String.valueOf(itemPosition));
    }
}

I've read before that there is a better way but I like this way is easy and not complicated.

Rissmon Suresh
  • 13,173
  • 5
  • 29
  • 38
0

All Above are very good answer but i am sure you will choose this one this gives you more flexibility than others

 recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), recyclerView, new SideMenuClickListener() {
            @Override
            public void onClick(View view, int position) {

            }

            @Override
            public void onLongClick(View view, int position) {
            }
        }));


static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {

        private GestureDetector gestureDetector;
        private SideMenuClickListener clickListener;

        public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final SideMenuClickListener clickListener) {
            this.clickListener = clickListener;
            gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    return true;
                }

                @Override
                public void onLongPress(MotionEvent e) {
                    View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                    if (child != null && clickListener != null) {
                        clickListener.onLongClick(child, recyclerView.getChildPosition(child));
                    }
                }
            });
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            View child = rv.findChildViewUnder(e.getX(), e.getY());
            if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
                clickListener.onClick(child, rv.getChildPosition(child));
            }
            return false;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean b) {
        }
    }
Sushant Gosavi
  • 3,647
  • 3
  • 35
  • 55