How should I go about implementing Java or XML code for highlighting (or showing a checkbox over) a list row or grid item in Multiple-Choice Modal mode in Android for use with a contextual action bar?
I have implemented the contextual action bar and the ListView/GridView and I can select them and run functions on the selected items but there is no visual feedback except for the brief highlighting of the list row/item when you long-click on it, which disappears when it is released.
My first thought was to set the background color of the row/item in the adapter but I cannot seem to get it to work. I have also tried the solution suggested by the accepted answer of this question: Android ListView Multi-Choice don't show highlight after chlicking but it didn't have any effect on the behavior of my ListView and GridView.
I am mostly interested in knowing the standard way of doing this according to material design guidelines and/or the most common way. Thank you in advance for any advice or solution.
EDIT
I tried Redman's answer (in fact something similar to it, as I am using the Contextual Action Mode and a multiple-choice listener) but I didn't get any result. Here's what I did in the listener:
public void onItemCheckedStateChanged(ActionMode actionMode, int i, long id, boolean checked) {
if (checked) {
selectedItems.add(listAdapter.getItem(i));
((CheckBox) listAdapter.getView(i,null,listView).findViewById(R.id.listCheckBox)).setChecked(true);
}
else {
selectedItems.remove(listAdapter.getItem(i));
((CheckBox) listAdapter.getView(i,null,listView).findViewById(R.id.listCheckBox)).setChecked(false);
}
}
It runs without an error but it doesn't do anything to the checkbox so I'm not sure what the problem is. Any help is really appreciated.
"I am mostly interested in knowing the standard way of doing this according to material design guidelines and/or the most common way."
I'm not sure my answer is material design or common but I've done a GridView based off handling of selections in the Google "Photos" app. I have tested it and know that it will both include a highlighted border and a 'checkbox'.
** disclaimers **
I considered using a selector but there seems to be problems with that if your view scrolls (see this). Also, I used a custom checkbox (really just two different drawables), partially because of this. Also, this answer just basically handles a phone in portrait mode. More would need to be added to handle different configurations (screen size, orientation, etc.)
There's a lot here, but like I said, I've tested it. The whole project has been posted to GitHub.
1st, in MainActivity onCreate()
mList = new ArrayList<>();
// fill your list here
mAdapter = new GridAdapter(getApplicationContext(), R.layout.grid_item, mList);
GridView gridView = (GridView)findViewById(R.id.grid_view);
gridView.setAdapter(mAdapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Log.d(TAG, "item clicked = " + i);
boolean isSelected = mList.get(i).isSelected();
mList.get(i).setSelected(!isSelected);
mAdapter.notifyDataSetChanged();
}
});
Next, overide getView() in your Adapter (I'm using an ArrayAdapter)
@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
RelativeLayout itemLayout;
GridItem gridItem = (GridItem)getItem(position);
// use existing Views when we can
if(convertView == null) {
LayoutInflater inflater = (LayoutInflater)
getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
itemLayout = (RelativeLayout) inflater.inflate(R.layout.grid_item, null);
} else {
itemLayout = (RelativeLayout) convertView;
}
// get your bitmap or list item etc. here
BitmapDrawable bitmap = gridItem.getBitmap();
ImageView imageView = (ImageView)itemLayout.findViewById(R.id.image_view);
imageView.setBackground(bitmap);
if(gridItem.isSelected()) {
final int PADDING = 16;
Log.d(TAG, "position " + position + " is selected");
itemLayout.findViewById(R.id.image_frame).setPadding(PADDING, PADDING, PADDING, PADDING);
itemLayout.findViewById(R.id.custom_check_box)
.setBackgroundResource(R.drawable.custom_check_box_selected);
} else {
Log.d(TAG, "postion " + position + " is NOT selected");
itemLayout.findViewById(R.id.image_frame).setPadding(0,0,0,0);
itemLayout.findViewById(R.id.custom_check_box)
.setBackgroundResource(R.drawable.custom_check_box_unselected);
}
return itemLayout;
}
Here's the core of the GridItem class, left out the getters & setters.
public class GridItem {
private BitmapDrawable bitmap = null;
private boolean isSelected = false;
public GridItem(BitmapDrawable bitmap) {
this.bitmap = bitmap;
}
}
That's it for the Java. Now for some xml
grid_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
>
<FrameLayout
android:layout_width="140dp"
android:layout_height="140dp"
android:id="@+id/image_frame"
>
<!-- view for the main image -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="center"
android:background="@android:color/white"
android:id="@+id/image_view"
/>
</FrameLayout>
<!-- view for the 'checkbox' in upper left corner -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:src="@drawable/custom_check_box_unselected"
android:id="@+id/custom_check_box"
/>
</RelativeLayout>
And this would go in content_main.xml or activity_main.xml, etc.
<GridView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="2"
android:verticalSpacing="10dp"
android:horizontalSpacing="20dp"
android:stretchMode="columnWidth"
android:gravity="center"
android:id="@+id/grid_view"
>
</GridView>
And now 2 files for your drawable folder.
custom_check_box_unselected.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="20dp"
android:width="20dp"
android:viewportWidth="400"
android:viewportHeight="400">
<!-- the outside box -->
<!-- top line & top left corner -->
<path android:pathData="M 340 30 H 62 c -20 0 -35 15 -35 35 "
android:strokeColor="#000000" android:strokeWidth="20" />
<!-- left line & bottom left corner -->
<path android:pathData="M 27 64 v271 c0 20 15 35 35 35 "
android:strokeColor="#000000" android:strokeWidth="20" />
<!-- bottom line & bottom right corner -->
<path android:pathData="M 60 370 h275 c20 0 35 -15 35 -35"
android:strokeColor="#000000" android:strokeWidth="20" />
<!-- right line & top right corner -->
<path android:pathData="M 370 336 v -271 c0 -20 -15 -35 -35 -35"
android:strokeColor="#000000" android:strokeWidth="20" />
</vector>
custom_check_box_selected.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="20dp"
android:width="20dp"
android:viewportWidth="400"
android:viewportHeight="400">
<!-- the outside box -->
<path android:pathData="M 340 30 H 62 c -20 0 -35 15 -35 35
v271 c0 20 15 35 35 35
h275 c20 0 35 -15 35 -35
v -271 c0 -20 -15 -35 -35 -35 "
android:fillColor="#FDD835" android:strokeColor="#000000" android:strokeWidth="20" />
<!-- the check mark -->
<path android:pathData="M 140 320 l -100 -100 25 -30
l 75 75 l 190 -190
l 25 30 l -190 190"
android:fillColor="#000000" android:strokeColor="#000000" android:strokeWidth="2" />
</vector>