Search code examples
androidlistviewhighlightlistadapterselected

Android ListView with ListAdapter, need to always have a selected entry


i have a ListView (lvProperties) whose adapter is a custom ListAdapter (not an ArrayAdapter). This adapter gets is data from an Instance variable of custom type Section (containing a few Strings, Integers, ArrayLists and methods).

I need this list to ALWAYS display a selection (there must be always a selected element, even on activity launch).

When using a simple ArrayAdapter in the past something like this would be enough:

lvProperties.requestFocus();
lvProperties.setSelection(0);

However, in this case it does not work at all. I have been looking around SO and the web and used:

lvProperties.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
lvProperties.setFocusable(true);            
lvProperties.setFocusableInTouchMode(true);
lvProperties.requestFocus();
lvProperties.setSelection(0);

And still nothing.

My ListAdapter:

    lvProperties.setAdapter(new ListAdapter() {
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                View v=convertView;

                if(v==null) {
                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    v=vi.inflate(R.layout.detail_row1, null);
                }

                TextView name=(TextView)v.findViewById(R.id.propName);
                TextView value=(TextView)v.findViewById(R.id.propValue);

                if(name!=null) {
                    name.setText(section.propNames.get(position));
                }
                if(value!=null) {
                value.setText(section.propValues.get(position));
                }

                return v;
            }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {}

        @Override
        public void registerDataSetObserver(DataSetObserver observer) {}

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }

        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public int getItemViewType(int position) {
            return 0;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public int getCount() {
            return section.propNames.size();
        }

        @Override
        public boolean isEnabled(int position) {
            return true;
        }

        @Override
        public boolean areAllItemsEnabled() {
            return true;
        }
    });

My unimplemented OnItemSelectedListener:

    lvProperties.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> arg0, View arg1,
                int arg2, long arg3) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onNothingSelected(AdapterView<?> arg0) {
            // TODO Auto-generated method stub

        }
    });

And my detail_row1.xml (I will be implementing to layouts for the rows next, but this is how it's right now:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:orientation="horizontal" 
  android:layout_height="wrap_content"
  android:padding="6dip"
  >

  <TextView
    android:id="@+id/propName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"   
    android:layout_weight="1"    
    android:textSize="16sp"
    android:textStyle="bold" 
    android:textColor="#000000"
    android:gravity="center_vertical|left"
    android:paddingTop="10sp"
    android:paddingBottom="10sp"
    android:paddingLeft="4sp"
     />

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="2"
    android:id="@+id/propValue"       
    android:textColor="#000000"
    android:textSize="16sp"
    android:textStyle="bold"
    android:paddingTop="10sp"
    android:paddingBottom="10sp"
    android:paddingRight="4sp"
    android:gravity="center_vertical|right"
    />  

</LinearLayout><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:orientation="horizontal" 
  android:layout_height="wrap_content"
  android:padding="6dip"
  >

  <TextView
    android:id="@+id/propName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"   
    android:layout_weight="1"    
    android:textSize="16sp"
    android:textStyle="bold" 
    android:textColor="#000000"
    android:gravity="center_vertical|left"
    android:paddingTop="10sp"
    android:paddingBottom="10sp"
    android:paddingLeft="4sp"
    />

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="2"
    android:id="@+id/propValue"       
    android:textColor="#000000"
    android:textSize="16sp"
    android:textStyle="bold"
    android:paddingTop="10sp"
    android:paddingBottom="10sp"
    android:paddingRight="4sp"
    android:gravity="center_vertical|right"
    />  

</LinearLayout>

any help / pointer to guide on how to accomplish this? I don't want to use a drawable, just the default selector for the Theme I'm using.

Again, there must always be one selected entry in the list.

I realize that by design this is not the intended behavior for lists BUT this is one of those cases where one size doesn't fit all


Solution

  • For all others interested, I just replicated what I needed with having to mess with ChoiceMode, requestFocus, setSelection, etc etc etc, works fine in API 8 upwards

    when app launches I want the first row to be highlighted already:

    selectedItem.setValue(0) // an inner class i have, could be a simple integer as well

    my Inner Class ArrayDapter (I need it to see the parent selectedItem all the time):

    note: i will be changed the hard coded color next, i just solved the problem i had:

    public class DetailsAdapter extends ArrayAdapter<Section> {
    
        public Section section;
        public View v;
        public Context c;
    
        public DetailsAdapter(Context context, int textViewResourceId,
                Section s) {
            super(context, textViewResourceId);
    
            this.section=s;
    
            this.c=context;
    
        }
    
        @Override
        public int getCount() {
            return section.propNames.size();
        }
    
        @Override
        public View getView(int pos, View convertView, ViewGroup parent){
            this.v = convertView;
    
            LayoutInflater vi = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            if(v==null) {     
                v=vi.inflate(R.layout.detail_row1, null); 
            }
    
            **LinearLayout llc=(LinearLayout) v.findViewById(R.id.detail_container);**
            TextView name=(TextView)v.findViewById(R.id.propName);
            TextView value=(TextView)v.findViewById(R.id.propValue);
            if(pos==selectedItem.value) {
               llc.setBackgroundColor(Color.LTGRAY);
            } else
            { llc.setBackgroundColor(Color.WHITE);}
            Log.e("Pos",""+pos);
    
            if(llc!=null) {
    
            }
    
            if(name!=null) {
    
                name.setText(section.namesAsArray()[pos]);
            }
            if(value!=null) {
                value.setText(section.valuesAsArray()[pos]);
            }
    
    
            return v;
    
        }
    }
    

    The trick so I could avoid android:state_xxxxxxx in my XML file was to wrap my custom row with a nested LinearLayout filling the outer one.

    the layout for the row:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:orientation="horizontal" 
    android:layout_height="wrap_content"
    android:id="@+id/detail_linearLayout"
    >
    
    <LinearLayout 
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:layout_weight="0"
       android:id="@+id/detail_container"
         android:padding="6dip"
       >
    
    <TextView 
    android:id="@+id/propName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"   
    android:layout_weight="1"    
    android:textSize="16sp"
    android:textStyle="bold" 
    android:textColor="#000000"
    android:gravity="center_vertical|left"
    android:paddingTop="10sp"
    android:paddingBottom="10sp"
    android:paddingLeft="4sp"
    />
    
    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="2"
    android:id="@+id/propValue"       
    android:textColor="#000000"
    android:textSize="16sp"
    android:textStyle="bold"
    android:paddingTop="10sp"
    android:paddingBottom="10sp"
    android:paddingRight="4sp"
    android:gravity="center_vertical|right"
    />  
    
    </LinearLayout>
    </LinearLayout>
    

    finally, on I set my OnItemClickListener:

        lvProperties.setOnItemClickListener(new OnItemClickListener() {
    
            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                    long arg3) {
    
                    **selectedItem.value=arg2;
                    lvProperties.invalidateViews();**
                            .
                                        .
                                        .
                         }});
    

    done and done!