Android ListView selected item stay highlighted - java

I have an XML with two ListView, one with a list of clients filled by a select query (lv_cli) and the other with the details of the client selected (lv_cli_det).
I would like to keep the client selected in the lv_cli while the lv_cli_det show the details.
XML:
<ListView
android:id="#+id/cli_lista"
android:layout_width="512dp"
android:layout_height="wrap_content"
android:fadeScrollbars="false"
>
</ListView>
<ListView
android:id="#+id/cli_lista_det"
android:layout_width="512dp"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/cli_lista"
android:fadeScrollbars="false" >
</ListView>
Java:
Cursor cursor = db.rawQuery("Select NrCl||';'||Nome From Clientes", null);
final ListView t = (ListView)findViewById(R.id.cli_lista);
ArrayAdapter<String> myarrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, listItems);
t.setAdapter(myarrayAdapter);
final ListView td = (ListView)findViewById(R.id.cli_lista_detalhe);
final ArrayAdapter<String> myarrayAdapter2 = new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, listItems2);
t.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String item = ((TextView)view).getText().toString();
String[] strArray = item.split("\\;");
cli.load(strArray[0].toString());
td.setAdapter(myarrayAdapter2);
listItems2.clear();
listItems2.add("Nome: " + cli.getNome());
listItems2.add("Morada: " + cli.getMorada());
listItems2.add("Localidade: " + cli.getLoca());
listItems2.add("Código Postal: " + cli.getCp());
listItems2.add("Pais: " + cli.getPais());
listItems2.add("Nif: " + cli.getNif());
listItems2.add("Tel: " + cli.getTel());
listItems2.add("Tlm: " + cli.getTlm());
listItems2.add("Tipo Preço: " + cli.getTipoPvn());
listItems2.add("Cond. Pagamento: " + cli.getCpg());
listItems2.add("Obs: " + cli.getObs());
td.setAdapter(myarrayAdapter2);
myarrayAdapter2.notifyDataSetChanged();
}
});

I found the proper way. It's very simple.
In resource describe following:
android:choiceMode="singleChoice"
android:listSelector="#666666"
(or you may specify a resource link instead of color value)
Programmatical:
listView.setSelector(Drawable selector);
listView.setSelector(int resourceId);
listView.setChoiceMode(int mode);
mode can be one of these: AbsListView.CHOICE_MODE_SINGLE, AbsListView.CHOICE_MODE_MULTIPLE, AbsListView.CHOICE_MODE_NONE (default)
(AbsListView is the abstract ancestor for the ListView class)
P.S. manipulations with onItemClick and changing view background are bankrupt, because a view itself is a temporary object. Hence you must not to track a view.
If our list is long enough, the views associated with scrolled out items will be removed from hierarchy, and will be recreated when those items will shown again (with cached display options, such as background). So, the view we have tracked is now not an actual view of the item, and changing its background does nothing to the actual item view. As a result we have multiple items selected.

To hold the color of listview item when you press it, include the following line in your listview item layout:
android:background="#drawable/bg_key"
Then define bg_key.xml in drawable folder like this:
<?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>
Finally, include this in your ListView onClickListener:
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,long arg3) {
view.setSelected(true);
... // Anything
}
});
This way, only one item will be color-selected at any time. You can define your color values in res/values/colors.xml with something like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="pressed_color">#4d90fe</color>
<color name="default_color">#ffffff</color>
</resources>

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
for (int j = 0; j < adapterView.getChildCount(); j++)
adapterView.getChildAt(j).setBackgroundColor(Color.TRANSPARENT);
// change the background color of the selected element
view.setBackgroundColor(Color.LTGRAY);
});
Perhaps you might want to save the current selected element in a global variable using the index i.

Simplistic way is,if you are using listview in a xml,use this attributes on your listview,
android:choiceMode="singleChoice"
android:listSelector="#your color code"
if not using xml,by programatically
listview.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
listview.setSelector(android.R.color.holo_blue_light);

You need selector like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- State when a row is being pressed, but hasn't yet been activated (finger down) -->
<item android:drawable="#color/app_primary_color_light" android:state_pressed="true" />
<!-- Used when the view is "activated". -->
<item android:drawable="#color/app_primary_color" android:state_activated="true" />
<!-- Default, "just hangin' out" state. -->
<item android:drawable="#android:color/transparent" /></selector>
And then set android:choiceMode="singleChoice" to your ListView.

