I am new to android and I have just started programming a simple app to try different things out.
I was programming a ListView (and, in the same way a GridView) but there is something I got wrong. Each item is a couple of an image and a text field.
| img | __text__ |
I want to be able to choose any number of list items, keeping them enlightened for all the selection process, before passing the selected items to the next activity. If I want to de-select one of them, I simply have to re-click on the item to have the selection disappear. For this purpose I use a custom selector so that when the item is pressed it changes colours.
If the items are all contained in a screen, everything is ok. But as soon as they grow in number and recycling kicks in, the enlightening of selected items which get out of the screen is lost. I have debugged the state of items and those whose enlightening is lost are still correctly selected, so I think it’s just a problem on how the graphic reloads when an item is restored after it went out of the device screen.
Here’s the code of the activity layout:
<!-- items_selection.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/Background">
<ListView
android:id="@+id/item_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="@color/divider"
android:dividerHeight="3dp"
android:choiceMode="multipleChoice"
android:listSelector="@drawable/list_selector">
</ListView>
</LinearLayout>
This is the Row Item layout:
<!-- list_row.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_selector"
android:orientation="horizontal"
android:padding="5dip" >
<LinearLayout
android:id="@+id/item_list_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginRight="5dip"
android:padding="3dip" >
<ImageView
android:id="@+id/item_image"
android:layout_width="@dimen/img_side"
android:layout_height="@dimen/img_side" />
</LinearLayout>
<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/item_list_item"
android:layout_centerVertical="true"
android:textColor="@color/black"
android:textSize="@dimen/textnorm"
/>
</RelativeLayout>
This is the selector I used:
<!-- list_selector.xml -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="false"
android:state_pressed="false"
android:drawable="@drawable/rect" />
<item
android:state_pressed="true"
android:drawable="@drawable/rect_sel" />
<item
android:state_selected="true"
android:state_pressed="false"
android:drawable="@drawable/rect_sel" />
</selector>
<!-- rect.xml -->
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#D5DDE0"
android:centerColor="#e7e7e8"
android:endColor="#CFCFCF"
android:angle="270" />
</shape>
<!-- rect_sel.xml -->
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#78DDFF"
android:centerColor="#16cedb"
android:endColor="#09adb9"
android:angle="270" />
</shape>
This is the code of the Activity:
public class ItemSelection extends AppCompatActivity {
private int numitems;
private ListView listview;
private ArrayList<Item> items = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.items_selection);
numitems = 15;
build_list();
listview = (ListView) findViewById(R.id.item_list);
listview.setAdapter(new ListAdapter(this, items));
}
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.next_btn, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch(id){
case R.id.next_btn:
Intent intent = new Intent (this, nextActivity.class);
intent.putStringArrayListExtra("items", Chosen_Items());
startActivity(intent);
return true;
default:
Toast.makeText(getApplicationContext(), "ERROR", Toast.LENGTH_SHORT).show();
return super.onOptionsItemSelected(item);
}
}
private void build_list() {
//Populates the item list with more items than the screen can support.
}
private ArrayList<String> Chosen_Items(){
ArrayList<String> selitems = new ArrayList<>();
for (int i=0; i<numitems; i++){
if (items.get(i).isSelected()){
selitems.add(items.get(i).getName());
}
}
return selitems;
}
This is the code of the listAdapter:
public class ListAdapter extends BaseAdapter {
private ArrayList <Item> items;
private Activity sActivity;
public ListAdapter(Activity sActivity, ArrayList<Item> items) {
this.sActivity = sActivity;
this.items = items;
}
@Override
public int getCount() {
return items.size();
}
@Override
public Object getItem(int position) {
return items.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
if(view == null) {
LayoutInflater li = sActivity.getLayoutInflater();
view = li.inflate(R.layout.list_row, null);
holder = new ViewHolder();
holder.text = (TextView)view.findViewById(R.id.item_name);
holder.img = (ImageView)view.findViewById(R.id.item_image);
view.setTag(holder);
}
else {
holder = (ViewHolder)view.getTag();
}
holder.text.setText(items.get(position).getName());
holder.img.setImageResource(items.get(position).getImage());
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View viewitem) {
if (!viewitem.isSelected() && !items.get(position).isSelected()) {
viewitem.setSelected(true);
items.get(position).setSelected(true);
}
else {
viewitem.setSelected(false);
items.get(position).setSelected(false);
}
}
});
return view;
}
private static class ViewHolder{
public TextView text;
public ImageView img;
}
}
I have already tried to manually set the background color of the items re-entering the screen (by using
view.setBackgroundResource(R.drawable.rect_sel)
in the adapter, before the click handler) but the problem remains. Can anyone help me solving the problem?
~~~~~~~~~~~~~~~~~~~ SOLUTION ~~~~~~~~~~~~~~~~~~
It seems the selector doesn't follow the recycle of the items and their views.There has to be a better and more elegant solution taking advantage of a selector in this situation. But out of all the attempts i made, none has worked. This solution is the best workaround and does not use the selector.
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
if(view == null) {
LayoutInflater li = sActivity.getLayoutInflater();
view = li.inflate(R.layout.list_row, null);
holder = new ViewHolder();
holder.text = (TextView)view.findViewById(R.id.item_name);
holder.img = (ImageView)view.findViewById(R.id.item_image);
view.setTag(holder);
}
else {
holder = (ViewHolder)view.getTag();
}
holder.text.setText(items.get(position).getName());
holder.img.setImageResource(items.get(position).getImage());
if(items.get(position).isSelected()){
view.setBackgroundResource(R.drawable.rect_sel);
}else{
view.setBackgroundResource(R.drawable.rect);
}
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View viewitem) {
if (!viewitem.isSelected() && !items.get(position).isSelected()) {
viewitem.setBackgroundResource(R.drawable.rect_sel);
viewitem.setSelected(true);
items.get(position).setSelected(true);
}
else {
viewitem.setBackgroundResource(R.drawable.rect);
viewitem.setSelected(false);
items.get(position).setSelected(false);
}
}
});
return view;
}
private static class ViewHolder{
public TextView text;
public ImageView img;
}
While in the list_row.xml file, the following line can be just deleted:
android:background="@drawable/list_selector"
In your getView()
method just add this test:
if (items.get(position).isSelected()){
view.setBackgroundResource(R.drawable.rect_sel);
} else {
view.setBackgroundResource(R.drawable.rect);
}
Or just view.setSelected(items.get(position).isSelected());
. While you already have a selector for your list item.
You must define the current selection state of view inside getView method.
Add this line:
viewitem.setSelected(items.get(position).isSelected());
after viewholder has been created like:
holder.img.setImageResource(items.get(position).getImage());
viewitem.setSelected(items.get(position).isSelected());
I think you should set the id for your RelativeLayout then add it to ViewHolder
private static class ViewHolder{
public TextView text;
public ImageView img;
RelativeLayout rl;
}
After that you handle event when click RelativeLayout then change background for RelativeLayout
public View getView(...)
...
...
// you should update the state of relative layout first
if (items.get(position).isSelected()) {
holder.setBackgroundColor(Color.parseColor("#ffff00"));
}else{
holder.setBackgroundColor(Color.parseColor("#ff0000"));
}
holder.rl.setOnClickListener(new View.OnClickListener(){ //remmember it is rl.setOnClick... not view.setOnClick...
public void onClick(View v) {
if (!items.get(position).isSelected()) {
items.get(position).setSelected(true);
holder.setBackgroundColor(Color.parseColor("#ffff00"));
}else {
items.get(position).setSelected(false);
holder.setBackgroundColor(Color.parseColor("#ff0000"));
}
}
});
}
Suggestion
You should modify your row layout like this (I have removed LinearLayout but the new layout still good)
<!-- list_row.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_selector"
android:orientation="horizontal"
android:padding="5dip" >
<ImageView
android:id="@+id/item_image"
android:layout_marginRight="5dip"
android:padding="3dip"
android:layout_alignParentLeft="true"
android:layout_width="@dimen/img_side"
android:layout_height="@dimen/img_side" />
<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/item_image"
android:layout_centerVertical="true"
android:textColor="@color/black"
android:textSize="@dimen/textnorm"
/>
</RelativeLayout>
Remember that your list row layout is more simple then your listview will scroll faster, smooth and prevent some annoying bug.
Hope this help
来源:https://stackoverflow.com/questions/32838197/android-listview-gridview-item-selection-with-scrolling