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
Related
This question already has answers here:
How to customize a Spinner in Android
(6 answers)
Closed 5 years ago.
I'm trying to remove the default highlight effect for Spinner item's onClick. I have my own specific spinner background which have radius in its corners. When I applied the new background, it still has its default background under my custom background. I've tried styling like this:
<style name="ExternalPSpinnerTheme" parent="Widget.AppCompat.Spinner.DropDown">
<item name="android:listSelector">#color/transparent</item>
<item name="android:listChoiceBackgroundIndicator">#color/transparent</item>
<item name="android:listChoiceIndicatorSingle">#color/transparent</item>
<item name="android:selectableItemBackground">#color/transparent</item>
<item name="android:background">#drawable/spinner_sortfriend_background</item>
<item name="android:cacheColorHint">#android:color/transparent</item>
<item name="android:popupBackground">#drawable/spinner_sortfriend_background</item>
<item name="android:dropDownSelector">#color/transparent</item>
</style>
I've tried changing the parent to Widget.AppCompat.Spinner but it didn't work.. PopupBackground and background works tho.
The other question is questioning on how to hide dividers and add its own spinner custom implementation, mine is asking about how to remove the default onClick highlight effect on the background. How did I try to achieve it? This is how:
if (position > 0 &&
(position + 1) < mDataset.size()){
divider.setVisibility(View.VISIBLE);
convertView.setBackground(ContextCompat.getDrawable(context, R.drawable.spinner_dropdown_background_middle_white_to_migrey));
} else if (position == 0){
divider.setVisibility(View.VISIBLE);
convertView.setBackground(ContextCompat.getDrawable(context, R.drawable.spinner_dropdown_background_top_white_to_migrey));
} else if (position + 1 == mDataset.size()){
divider.setVisibility(View.GONE);
convertView.setBackground(ContextCompat.getDrawable(context, R.drawable.spinner_dropdown_background_bottom_white_to_migrey));
}
And I still didn't get the right answer, I even tried the solution in the other question. I just realized something tho, only the background on the first item overlaps with my custom background click effect, but the one at the end of the list doesn't get overlapped.. Weird..
Any solution?
Create custom Spinner Layout in your xml as follows
<RelativeLayout
android:id="#+id/relativeLayout_multiChoiceDropDown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/drawable_border_profile">
<TextView
android:id="#+id/textView_multiChoiceDropDownText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginBottom="#dimen/margin_5dp"
android:layout_marginEnd="#dimen/margin_5dp"
android:layout_marginStart="#dimen/margin_5dp"
android:layout_marginTop="#dimen/margin_5dp"
android:layout_toStartOf="#+id/button_multiChoiceDropDownArrow"
android:padding="#dimen/margin_5dp"
android:text="Select Item"
android:textColor="#color/black"
android:textSize="#dimen/text_size_16"/>
<Button
android:id="#+id/button_multiChoiceDropDownArrow"
android:layout_width="#dimen/profile_multi_choice_width"
android:layout_height="#dimen/profile_multi_choice_height"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_margin="#dimen/margin_5dp"
android:background="#drawable/dropdownarrow"/>
</RelativeLayout>
drawable_border_profile.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#80FFFFFF"/>
<stroke
android:width="0.5dp"
android:color="#color/light_gray"/>
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp"/>
</shape>
Then Set OnClickListener on RelativeLayout. On that You can open Popup Window
LayoutInflater inflater = (LayoutInflater)parentActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.pop_up_window, null);
RelativeLayout layout1 = holder.relativeLayout_multiChoiceDropDown;
pw = new PopupWindow(layout, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
pw.setBackgroundDrawable(new BitmapDrawable());
pw.setTouchable(true);
pw.setOutsideTouchable(true);
pw.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
pw.setTouchInterceptor(new View.OnTouchListener()
{
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_OUTSIDE)
{
pw.dismiss();
return true;
}
return false;
}
});
pw.setContentView(layout);
pw.showAsDropDown(layout1, -5, 0);
final ListView list = (ListView) layout.findViewById(R.id.dropDownList);
Adapter_DropDown adapter = new Adapter_DropDown(parentActivity, items);
list.setAdapter(adapter);
pop_up_window.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="#+id/PopUpView"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/qatool_basic_info_dropdown"
android:orientation="vertical">
<ListView
android:id="#+id/dropDownList"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="#000"
android:dividerHeight="0.5dp">
</ListView>
You can use customArray Adapter
public class Adapter_Spinner extends ArrayAdapter
{
Context context;
List<Response_IdDesc> dataArray;
public Adapter_Spinner(Context context, int textViewResourceId, List<Response_IdDesc> dataArray)
{
super(context, textViewResourceId, dataArray);
this.dataArray = dataArray;
this.context = context;
}
#Override public Response_IdDesc getItem(int position)
{
return dataArray.get(position);
}
#Override public int getCount()
{
return dataArray.size();
}
#Override public int getPosition(Object item)
{
return dataArray.indexOf(item);
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent)
{
return getCustomView(position, convertView, parent);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
return getCustomView(position, convertView, parent);
}
public View getCustomView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.rowitem_spinner_profile, parent, false);
TextView label = (TextView) view.findViewById(R.id.textview_spinner);
label.setText(dataArray.get(position).DESC);
return view;
}
}
Then set your spinner adapter like this
Adapter_Spinner adapter_spinner = new Adapter_Spinner(parentActivity, R.layout.rowitem_spinner, list);
spinner.setAdapter(adapter_spinner);
What i have tried :
listview_selector_focussed.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#008000"
android:endColor="#00FF00"
android:angle="90" />
</shape>
listview_selector_pressed.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#800000"
android:endColor="#FF0000"
android:angle="90" />
</shape>
listview_selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:state_focused="true"
android:drawable="#drawable/listview_selector_focused" />
<item
android:state_pressed="true"
android:drawable="#drawable/listview_selector_pressed" />
</selector>
listview layout :
<ListView
android:listSelector="#color/listview_selector"
/>
I have tried this much.. but un-fortunately this thing wont work
I want to change the listview row color when i click and when i click on another row previous row must be deselected and regain original state
Adapter class :
public class ListViewAdapter extends BaseAdapter {
Context ctx;
ArrayList<HashMap<String, String>> arraylist;
LayoutInflater inflater;
TextView tvA, tvB;
String a, b;
String out;
ListViewAdapter(Context ctx, ArrayList<HashMap<String, String>> arraylist) {
this.ctx = ctx;
this.arraylist = arraylist;
}
#Override
public int getCount() {
return arraylist.size();
}
#Override
public Object getItem(int position) {
return arraylist.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
inflater = (LayoutInflater) ctx
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemView = inflater.inflate(R.layout.listitem, parent, false);
tvA = (TextView) itemView.findViewById(R.id.tvA);
tvB = (TextView) itemView.findViewById(R.id.tvB);
tvA.setText(arraylist.get(position).get("a"));
a = arraylist.get(position).get("a");
b = arraylist.get(position).get("b");
return itemView;
}
listview adapter layout :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:orientation="horizontal">
<TextView
android:padding="5dp"
android:id="#+id/tvA"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minLines="2"
android:gravity="center_vertical"
android:text="Large Text"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#0052a5"
android:textSize="#dimen/font_large" />
<TextView
android:padding="5dp"
android:id="#+id/tvB"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="bottom"
android:text="Small Text"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#0052a5" />
</LinearLayout>
a good answer might be this one
You can set the onItemClick listener on your listview and in your adapter's getView() style the line.
PS: you will need to import the line background layout inside the adapter and set his background :)
try to something like my code
public class ReportActivity extends ActionBarActivity {
ListView listview;
Context mContext;
DatabaseHandler dbHandler;
ArrayList<ReportModel> mlist;
ReportAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_report);
mContext=this;
listview=(ListView) findViewById(R.id.listView);
dbHandler=new DatabaseHandler(mContext);
mlist=dbHandler.getAllContacts();
Collections.reverse(mlist);
adapter = new ReportAdapter(mContext, R.layout.adapter_layout,
mlist);
// Binds the Adapter to the ListView
listview.setAdapter(adapter);
listview.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
// Capture ListView item click
listview.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
#Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
// Capture total checked items
final int checkedCount = listview.getCheckedItemCount();
// Set the CAB title according to total checked items
mode.setTitle(checkedCount + " Selected");
// Calls toggleSelection method from ListViewAdapter Class
adapter.toggleSelection(position);
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.delete:
// Calls getSelectedIds method from ListViewAdapter Class
SparseBooleanArray selected = adapter
.getSelectedIds();
// Captures all selected ids with a loop
for (int i = (selected.size() - 1); i >= 0; i--) {
if (selected.valueAt(i)) {
ReportModel selecteditem = adapter
.getItem(selected.keyAt(i));
// Remove selected items following the ids
adapter.remove(selecteditem);
dbHandler.deleteContact(selecteditem.getId());
}
}
// Close CAB
mode.finish();
return true;
default:
return false;
}
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
// TODO Auto-generated method stub
adapter.removeSelection();
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
});
}
}
}
create xml file activity_main.xml in menu folder........
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/delete"
android:title="delete"/>
</menu>
I need to disable the second element in my navigation drawer until a certain task is finished. Here is how I disable the second element:
nav_item_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/dark_green"
android:state_pressed="true" />
<item android:drawable="#android:color/transparent"
android:state_enabled="true" />
<item android:drawable="#android:color/darker_gray"
android:state_enabled="false" />
</selector>
nav_fragment.xml (wrapped in a DrawerLayout)
<ListView
android:id="#+id/left_drawer"
android:layout_width="#dimen/nav_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:cacheColorHint="#0000"
android:background="#drawable/gradient_radial_backround"
android:choiceMode="singleChoice"
android:divider="#color/green_light"
android:dividerHeight="1dp" />
nav_list_item.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/nav_list_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/nav_item_selector"
android:orientation="horizontal"
android:padding="#dimen/standard_padding">
<ImageView
android:id="#+id/nav_list_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#null"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/nav_list_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="sans-serif-light"
android:gravity="center_vertical"
android:minHeight="#dimen/listPreferredItemHeightSmall"
android:paddingLeft="#dimen/activity_horizontal_small_margin"
android:text="List Item"
android:textSize="#dimen/text_nav_list_entry"
android:textColor="#color/dark_green" />
</LinearLayout>
Activity:
public class NavItemAdapter extends ArrayAdapter<String> {
LayoutInflater inflater;
private String[] listItems;
public NavItemAdapter(Context context, int textViewResourceId, String[] listItems) {
super(context, textViewResourceId, listItems);
this.listItems = listItems;
inflater = LayoutInflater.from(context);
}
#Override
public boolean isEnabled(int position)
{
if(position == 1)
return false;
else
return true;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = new NavItemView(getContext());
NavItemView navItemView = (NavItemView) convertView;
switch (position) {
case 0:
navItemView.setUpItem(R.drawable.ic_nav_1_checked, listItems[position], R.drawable.ic_nav_1);
break;
case 1:
navItemView.setUpItem(R.drawable.ic_nav_2_checked, listItems[position], R.drawable.ic_nav_2);
break;
case 2:
navItemView.setUpItem(R.drawable.ic_nav_3_checked, listItems[position], R.drawable.ic_nav_3);
break;
case 3:
navItemView.setUpItem(R.drawable.ic_nav_4_checked, listItems[position], R.drawable.ic_nav_4);
break;
case 4:
navItemView.setUpItem(R.drawable.ic_nav_5_checked, listItems[position], R.drawable.ic_nav_5);
break;
case 5:
navItemView.setUpItem(R.drawable.ic_nav_6_checked, listItems[position], R.drawable.ic_nav_6);
break;
}
return convertView;
}
}
class NavItemView extends LinearLayout implements Checkable {
private View v;
private ImageView iconView;
private TextView textView;
private Boolean checked = false;
private int mImageChecked;
private int mImage;
public NavItemView(Context context) {
super(context);
LayoutInflater inflater = LayoutInflater.from(context);
v = inflater.inflate(R.layout.list_item_navdrawer, this, true);
assert v != null;
iconView = (ImageView) v.findViewById(R.id.nav_list_icon);
textView = (TextView) v.findViewById(R.id.nav_list_text);
}
#Override
public boolean isChecked() {
return checked;
}
#Override
public void setChecked(boolean checked) {
this.checked = checked;
if (isChecked()) {
setBackgroundColor(getResources().getColor(R.color.transparent));
textView.setTextColor(getResources().getColor(R.color.light_pink));
iconView.setImageResource(mImageChecked);
} else {
//Reset to default
setBackgroundColor(getResources().getColor(R.color.transparent));
textView.setTextColor(getResources().getColor(R.color.green_lighter));
iconView.setImageResource(mImage);
}
}
#Override
public void toggle() {
checked = !checked;
}
private void setUpItem(int imageChecked, String title, int image) {
mImageChecked = imageChecked;
mImage = image;
textView.setText(title);
iconView.setImageResource(image);
}
}
What happens is the item is disabled, but the selector does not kick in (it stays the same colour). Not only that but that item's divider disappears which is even worse! What is happening here?
I actually can't believe this but the only solution seems to be a bit of a weird hack. There are several threads on this question but the only solid answer I've found is from user Jason Lin located here.
It seems there were two pieces of the puzzle missing, the first is that returning false for the item I want to disable in isEnabled() merely makes the item unclickable and unfocusable. To actually disable it I need to perform the same check in getView() and call setEnabled(false) on the item...
if(position == 1)
convertView.setEnabled(false);
AND
Set android:duplicateParentState="true" on the linear layout of the row.
This was the most important part as obviously I tried to called setEnabled(false) in getView() first of all, but it took me all day to learn I need the combination of all 3 of these things - returning false in isEnabled(), setting enabled to false in getView(), and setting duplicateParentState to true :)
I am using Listview with custom Adapter and a selector which I have set as background of the listview row item parent layout. When I am not setting OnClick listener to the parent layout of the listview item, the selector is working fine and the row gets selected.
But as soon as I am setting OnClick listener to the parent layout of the listview item , the selector is not working. I don't want to use onItemClickListener as I have to take care of certain things from the ListView Adapter.
I have been searching for this for hours but didn't got any clue.
Please help.
Thanks
My Custom Adapter class
public class CustomAdapter extends BaseAdapter {
private final String[] mNotificationList;
private static LayoutInflater inflater = null;
private Activity mActivity;
public CustomAdapter(Activity activity,String[] data) {
mActivity = activity;
mNotificationList = data;
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
if(mNotificationList == null){
return 0;
}
else{
return mNotificationList.length;
}
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int id) {
return id;
}
#Override
public View getView(final int position, View converterView, ViewGroup parent) {
if (converterView == null){
converterView = inflater.inflate(R.layout.notification_list_row, null);
}
NotificationListHolder listRowHolder = (NotificationListHolder)converterView.getTag();
if(listRowHolder==null) {
listRowHolder = new NotificationListHolder();
initializeView(listRowHolder,converterView);
}
bindView(listRowHolder,mNotificationList[position]);
return converterView;
}
private void bindView(NotificationListHolder listRowHolder, String notificationDetails) {
listRowHolder.articleName.setText(notificationDetails);
listRowHolder.parentLayout.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mActivity, "TOAST",Toast.LENGTH_SHORT).show();
}
});
}
private void initializeView(NotificationListHolder listRowHolder,View converterView) {
listRowHolder.parentLayout = (LinearLayout) converterView.findViewById(R.id.notificationlist_row_layout);
listRowHolder.articleName = (TextView) converterView.findViewById(R.id.article_text);
converterView.setTag(listRowHolder);
}
public static class NotificationListHolder{
public LinearLayout parentLayout;
public TextView articleName;
}
}
list row item xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/notificationlist_row_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/list_selector" >
<TextView
android:id="#+id/article_text"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_marginRight="40dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#android:color/black"
android:textStyle="bold" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_marginRight="40dp"
android:text="second view"
android:layout_below="#+id/article_text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#android:color/black"
android:textStyle="bold" />
</RelativeLayout>
list selector xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:drawable="#color/msg_read_bg" />
<item
android:state_pressed="true"
android:drawable="#color/msg_read_bg" />
<item
android:state_activated="true"
android:drawable="#color/msg_read_bg" />
<item
android:state_active="true"
android:drawable="#color/msg_read_bg" />
<item
android:state_focused="true"
android:drawable="#color/msg_read_bg" />
<item android:drawable="#android:color/white" />
</selector>
I'm trying to customize a ListView to have a default background image and highlighted background image for each single row.
However, the highlighted background image affects single rows, and the default background image affects the whole ListView; but I need it to affect each single row.
Could somebody tell me how to do this?
This is my code:
layout/main.xml:
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:dividerHeight="1dip"
android:listSelector="#drawable/bg_highlighted"
/>
drawable/selector.xml:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="#drawable/bg_default"/>
<item android:state_focused="true" android:drawable="#drawable/bg_default"/>
</selector>
src/main.java:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this,R.layout.list, mStrings); /*"Item1","Item2","Item3","Item4"*/
ListView lv = (ListView)findViewById(R.id.list);
lv.setAdapter(adapter);
lv.setBackgroundResource(R.drawable.selector);
As default background and highlighted background I use png images.
This is what I have, but this is what I want.
I did like heepie advised above. And as I understand there is not a property that allow to set background drawable to single row of ListView directly. It's should be done by creating custom view in getView method of a custom adapter. This is my final code. May be it will be useful to someone.
layout/main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/my_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
layout/my_list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="left|center_vertical"
android:paddingLeft="6dip"
android:textColor="#FFFFFF"
android:background="#drawable/list_selector"
/>
drawable/list_selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/bg_highlighted" />
<item android:drawable="#drawable/bg_default" />
</selector>
Drawable png images:
drawable/bg_default.png:
drawable/bg_highlighted.png:
src/main.java:
public class main extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyAdapter adapter;
ArrayList<String> mStrings = new ArrayList<String>();
mStrings.add("Item 1");
mStrings.add("Item 2");
mStrings.add("Item 3");
mStrings.add("Item 4");
ListView lv = (ListView) findViewById(R.id.my_list);
adapter = new MyAdapter(this, mStrings);
lv.setAdapter(adapter);
}
}
src/MyAdapter.java:
public class MyAdapter extends BaseAdapter {
public Activity activity;
ArrayList<String> data = new ArrayList<String>();
private static LayoutInflater inflater = null;
public MyAdapter(Activity a, ArrayList<String> d) {
activity = a;
data = d;
inflater = LayoutInflater.from(activity);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = inflater.inflate(R.layout.my_list_item, null);
holder = new ViewHolder();
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//Touch on view handle
Log.d("", "Touched row "+position);
}
});
//customizing view
holder.textView = (TextView)convertView.findViewById(R.id.my_textview);
holder.textView.setText(data.get(position));
return convertView;
}
public static class ViewHolder {
public TextView textView;
}
#Override
public int getCount() {
return data.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
}
And this is a result:
Instead of using arrayadapter would probably be better using a new class extending baseadapter and changing the background of each row from the getview method
The problem I see is you're actually using the item selector as the entire ListView background here:
lv.setBackgroundResource(R.drawable.selector);
All you have to do is to set this selector as the background of the root view inside your layout/list.xml (the layout inflated for each item in the list)