From Avinash Kumar Pankaj's example
View v;
then at oncreate method
v = new View(getActivity());
and then onlistitemclick method i wrote
public void onListItemClick(ListView listView, View view, int position,
long id) {
v.setBackgroundResource(0);
view.setBackgroundResource(R.color.green);
v = view;
}
It worked for me. Thank you.
I replaced
v = new View(getActivity());
to
v = new View(this);
and the code worked well.
It is necessary the xml files 'colors' and 'bg_key' from previous examples too, as well as ListView attribute android:background="#drawable/bg_key"
Mauro

*please be sure there is no Ripple at your root layout of list view container
add this line to your list view
android:listSelector="#drawable/background_listview"
here is the "background_listview.xml" file
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/white_background" android:state_pressed="true" />
<item android:drawable="#color/primary_color" android:state_focused="false" /></selector>
the colors that used in the background_listview.xml file :
<color name="primary_color">#cc7e00</color>
<color name="white_background">#ffffffff</color>
after these
(clicked item contain orange color until you click another item)

One way you can do this, is to Keep track of the current selected position in your activity:
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long arg3) {
currentPosition = position
lv_cli.notifyDataSetChanged();
}
Now, be sure you assign an ID to the parent layout (linearLayout, boxLayout, relativeLayout, .. Whatever you prefer) of your list item.
Then in your ListView you can do something Like this:
layoutBackground = (LinearLayout) convertView.findViewById(R.id.layout_background);
if (YourActivity.this.currentPosition == position) {
layoutBackground.setBackgroundColor(YourActivity.this.getResources().getColor(R.color.hilight_color));
} else{
layoutBackground.setBackgroundResource(R.drawable.list_item_drawable);
}
Basically, you just set the hilight color to the layout as a background when it equals your current selected position.
Notice how I set a drawable background resource when the item is not selected. This could be in your case different (since you posted no code). In my case, this drawable is a selector which makes sure the item is hi-lighted when pressed.
NOTE: This simple code doesn't use a view-holder, but I really
recommend using one.

Use the id instead:
This is the easiest method that can handle even if the list is long:
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
Holder holder=new Holder();
View rowView;
rowView = inflater.inflate(R.layout.list_item, null);
//Handle your items.
//StringHolder.mSelectedItem is a public static variable.
if(getItemId(position)==StringHolder.mSelectedItem){
rowView.setBackgroundColor(Color.LTGRAY);
}else{
rowView.setBackgroundColor(Color.TRANSPARENT);
}
return rowView;
}
And then in your onclicklistener:
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
StringHolder.mSelectedItem = catagoryAdapter.getItemId(i-1);
catagoryAdapter.notifyDataSetChanged();
.....

Related

Popup context menu on ListView hold

I have a code sample got from the internet and the code goes like this
ListView lv1;
lv1 = (ListView) findViewById(R.id.custom_list);
lv1.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
// print toast
}
});
Well I want to show context menu on list item click and I don't know how to do that.
You can use PopupMenu.
Create popup.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/menu_action1"
android:icon="#drawable/menu_action1"
android:title="#string/menu_action1" />
...
</menu>
and add this to onItemClick:
PopupMenu popup = new PopupMenu(this, v);
// this = your activity
popup.setOnMenuItemClickListener(this);
popup.inflate(R.menu.actions);
popup.show();
In addition your Activity should implements OnMenuItemClickListener so you can get menu click callback.

Android: Gridview Items and making changes to individual items

