Maybe it's possible for ListView, but not very easily.. Anyway, if working with a RecyclerView from the v7 support package, there's a nice post here how to do what I want:
tracking-selected-item-in-recyclerview
SO answer leading there:
how-to-properly-highlight-selected-item-on-recyclerview
And just to save time to anyone trying to do this simple yet so complex tast of getting a sane list with selection working properly... so basic, here's my explanation:
Obtain the "support v7" package by using the SDK manager.
If working in Android Studio, adding the v7 jar to the project should be a breeze, with eclipse it's like walking on fire and drinking acid at the same time (no fun at all) - to make things short: copy the following jars from the sdk repository:
a. sdk/.../extra/...v4/android-support-v4.jar to your project under "libs",
b. Do the same for sdk/.../extra/...v7/..RecyclerView/recyclerview-v7-22.0.0.jar c. And sdk/.../extra/...v7/..CardView/cardview-v7-22.0.0.jar
d. Merge sdk/.../extra/...v7/..CardView/values.xml into your project's values.xml
Up till here - just to add the RecyclerView to your project ... hhhh .. so much work! ;) And I am saving you the hours of research...
Note: The layout editor won't recognise the RecyclerView, so you will have to add it manually (and it will continue to shout that this is a bad class... bla bla bla)
- Main Activity layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.listtester.MainActivity" >
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/up_button"
android:scrollbars="vertical" />
<Button
android:id="@+id/up_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/my_recycler_view"
android:layout_alignParentBottom="true"
android:text="Up" />
<Button
android:id="@+id/down_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@+id/up_button"
android:text="Down" />
</RelativeLayout>
- item layout:
<?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="?android:attr/listPreferredItemHeight"
android:padding="6dip"
android:background="@drawable/list_item_selector">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginRight="6dip"
android:contentDescription="TODO"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/secondLine"
android:layout_width="fill_parent"
android:layout_height="26dip"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_toRightOf="@id/icon"
android:ellipsize="marquee"
android:singleLine="true"
android:text="Description"
android:textSize="12sp" />
<TextView
android:id="@+id/firstLine"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@id/secondLine"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:layout_toRightOf="@id/icon"
android:gravity="center_vertical"
android:text="Example application"
android:textSize="16sp" />
</RelativeLayout>
- add colors.xml to res/values folder
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="pressed_color">#ffffff00</color>
<color name="default_color">#ffffffff</color>
</resources>
- add list_item_drawable.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/pressed_color"/>
<item
android:drawable="@color/default_color" />
</selector>
- MainActivity.java
package com.example.listtester;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity
{
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
public static class ViewHolder extends RecyclerView.ViewHolder
{
// each data item is just a string in this case
public TextView txtHeader;
public TextView txtFooter;
public ViewHolder( View v )
{
super( v );
txtHeader = (TextView)v.findViewById( R.id.firstLine );
txtFooter = (TextView)v.findViewById( R.id.secondLine );
}
}
@Override
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
mRecyclerView = (RecyclerView)findViewById( R.id.my_recycler_view );
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize( true );
// use a linear layout manager
mLayoutManager = new LinearLayoutManager( this );
mRecyclerView.setLayoutManager( mLayoutManager );
// specify an adapter (see also next example)
ArrayList<String> myDataset = new ArrayList<String>();
myDataset.add( "1234" );
myDataset.add( "adsf" );
myDataset.add( "2344" );
myDataset.add( "1234" );
myDataset.add( "32456344" );
myDataset.add( "12356564" );
myDataset.add( "1dfsdg234" );
myDataset.add( "1234" );
myDataset.add( "32456344" );
myDataset.add( "12356564" );
myDataset.add( "1dfsdg234" );
myDataset.add( "1234" );
myDataset.add( "32456344" );
myDataset.add( "12356564" );
myDataset.add( "1dfsdg234" );
mAdapter = new MyAdapter( myDataset );
mRecyclerView.setAdapter( mAdapter );
Button up = (Button)findViewById( R.id.up_button );
up.setOnClickListener( new OnClickListener()
{
@Override
public void onClick( View v )
{
mAdapter.scrollUp();
}
} );
Button down = (Button)findViewById( R.id.down_button );
down.setOnClickListener( new OnClickListener()
{
@Override
public void onClick( View v )
{
mAdapter.scrollDown();
}
} );
}
@Override
public boolean onCreateOptionsMenu( Menu menu )
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate( R.menu.main, menu );
return true;
}
@Override
public boolean onOptionsItemSelected( MenuItem item )
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if( id == R.id.action_settings )
{
return true;
}
return super.onOptionsItemSelected( item );
}
}
- MyAdapter.java
package com.example.listtester;
import java.util.ArrayList;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>
{
private ArrayList<String> mDataset;
private int mSelectedItem = 0;
private RecyclerView mRecyclerView;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder
{
// each data item is just a string in this case
public TextView txtHeader;
public TextView txtFooter;
public ViewHolder( View v )
{
super( v );
txtHeader = (TextView)v.findViewById( R.id.firstLine );
txtFooter = (TextView)v.findViewById( R.id.secondLine );
// Handle item click and set the selection
itemView.setClickable( true );
itemView.setOnClickListener( new View.OnClickListener()
{
@Override
public void onClick( View v )
{
// Redraw the old selection and the new
notifyItemChanged( mSelectedItem );
mSelectedItem = mRecyclerView.getChildAdapterPosition( v );
notifyItemChanged( mSelectedItem );
}
} );
}
}
public void scrollUp()
{
RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
tryMoveSelection( lm, -1 );
}
public void scrollDown()
{
RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
tryMoveSelection( lm, 1 );
}
@Override
public void onAttachedToRecyclerView( final RecyclerView recyclerView )
{
super.onAttachedToRecyclerView( recyclerView );
mRecyclerView = recyclerView;
}
private boolean tryMoveSelection( RecyclerView.LayoutManager lm, int direction )
{
int nextSelectItem = mSelectedItem + direction;
// If still within valid bounds, move the selection, notify to redraw,
// and scroll
if( nextSelectItem >= 0 && nextSelectItem < getItemCount() )
{
notifyItemChanged( mSelectedItem );
mSelectedItem = nextSelectItem;
notifyItemChanged( mSelectedItem );
lm.scrollToPosition( mSelectedItem );
return true;
}
return false;
}
public void add( int position, String item )
{
mDataset.add( position, item );
notifyItemInserted( position );
}
public void remove( String item )
{
int position = mDataset.indexOf( item );
mDataset.remove( position );
notifyItemRemoved( position );
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter( ArrayList<String> myDataset )
{
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
@Override
public MyAdapter.ViewHolder onCreateViewHolder( ViewGroup parent,
int viewType )
{
// create a new view
View v = LayoutInflater.from( parent.getContext() ).inflate(
R.layout.item_layout, parent, false );
// set the view's size, margins, paddings and layout parameters
ViewHolder vh = new ViewHolder( v );
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder( ViewHolder holder, int position )
{
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get( position );
holder.txtHeader.setText( mDataset.get( position ) );
// holder.txtHeader.setOnClickListener( new OnClickListener()
// {
// @Override
// public void onClick( View v )
// {
// remove( name );
// }
// } );
holder.txtFooter.setText( "Footer: " + mDataset.get( position ) );
holder.itemView.setSelected( mSelectedItem == position );
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount()
{
return mDataset.size();
}
}
I had gone to such a long and frustrating jurney just to achieve this very basic selection thingy (not to mention adding the recyclerview to the project...) Hope it helps someone.