I'm building an app similar to instagram just for training purposes and basically I have a list of images that I populate through a grid view adapter. In my XML file for 'single_list_item' I have defined a button to be added along with the image for each single item on the grid view list.
However, I want to be able to click on these buttons to 'place a like' on these images separately (similar to instagram's like system). In other words: a user could place a like on the first image and not like the rest of the images. I display a like using a different image resource.
Images get populated by the grid view
Each image has a Like button beneath it
I click on a random like button belonging to a specific image
that image should be selected as 'liked'
What I tried so far:
My grid adapter:
public class ProductGridInflator extends ArrayAdapter<Product>{
.......
.......
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View child = convertView;
final RecordHolder holder;
LayoutInflater inflater = ((Activity) ctx).getLayoutInflater();
if (child == null) {
child = inflater.inflate(R.layout.grid_single, parent, false);
holder = new RecordHolder();
holder.productName = (TextView) child.findViewById(R.id.grid_text);
holder.image = (ImageView) child.findViewById(R.id.grid_image);
holder.heartButton = (ImageButton) child.findViewById(R.id.btnHeartLike);
holder.heartButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (v.isSelected()){
v.setSelected(false);
} else {
holder.heartButton.setSelected(false);
v.setSelected(true);
}
}
});
child.setTag(holder);
}else{
holder = (RecordHolder) child.getTag();
}
}
}
And I'm selecting the appropriate image from a selector (Liked or unliked)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/heart_clicked" android:state_selected="true"/>
<item android:drawable="#drawable/heart304"/>
</selector>
However this code is misbehaving when I test it. When I click on the like button of a specific image list item, two or three other list item's like buttons get clicked as well. Is there a better way to get this done? Individual buttons for each list item?
THe Problem is that you are reusing the onclicklisteners .. Remove this from your getView
holder.heartButton.setOnClickListener(new OnClickListener() {
And instead write in your activity
YouGridView.setOnItemClickListener
And add what you want to do when an item is clicked.

Android drawSelectorOnTop with GridView

I am developing a tabbed application in which one of the fragments, CollectionsFragment, contains a GridView with an ImageView in each slot. I would like the to use a selector to give feedback to users when the user clicks on one of the images.
I have successfully implemented the selector, however, my problem is that the selector is only drawing in the background of the image, but I would like to the selector to draw over the entire image. I have seen this problem referenced elsewhere, however, the solution selected by many, setting the drawSelectorOnTop property of the GridView, is not working for me.
The relevant fragment with the relevant adapter code:
public class CollectionsFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_collections, container, false);
// Grid view is inside the xml view inflated above
GridView gridView = (GridView)view.findViewById(R.id.gridview);
gridView.setDrawSelectorOnTop(true);
((GridView) gridView).setAdapter(new CustomGridViewAdapter(getActivity()));
return view;
}
private class CustomGridViewAdapter extends BaseAdapter {
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
View v = view;
ImageView picture;
TextView name;
if(v == null) {
v = inflater.inflate(R.layout.collections_item, viewGroup, false);
v.setTag(R.id.picture, v.findViewById(R.id.picture));
v.setTag(R.id.text, v.findViewById(R.id.text));
}
picture = (ImageView)v.getTag(R.id.picture);
name = (TextView)v.getTag(R.id.text);
Item item = (Item)getItem(i);
name.setText(item.name);
picture.setImageResource(item.drawableId);
picture.setBackgroundResource(R.drawable.selector);
return v;
}
}
}
And my selector for completeness sake:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="#color/buttonhighlight"/> <!-- pressed state -->
<item android:state_focused="true"
android:drawable="#color/buttonhighlight"/> <!-- focused state -->
<item android:drawable="#android:color/transparent"/> <!-- default state -->
</selector>
Thanks for any help,
I think you are mistaken about setDrawSelectorOnTop(boolean). The selector drawable that is being referenced here is GridView's internal selector drawable.
Even in the simplest implementation of GridView, when a grid item is clicked, the blue border is drawn around it. This is because, by default, gridview's own selector is drawn behind the item. If you call setDrawSelectorOnTop(true), the selector (blue) will be drawn over the item.
But setDrawSelectorOnTop(boolean) has nothing to do with the selector you are setting in the adapter. Whether you pass true, or false, the ImageView's selector's behavior won't change.
Solution:
Instead of setting the selector on each ImageView inside the adapter, make the GridView use your selector drawable:
GridView gridView = (GridView)view.findViewById(R.id.gridview);
gridView.setDrawSelectorOnTop(true);
// Make GridView use your custom selector drawable
gridView.setSelector(getResources().getDrawable(R.drawable.selector));
Now, there's no need for:
picture.setBackgroundResource(R.drawable.selector);
Edit:
Although I don't recommend this (obvious overhead), it should work:
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
View v = view;
ImageView picture;
....
....
LayerDrawable ld = new LayerDrawable(new Drawable[]
// Drawable from item
{ getResources().getDrawable(item.drawableId),
// Selector
getResources().getDrawable(R.drawable.selector)});
// Set the LayerDrawable
picture.setImageDrawable(ld);
// Don't need this
// picture.setBackgroundResource(R.drawable.selector);
return v;
}
Try setting the xml attribute android:drawSelectorOnTop in your activity_collections.xml file.
See if placing gridView.setDrawSelectorOnTop(true); after gridView.setAdapter(); helps. Sometimes, the order matters (weird).
If all else fails, you may have to switch GridView to some other view where setDrawSelectorOnTop() is proven to work consistently.

ListView row buttons: How do I create a custom Adapter that connects a View.OnClickListener to a button on each row of a ListView?

I want my ListView to contain buttons, but setting the button's xml property, onClick="myFunction" and then placing a public void myFunction(android.view.View view) method in the activity causes an NoSuchMethodException (the stack trace is null) to be thrown, as although the onclick listener is there, it doesn't fire myFunction(...) and cause the activity to close.
How do I create a custom Adapter that connects a View.OnClickListener to a button on each row of a ListView?
My ListView is created as follows...
[activity.java content..]
public void myFunction(android.view.View view)
{
//Do stuff
}
[activity.xml content..]
<LinearLayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".FrmCustomerDetails" >
<ListView android:id="#+id/LstCustomerDetailsList" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:clickable="true" android:clipChildren="true" android:divider="#null" android:dividerHeight="0dp" android:fastScrollEnabled="true" android:footerDividersEnabled="false" android:headerDividersEnabled="false" android:requiresFadingEdge="vertical" android:smoothScrollbar="true" />
</LinearLayout>
[activity_row_item.xml content..]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="#+id/Llt" android:layout_width="match_parent" android:layout_height="match_parent" >
<Button android:id="#+id/Btn" android:text="Click me" android:onClick="myFunction" />
</LinearLayout>
Here is how to create the custom Adapter, connecting View.OnClickListener to a ListView with a button per row...
1. Create a layout for a typical row
In this case, the row is composed of three view components:
name (EditText)
value (EditText:inputType="numberDecimal")
delete (Button)
Xml
pay_list_item.xml layout is as follows:
<?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" >
<EditText
android:id="#+id/pay_name"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="2"
android:hint="Name" />
<EditText
android:id="#+id/pay_value"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:inputType="numberDecimal"
android:text="0.0" />
<Button
android:id="#+id/pay_removePay"
android:layout_width="100dp"
android:layout_height="fill_parent"
android:text="Remove Pay"
android:onClick="removePayOnClickHandler" />
</LinearLayout>
Note: the button has onClick handler defined in xml layout file, because we want to refer its action to a specific list item.
Doing this means that the handler will be implemented in Activity file and each button will know which list item it belongs to.
2. Create list item adapter
This is the java class that is the controller for pay_list_item.xml.
It keeps references for all of its views, and it also puts these references in tags, extending the ArrayAdapter interface.
The Adapter:
public class PayListAdapter extends ArrayAdapter<Payment> {
private List<Payment> items;
private int layoutResourceId;
private Context context;
public PayListAdapter(Context context, int layoutResourceId, List<Payment> items) {
super(context, layoutResourceId, items);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
PaymentHolder holder = null;
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new PaymentHolder();
holder.Payment = items.get(position);
holder.removePaymentButton = (ImageButton)row.findViewById(R.id.pay_removePay);
holder.removePaymentButton.setTag(holder.Payment);
holder.name = (TextView)row.findViewById(R.id.pay_name);
holder.value = (TextView)row.findViewById(R.id.pay_value);
row.setTag(holder);
setupItem(holder);
return row;
}
private void setupItem(PaymentHolder holder) {
holder.name.setText(holder.Payment.getName());
holder.value.setText(String.valueOf(holder.Payment.getValue()));
}
public static class PaymentHolder {
Payment Payment;
TextView name;
TextView value;
ImageButton removePaymentButton;
}
}
Here we list the Payment class items.
There are three most important elements here:
PayListAdapter constructor: sets some private fields and calls superclass constructor. It also gets the List of Payment objects. Its implementation is obligatory.
PaymentHolder: static class that holds references to all views that I have to set in this list item. I also keep the Payment object that references to this particular item in list. I set it as tag for ImageButton, that will help me to find the Payment item on list, that user wanted to remove
Overriden getView method: called by superclass. Its goal is to return the single List row. We create its fields and setup their values and store them in static holder. Holder then is put in row’s tag element. Note that there is a performance issue, as the row is being recreated each time it is displayed. I used to add some flag in holder like isCreated, and set it to true after row was already created. then you can add if statement and read tag’s holder instead of creating it from scratch.
Payment.java is quite simple as for now and it looks a bit like BasicNameValuePair:
public class Payment implements Serializable {
private String name = "";
private double value = 0;
public Payment(String name, double value) {
this.setName(name);
this.setValue(value);
}
...
}
There are additional gets and sets for each private field not shown.
3. Add ListView to the activity layout xml file
In its simpliest form, it will be enough to add this view to activity layout:
<ListView
android:id="#+id/EnterPays_PaysList"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
4. Set up adapter to this list view in Activity Java code
In order to display items in ListView you need to set up its adapter and map it to some other ArrayList of Payment objects (as I am extending an Array adapter here). Here is code that is responsible for binding adapter to editPersonData.getPayments() ArrayList:
PayListAdapter adapter = new PayListAdapter(AddNewPerson.this, R.layout.pay_list_item, editPersonData.getPayments());
ListView PaysListView = (ListView)findViewById(R.id.EnterPays_PaysList);
PaysListView.setAdapter(adapter);
5. Adding / removing items to ListView (and its adapter)
Adapter is handled just like any other ArrayList, so adding new element to it is as simple as:
Payment testPayment = new Payment("Test", 13);
adapter.add(testPayment);
adapter.remove(testPayment);
6. Handle Remove Payment button click event
In an activity’s code, where ListView is displayed, add public method that will handle remove button click action. The method name has to be exactly the same as it was in pay_list_item.xml:
android:onClick="removePayOnClickHandler"
The method body is as follows:
public void removePayOnClickHandler(View v) {
Payment itemToRemove = (Payment)v.getTag();
adapter.remove(itemToRemove);
}
The Payment object was stored in ImageButton’s Tag element. Now it is enough to read it from Tag, and remove this item from the adapter.
7. Incorporate remove confirmation dialog window
Probably you need also make sure that user intentionally pressed the remove button by asking him additional question in confirmation dialog.
Dialogue
a) Create dialog’s id constant
This is simply dialog’s ID. it should be unique among any other dialog window that is handled by current activity. I set it like that:
protected static final int DIALOG_REMOVE_CALC = 1;
protected static final int DIALOG_REMOVE_PERSON = 2;
b) Build dialog
I use this method to build dialog window:
private Dialog createDialogRemoveConfirm(final int dialogRemove) {
return new AlertDialog.Builder(getApplicationContext())
.setIcon(R.drawable.trashbin_icon)
.setTitle(R.string.calculation_dialog_remove_text)
.setPositiveButton(R.string.calculation_dialog_button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
handleRemoveConfirm(dialogRemove);
}
})
.setNegativeButton(R.string.calculation_dialog_button_cancel, null)
.create();
}
AlertDialog builder pattern is utilized here. I do not handle NegativeButton click action – by default the dialog is just being hidden. If dialog’s confirm button is clicked, my handleRemoveConfirm callback is called and action is performed based on dialog’s ID:
protected void handleRemoveConfirm(int dialogType) {
if(dialogType == DIALOG_REMOVE_PERSON){
calc.removePerson();
}else if(dialogType == DIALOG_REMOVE_CALC){
removeCalc();
}
}
c) Show Dialog
I show dialog after my remove button click. The showDialog(int) is Android’s Activity’s method:
OnClickListener removeCalcButtonClickListener = new OnClickListener() {
public void onClick(View v) {
showDialog(DIALOG_REMOVE_CALC);
}
};
the showDialog(int) method calls onCreateDialog (also defined in Activity’s class). Override it and tell your app what to do if the showDialog was requested:
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_REMOVE_CALC:
return createDialogRemoveConfirm(DIALOG_REMOVE_CALC);
case DIALOG_REMOVE_PERSON:
return createDialogRemoveConfirm(DIALOG_REMOVE_PERSON);
}
}
Take a look at this blog post I wrote on exactly this matter:
Create custom ArrayAdapter
There are comments that explain every action I make in the adapter.
Here is the explanation in short:
So lets for example take a row where you want to place a CheckBox, ImageView
and a TextView while all of them are clickable. Meaning that you can click the
row it self for going to another Actvity for more details on the row, check its
CheckBox or press the ImageView to perform another operation.
So what you should do is:
1. First create an XML layout file for your ListView row:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/cbCheckListItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp" />
<TextView
android:id="#+id/tvItemTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="item string" />
<ImageView
android:id="#+id/iStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#drawable/ic_launcher"
android:src="#drawable/ic_launcher" />
</LinearLayout>
2. Second in your java code define a ViewHolder, a ViewHolder
is designed to hold the row views and that way operating more quickly:
static class ViewHolder
{
TextView title;
CheckBox checked;
ImageView changeRowStatus;
}
3. Now we have to define CustomArrayAdapter, using the array adapter
we can define precisely what is the desired output for each row based on the content of this
row or it’s position. We can do so by overriding the getView method:
private class CustomArrayAdapter extends ArrayAdapter<RowData>
{
private ArrayList<RowData> list;
//this custom adapter receives an ArrayList of RowData objects.
//RowData is my class that represents the data for a single row and could be anything.
public CustomArrayAdapter(Context context, int textViewResourceId, ArrayList<RowData> rowDataList)
{
//populate the local list with data.
super(context, textViewResourceId, rowDataList);
this.list = new ArrayList<RowData>();
this.list.addAll(rowDataList);
}
public View getView(final int position, View convertView, ViewGroup parent)
{
//creating the ViewHolder we defined earlier.
ViewHolder holder = new ViewHolder();)
//creating LayoutInflator for inflating the row layout.
LayoutInflater inflator = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//inflating the row layout we defined earlier.
convertView = inflator.inflate(R.layout.row_item_layout, null);
//setting the views into the ViewHolder.
holder.title = (TextView) convertView.findViewById(R.id.tvItemTitle);
holder.changeRowStatus = (ImageView) convertView.findViewById(R.id.iStatus);
holder.changeRowStatus.setTag(position);
//define an onClickListener for the ImageView.
holder.changeRowStatus.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Toast.makeText(activity, "Image from row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
holder.checked = (CheckBox) convertView.findViewById(R.id.cbCheckListItem);
holder.checked.setTag(position);
//define an onClickListener for the CheckBox.
holder.checked.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
//assign check-box state to the corresponding object in list.
CheckBox checkbox = (CheckBox) v;
rowDataList.get(position).setChecked(checkbox.isChecked());
Toast.makeText(activity, "CheckBox from row " + position + " was checked", Toast.LENGTH_LONG).show();
}
});
//setting data into the the ViewHolder.
holder.title.setText(RowData.getName());
holder.checked.setChecked(RowData.isChecked());
//return the row view.
return convertView;
}
}
4. Now you need to set this adapter, as the adapter of your ListView.
this ListView can be created in java or using an XML file, in this case I’m using a list that was
defined in the XML file using the “list” id:
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
ListView list = (ListView)findViewById(R.id.list);
CustomArrayAdapter dataAdapter = new CustomArrayAdapter(this, R.id.tvItemTitle, rowDataList);
list.setAdapter(dataAdapter);
}
5. Finally if we want to be able to press the row it self and not only a certain view in it
we should assign an onItemClickListener to the ListView:
list.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View view,int position, long id)
{
Toast.makeText(activity, "row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
First, the way of adding listeners in xml using onClick="function" is deprecated. You need a ViewHolder class to link the button in the xml to your java code. Then you can implement onClickListener for that.
Inside your getView() implementation of CustomAdapter, you can try like below.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.xxxxx, null);
holder = new ViewHolder();
holder.invite = (Button) convertView.findViewById(R.id.button);
} else {
holder = (ViewHolder) convertView.getTag();
}
final int pos = position;
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handleClick(pos);
}
});
}
class ViewHolder {
Button button;
}

Android listActivity onListItemClick with CheckBox

my question is how to access and change the checkBox mode for any item in a listactivity. I have an XML template file with a checkbox and a textview, and these define a row. Here's what I'm trying so far:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Toast.makeText(this, "You selected: " + Integer.toString(position), Toast.LENGTH_LONG).show();
CheckBox checkbox = (CheckBox) findViewById(R.id.checkbox);
if (checkbox.isChecked() == false) {
checkbox.setChecked(true);
} else {
checkbox.setChecked(false);
}
}
Obviously though using R.id.checkbox only toggles the first checkbox (actually, it does the first checkbox of whatever part of the list I'm looking at on my screen). I'm not sure what function to use to get the checkbox of any row though. The Toast works fine btw, so at least it registers position properly.
Thanks for any help.
EDIT - I'm now trying to subclass the SimpleCursorAdapter to better control the behaviour I want. Here is that subclass:
public class musicPlaylist extends SimpleCursorAdapter {
private Cursor c;
private Context context;
private ArrayList<String> checkList = new ArrayList<String>();
private final static int SELECTED = 1;
private final static int NOT_SELECTED = 0;
public musicPlaylist(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
this.c = c;
this.context = context;
}
public View getView(int pos, View inView, ViewGroup parent) {
View v = inView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.song_item, null);
}
this.c.moveToPosition(pos);
int columnIndex = this.c.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME);
String song = this.c.getString(columnIndex);
TextView sTitle = (TextView) v.findViewById(R.id.text1);
sTitle.setText(song);
v.setId(NOT_SELECTED);
v.setTag(song);
v.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v.getId() == NOT_SELECTED) {
v.setId(SELECTED);
Toast.makeText(context, "Test: " + v.getId(), Toast.LENGTH_SHORT).show();
v.setBackgroundColor(Color.parseColor("#FFFFFF"));
} else {
v.setId(NOT_SELECTED);
Toast.makeText(context, "Test: " + v.getId(), Toast.LENGTH_SHORT).show();
v.setBackgroundColor(Color.parseColor("#000000"));
}
}
});
return v;
}
}
And for reference, here is the XML of the ListActivity I'm making:
<?xml version="1.0" encoding="utf-8"?>
<ListView android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:drawSelectorOnTop="false"
android:fastScrollEnabled="true"
android:cacheColorHint="#00000000"
/>
<TextView android:id="#android:id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="No data"/>
Current behaviour: the list of songs from the SD card is made into a nice scrollable list. I do get somewhat proper responses from getView()'s onClick: The first time I click an item, it Toasts that its tag is "1" and its background goes white, while the second time I toast the same item, I get "0" and the background goes black, which is as expected.
The problem is if I select item1 (making its background white) and then scroll down, I'll notice that item11, item21, item31, ... , etc ALSO have white backgrounds. But when I click on them, their ID attribute goes to "1", meaning they've technically never been clicked before! So basically when the scroll "refreshes" to the next list of 10, it copies the color scheme of the first 10...?
Hope I explained it clearly.
I think this is bit deeper question and not direct answer is needed.
What do you want to achieve? Do you really want to make selected ONLY the checkboxes that you see on screen? Mind that this might be pretty random - list view only holds item views for the checkboxes that are visible on screen and they are reused for other items whenever the item is scrolled outside the screen.
I'd say that almost for sure you need to change state of all the checkboxes in your list (even those not visible) or some subset of them (like section). Which really translates into the proper way it should be done:
modify your data model appropriately
marking the appropriate flags selected in corresponding data model elements
(some boolean values you store per item)
call notifyDataSetChanged() on your adapter.
As a result, list view will recreate all the views which are visible on screen. Assuming that your "getView()" in adapter is written correctly, it will read the right model and update checked state on the item appropriately.
By notifyDataSetChanged - if you have 10 items visible on screen you will have 10 times getView() called for every item visible.
I recommend using android:choiceMode="multipleChoice" instead of manually manipulating your rows this way. The row widget will need to implement the Checkable interface, which can either be done by using CheckedTextView as the row itself, or creating a subclass of your desired container and implementing Checkable on it.
You use "global" view instead your row view. Try like that:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
CheckBox checkbox = (CheckBox) v.findViewById(R.id.checkbox);
if (checkbox.isChecked() == false) {
checkbox.setChecked(true);
} else {
checkbox.setChecked(false);
}
}

Categories