I'm loading phone contacts in a custom ListView. Each row is a checkable LinearLayout containing a CheckedTextView and another TextView.
I'm feeding the list view with a custom ArrayAdapter. My problem is that I can't control CheckedTextViews inside getView(). For example when I try the following
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row == null){
row = inflater.inflate(layout, parent, false);
}
CheckedTextView checkedTextView = (CheckedTextView) row.findViewById(R.id.checkedTextView);
checkedTextView.setText("A");
checkedTextView.setChecked(true);
return row;
}
That's supposed to check every text view whenever I scroll the list view, but that's not happening. Can anybody tell me how to do it?
EDIT: It's important to check it inside getView(), I can't just check all after setListAdapter()
EDIT2: This is the xml file showing the view of each row
<?xml version="1.0" encoding="utf-8"?>
<com.example.multiplecontacts.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<CheckedTextView
android:id="#+id/checkedTextView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:paddingBottom="0dp"
android:text="CheckedTextView"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/subTextView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Small Text"
android:paddingTop="0dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
</com.example.multiplecontacts.CheckableLinearLayout>
CheckableLinearLayout is a custom layout that extends LinearLayout and implements Checkable as I said before. And I've taken it from here
Did you set a checkMark property for your CheckedTextView in your layout xml?
For example: android:checkMark="?android:attr/listChoiceIndicatorMultiple
CheckedTextView is not just a checkbox with a text. You must also note, that CheckedTextView is not focusable, or clickable without some manipulation (since it was designed for ListView and therefore it's state must be controlled by ListView's setOnItemClickListener)
setChoiceMode should be set for a ListView.
And checking of the row inside adapter's getView should be done via: listView.setItemChecked(position, value)
#Pier-Luc Gendreau and #fox mentioned the solution in comments but not in answer, so posting answer on their behalf.
As mentioned by #AlexOrlov, CheckedTextView is not focusable, or clickable without some manipulation. So what you have to do is set item check using ListView, you can either do it from the adapter itself or from activity/fragment where you have ListView.
To do it from adapter,
public View getView(int position, View convertView, ViewGroup parent)
{
if (//logic) {
((ListView) parent).setItemChecked(position, true);
}
}
From activity/fragment
if (//logic) {
listView.setItemChecked(position, true);
}
When ever you scroll list, every time it calls its getview() method. So, if you have any checkbox or any editbox in listcell it will reinitialize it.
My idea is to store the status (checked or unchecked) of checkbox. So here I used ArrayList first I filled it with false value then on its click event i used to store it actual status.
class MyAdapter extends BaseAdapter implements OnClickListener {
private ArrayList<Boolean> checks=new ArrayList<Boolean>();//Boolean type array to manage check box
private static LayoutInflater inflater=null;
public MyAdapter(Context context, ArrayList<HashMap<String, String>> d)
{
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//fill with false values
for (int i = 0; i < d.size(); i++)
{
checks.add(i, false);
}
}
public int getCount()
{
return data.size();
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.<your layout>, null);
//Checkbox is of button type----android:button="#drawable/btn_check"
//make a selector xml for checkbox
checkBox=(CheckBox)vi.findViewById(R.id.check_box);
checkBox.setTag(Integer.valueOf(position));
checkBox.setOnClickListener(this);
checkBox.setChecked(checks.get(position));
return vi;
}
#Override
public void onClick(View v)
{
int viewId=v.getId();
if(viewId== R.id.check_box)
{
Integer index = (Integer)v.getTag();
boolean state = checks.get(index.intValue());
checks.set(index.intValue(), !state);
}
}
}
Update: Solution 2nd: You can put a boolean variable in your ViewHolder class. This boolean variable will used to define wether item is selected or not.
Hope this should help you.
Related
I have followed the tutorial here to create a custom ListView that shows items with category headers. I have modified the list_item_entry.xml to put a CheckBox in the item:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize" >
<CheckBox
android:id="#+id/option_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="6dp"
android:focusable="false"
android:clickable="false" />
<TextView
android:id="#+id/list_item_entry_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
</LinearLayout>
My problem is that if I check some of the CheckBoxes then scroll them off the screen, when they come back they are unchecked. However listView.getCheckedItemPositions() still shows that the item is checked.
I'm pretty sure that my problem is with the getView() method in my custom ArrayAdapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Item i = items.get(position);
if (i != null) {
if(i.isSection()){
SectionItem si = (SectionItem)i;
convertView = vi.inflate(R.layout.list_item_section, parent, false);
convertView.setOnClickListener(null);
convertView.setOnLongClickListener(null);
convertView.setLongClickable(false);
final TextView sectionView =
(TextView) convertView.findViewById(R.id.list_item_section_text);
sectionView.setText(si.getTitle());
}else{
EntryItem ei = (EntryItem)i;
convertView = vi.inflate(R.layout. list_item_entry, parent, false);
final TextView title =
(TextView) convertView.findViewById(R.id.list_item_entry_title);
if (title != null)
title.setText(ei.getTitle());
}
}
return convertView;
}
I think that I have two issues here, though I have no idea how to solve either:
Using vi.inflate every time is causing android to constantly create views which is bad (not sure about this). I tried to only inflate it if convertView == null but then sometimes convertView would be in the wrong format, ie. List_item_section when it should be List_item_entry. Is it fine to inflate it everytime?
I think that inflating the view each time is causing the CheckBoxes to be reset, although I may be wrong about this.
So how do I make it so the CheckBoxes will stay checked when the leave and return to the screen? And will this method fill Android's memory with Views if the the list is sufficiently long?
Update:
I liked #user3815165's answer because I didn't need to store the checked value for a sectionItem which doesn't have a checkbox. But as I mentioned in a comment, since the items list is not in the context of the Activity then the values of whether each EntryItem is checked or not persists when the view is destroyed and creates bugs.
So I decided to go with #Palash's answer, even though it stored data not needed (only a single boolean value for each SectionItem in the list). It works perfectly.
you need to maintain a status array of type boolean in your activity, pass that array into your list adapter and while setting the checkbox check status of that position, also you need to update that status array likewise on click event of checkbox.
try this you will get the desired output.
//While Setting the checkbox in adapter
if(bStatus[position]==false)
{
itemSet.chSelectItem.setChecked(false);
}else if(bStatus[position]==true)
{
itemSet.chSelectItem.setChecked(true);
}
In your main Activity
//initilize Arraylist in main Activity
boolean[] bStatus;
bStatus = new boolean[BeanArray.size()];
Arrays.fill(bStatus, false);
MyAdapter adapter = new MyAdapter(this, BeanArray, bStatus);
listView.setAdapter(adapter);
class Item{
boolean isSection;
String title;
boolean isOptionChecbox;
//your getter/setter
#Override
public String toString() {
return title;
}
}
you Adapter:
public class listAdapter extends ArrayAdapter<Item> {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Item i = items.get(position);
if(i.isSection()){
convertView = vi.inflate(R.layout.list_item_section, parent, false);
convertView.setOnClickListener(null);
convertView.setOnLongClickListener(null);
convertView.setLongClickable(false);
final TextView sectionView = (TextView) convertView.findViewById(R.id.list_item_section_text);
sectionView.setText(si.getTitle());
} else{
convertView = vi.inflate(R.layout. list_item_entry, parent, false);
final TextView title = (TextView) convertView.findViewById(R.id.list_item_entry_title);
if (title != null) title.setText(ei.getTitle());
CheckBox optionCheckbox = (CheckBox) convertView.findViewById(R.id.option_checkbox);
optionCheckbox.setChecked(ei.isOptionCheckbox());
optionCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
item.setOptionCheckbox(b);
}
});
}
return convertView;
}
}
Is it possible to change the "style" of a list view once it's displayed? What I want to do is to start with a list view using simple_list_item_1 but switch to simple_list_item_multiple_choice after the list has been populated.
If this isn't possible, can someone please show me how to populate a list view with a text view and checkbox as I'm finding this very difficult to understand.
As always, thanks in advance.
if the two view "simple_list_item_multiple_choice" and "simple_list_item_1" are just different based on the checkbox, then you can just use the "simple_list_item_multiple_choice" where you can make the checkbox hidden on very startup and when the data is loaded you can make the checkbox visible. Or you can show a activity-indicator when the data is loading and after loading the data you can directly port the custom ListView, think this one is better. Some sample code is provided with explanation.
First of all a custom adapter for the listView. This adapter is for each item of the ListView.
//Custom Adapter
private class DataAdapter extends BaseAdapter {
#Override
public int getCount() {
if (channelListTitles.size()>0){
return dataArrayList.size(); //dataArrayList is a ArrayList<String>
}
else {
return 0;
}
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView==null){
convertView = getActivity().getLayoutInflater().inflate(R.layout.item_view,null); //item_view is the custom view for the items of the ListView
}
if (convertView != null) {
((TextView)convertView.findViewById(R.id.item_view_title)).setText(channelListTitles.get(position)); //item_view_title is the TextView
if(isDataLoaded){ //isDataLoaded is a bool value to indicate if data is loaded
CheckBox checkBox = (CheckBox)convertView.findViewById(R.id.checkbox);
checkBox.setVisibility(View.VISIBLE);
}
}
return convertView;
}
}
Now the item_view for each list-item:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/checkbox"
android:visibility="invisible"/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/item_view_title"
android:layout_gravity="center_vertical"/>
</LinearLayout>
Now finally declare a listView on which the upper custom view will be ported. Posting the whole code of the OnCreateView() for better understanding. Here I am using Fragment, you need to change the corresponding methods if you are using Activity:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
if (rootView != null) {
ListView listView = (ListView) rootView.findViewById(R.id.item_list);
DataAdapter itemDataAdapter = new DataAdapter();
listView.setAdapter(itemDataAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// OnItemClickListener goes here
}
});
}
return rootView;
}
Now the fragment_main:
<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"
tools:context=".MainActivity$PlaceholderFragment">
<ListView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/item_list"/>
</RelativeLayout>
The Adapter is an important factor when you want to load data on dynamically. Basically Adapter is the bridge between data and view. Once you have the data you can call the adapter to update the view by using itemDataAdapter.notifyDataSetChanged();.
Yes you can do that, You need to make a xml for the row. And make an adapter class and overwrite getView() and inflate the view here. I always extend BaseAdapter.
# adelphia, i thing you want to use custom listview. Follow this tutorials for custom list view (follow 72 - 84 tutorials).
I have tried to hide items in a custom list adapter. I can hide the visibility of the text but I cannot hide the whole list item. It still shows the dividers etc. I have tried:
tv.setVisibility(View.INVISIBLE);
tv.setVisibility(View.GONE);
convertView.setVisibility(View.INVISIBLE);
convertView.setVisibility(View.GONE);
When I use the convertView i get a null pointer exception.
You can set ContentView with No Element.
In getView() of Your Custom Adapter.
if(condition)
{
convertView=layoutInflater.inflate(R.layout.row_null,null);
return convertView;
}
else
{
convertView=layoutInflater.inflate(R.layout.row_content,null);
return convertView;
}
your XML row_null.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="wrap_content">
</LinearLayout>
You have 3 ways to do this:
Remove the items from your list inside or outside the adapter.
Inside the adapter you can do it in the Constructor.
private List<String> list;
public MyAdapter(Context context, List<String> list) {
list.remove(0);
list.remove(1);
list.remove(<whateverPositionYouLike>);
this.list = list;
}
You need to figure out how many items you want to hide and need to build a similar logic.
#Override
public int getCount() {
// In this adapter, we are supposed to hide the first item of the list view
// Returning 0 if no of items are already 0
if(list.size() <=1)
return 0;
// if number of items are more than one, returning one item less
return list.size() - 1;
}
#Override
public Object getItem(int position) {
// skipping the position
return list.get(position + 1);
}
`
#Override
public View getView(final int position, View v, ViewGroup arg2) {
// this is important, as we are supposed to skip the first item of listview
final int localPosition = position +1;
ViewHolderItem holder;
if (v == null) {
holder = new ViewHolderItem();
v = inflater.inflate(R.layout.list_row, null);
v.setTag(holder);
} else {
holder = (ViewHolderItem) v.getTag();
}
return v;
}
`
Answer provided by #hims_3009
You cannot in the way you are trying to, you'll need to use a custom Adapter and implement in there the logic for showing/not showing a line.
If you want to hide a row in a listview, you need to delete data in that position. For example if you use array adapter and want to hide the row on 5. position. You have to delete line from your array and the call notifyDatasetChanged() method.
(You can delete data from array by using tempArray)
or use this way
private List<String> items = new ArrayList<String>();
// make list a new array, clearing out all old values in it.
for(i=0; i<10; i++)
{
if((i != 5) && (i != 6)){
// if k = 5 or 6, dont add those items to the list
items.add(something[i]); /// whatever your list items are.
}
}
ArrayAdapter<String> itemList = new
ArrayAdapter<String>(this,R.layout.itemlistlayout);
setListAdapter(itemlist);
If you want to hide an entire item you need to build some logic into your getView() method to make it skip over various parts of your data.
lets say you have an ArrayAdapter and I want to hide the data that is at index 2. Your getView() method could look something like this.
#Override
public View getView(int pos, View convertView, ViewGroup, parent){
View mView = convertView;
//TODO: Check if convertView was null, and inflate
// or instantiate if needed.
//Now we are going to set the data
mTxt = mView.findViewById(R.id.mTxt);
if(pos >= 2){
//If position is 2 or above, we ignore it and take the next item.
mTxt.setText(this.getItem(pos + 1).toString());
}else{
//If position is below 2 then we take the current item.
mTxt.setText(this.getItem(pos).toString());
}
return mView;
}
Note that this example is generalized, it is not meant to be able to be dropped directly into your project. I had to make assumptions about some things which I don't know the truth on. You can use the same concept as I've shown here though and modify it to your situation to be able to "hide" rows in your ListView.
If you already have a custom list adapter you can just call notifyDataSetChanged() on the adapter and the adapter itself has of course to implement the logic to filter out the views for the rows you want to hide. These are the methods that come to my mind that need to reflect that logic:
#Override
public Object getItem(int position) {
return mItems.get(position);
}
#Override
public int getCount() {
return mItems.size();
}
#Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
// here goes your logic to hide a row
Additionally you might have to change getItemId() as well.
I think I have a much easier / safer solution: you just have to "embed" your item in a Layout, and change the visibility of this parent layout.
<?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="wrap_content"
android:orientation="horizontal" >
<!-- Embed ListView Item into a "parent" Layout -->
<LinearLayout
android:id="#+id/parentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- This is the normal content of your ListView Item -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="World" />
</LinearLayout>
</LinearLayout>
Then in your code just do:
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater li = mActivity.getLayoutInflater();
view = li.inflate(R.layout.my_listview_item, null);
}
LinearLayout parentLayout = (LinearLayout) view.findViewById(R.id.parentLayout);
if (shouldDisplayItem(position)) {
parentLayout.setVisibility(View.VISIBLE);
} else {
parentLayout.setVisibility(View.GONE);
}
return view;
}
This way you always use/reuse the same item, and just hide/show it.
I have a Gridview filled by an Adapter which returns LinearLayouts each contains an ImageButton and TextView.
In the adapter I am binding an onClick and onLongClick event to the ImageButton.
I am trying to bind OnItemClickListener to the gridview but I don't know why that the onItemclicked never fired up.
It's my 6th hour without anything.
By the way;
OnItemSelectListener working perfectly on the Grid.
I am checking if some piece of code accidentally handles the onItemClicked but couldn't catch yet.
I need help guys.
gridView = (GridView) layoutInflater.inflate(R.layout.gridview, null);
gridView.setOnItemClickListener(new ItemClickListener());
.
.
.
//inner handler class
class ItemClickListener implements AdapterView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(mainActivity.getApplicationContext(),view + " clicked at pos " +
i,Toast.LENGTH_SHORT).show();
}
}
Do not use clickable objects in the grid. In that case Android cannot handle the click event of GridView.
Instead, use something to show a similar user interface view. Then handle that object's click actions.
Don't: put Button in the GridView to perform some click actions.
Do: put an ImageView instead of ImageButton and handle ImageView's click events.
If you wants to use Button or ImageButton then you need to write these attributes in your xml code of the widgets.
android:focusable="false"
android:focusableInTouchMode="false"
Its works for me.
But in GridView, Try to avoid use of these widgets. You can use any other widgets in place of these (Like ImageView or any other).
Also make sure, that your ListAdpter returns true for
public boolean isEnabled(int _position)
for the position you want to click.
Hey guyz finally got a solution...
what we were doing is directly accessing the Layout inside the GridView, so the onItemClickListener finds it confusing to access the item.
So the solution is to apply the onClickListener inside the Adapter (i.e. normally ArrayAdapter)
so what i m trying to say is:
public View getView(int position, View convertView, ViewGroup parent) {
//Here row is a view and we can set OnClickListener on this
final View row;
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
//Here we inflate the layout to view (linear in my case)
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ViewHolder();
holder.imageTitle = (TextView) row.findViewById(R.id.text);
holder.image = (ImageView) row.findViewById(R.id.image);
row.setTag(holder);
} else {
row = convertView;
holder = (ViewHolder) row.getTag();
}
ImageItem item = data.get(position);
holder.imageTitle.setText(item.getTitle());
holder.image.setImageBitmap(item.getImage());
//Now get the id or whatever needed
row.setId(position);
// Now set the onClickListener
row.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(context, "Clicked" + row.getId() + "!!",
Toast.LENGTH_SHORT).show();
}
});
return row;
}
Try to set
android:clickable="false"
android:focusable="false"
I meet same problem too, because of several reasons.
So, here's my tips:
Extend BaseAdapter for your adapter;
Use OnClickListener inside the getView in adapter instead setting OnItemClickListener for GridView;
Avoid setting LayoutParams multiple times;
Check if position = 0, don't use convertView, inflate new View;
Set OnClickListener not only for parent View, but for any child View, if any;
Make all your Views clickable.
I just tested it on 4 devices, and this solution works as expected. Hope, it will help in your case.
Correct me, if I made something wrong.
Layout code XML:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#273238"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:padding="1dp">
<ImageView
android:id="#+id/open_image_item_imageview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/loh"
android:clickable="true"
android:focusable="false"
android:focusableInTouchMode="false"
android:scaleType="centerCrop"/>
<TextView
android:id="#+id/open_image_item_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="false"
android:focusableInTouchMode="false"
android:layout_gravity="bottom"
android:padding="4dp"
android:textSize="10sp"
android:ellipsize="start"
android:background="#55000000"
android:textColor="#FFFFFF"
android:text="image name"/>
</FrameLayout>
Adapter code Java:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = null;
if(convertView != null && position != 0)
view = convertView;
else{
view = LayoutInflater.from(getContext()).inflate(R.layout.open_image_item_layout, null, false);
view.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, size));
}
TextView textView = (TextView)view.findViewById(R.id.open_image_item_textview);
ImageView imageView = (ImageView)view.findViewById(R.id.open_image_item_imageview);
...
View.OnClickListener onClickListener = getOnClickListener(files[position]);
view.setOnClickListener(onClickListener);
textView.setOnClickListener(onClickListener);
imageView.setOnClickListener(onClickListener);
return view;
}
I've seen example com.example.android.apis.view.List11 from ApiDemos. In that example, each row takes the view android.R.simple_list_item_multiple_choice. Each such view has a TextView and a CheckBox.
Now I want each view to have 2 TextViews and 1 CheckBox, somewhat similar to the List3 example. I tried creating a custom layout file row.xml like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<CheckBox
android:id="#+id/checkbox"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="fill_parent" />
<TextView
android:id="#+id/text_name"
android:textSize="13px"
android:textStyle="bold"
android:layout_toLeftOf="#id/checkbox"
android:layout_alignParentLeft="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/text_phone"
android:textSize="9px"
android:layout_toLeftOf="#id/checkbox"
android:layout_below="#id/text_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
Then in Activity's onCreate(), I do like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Query the contacts
mCursor = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);
startManagingCursor(mCursor);
ListAdapter adapter = new SimpleCursorAdapter(this,
R.layout.row,
mCursor,
new String[] { Phones.NAME, Phones.NUMBER},
new int[] { R.id.text_name, R.id.text_phone });
setListAdapter(adapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
The result kind of looks like what I want, but it looks like the list doesn't know which item of it is selected. Also, I need to click exactly on the CheckBox. In the List11 example, I only need to click on the item row.
So what do I need to do to make a multiple choice list with my custom view for each row? Many thanks.
You have to make your own RelativeLayout that implements the Checkable interface and have a reference to the CheckBox or to the CheckedTextView (or a list if it's multiple choice mode).
Look at this post:
http://www.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/
The answer of Rahul Garg is good for the first time the list is loaded, if you want some rows to be checked depending on the model data, but after that you have to handle the check/uncheck events by yourself.
You can override the onListItemCLick() of the ListActivity to check/uncheck the rows
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
ViewGroup row = (ViewGroup)v;
CheckBox check = (CheckBox) row.findViewById(R.id.checkbox);
check.toggle();
}
If you do so, do not set the ListView to CHOICE_MODE_MULTIPLE, because it makes strange things when calling the function.
To retrieve the list of checked rows, you have to implement a method yourself, calling getCheckItemIds() on the ListView does not work:
ListView l = getListView();
int count = l.getCount();
for(int i=0; i<count; ++i) {
ViewGroup row = (ViewGroup)l.getChildAt(i);
CheckBox check = (Checked) row.findViewById(R.id.ck1);
if( check.isChecked() ) {
// do something
}
}
Each such view has a TextView and a
CheckBox.
No, it doesn't. It has a CheckedTextView.
So what do I need to do to make a
multiple choice list with my custom
view for each row?
Try making the CheckBox android:id value be "#android:id/text1" and see if that helps. That is the ID used by Android for the CheckedTextView in simple_list_item_multiple_choice.
The solution is to create a custom View that implements the Clickable interface.
public class OneLineCheckableListItem extends LinearLayout implements Checkable {
public OneLineCheckableListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
private boolean checked;
#Override
public boolean isChecked() {
return checked;
}
#Override
public void setChecked(boolean checked) {
this.checked = checked;
ImageView iv = (ImageView) findViewById(R.id.SelectImageView);
iv.setImageResource(checked ? R.drawable.button_up : R.drawable.button_down);
}
#Override
public void toggle() {
this.checked = !this.checked;
}
}
And create a custom layout for the list items using the new widget.
<?xml version="1.0" encoding="utf-8"?>
<ax.wordster.OneLineCheckableListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:background="#drawable/selector_listitem"
android:orientation="horizontal" >
<ImageView
android:id="#+id/SelectImageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="#drawable/button_friends_down" />
<TextView
android:id="#+id/ItemTextView"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:gravity="center"
android:text="#string/___"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#color/text_item" />
</ax.wordster.OneLineCheckableListItem>
Then create a new custom Adapter using the layout above.
It is possible by some trick
in your ListActivtyClass in method
protected void onListItemClick(ListView l, View v, int position, long id) {
//just set
<your_model>.setSelected(true);
}
now in you custom Adapter
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(textViewResourceId, parent, false);
}
if (<your_model>.isSelected()) {
convertView.setBackgroundColor(Color.BLUE);
} else {
convertView.setBackgroundColor(Color.BLACK);
}
return convertView;
}
this way you can customize the view in adapter when the item is selected in the list.
Simple example how to get a custom layout to work as custom checkbox:
private class FriendsAdapter extends ArrayAdapter<WordsterUser> {
private Context context;
public FriendsAdapter(Context context) {
super(context, R.layout.listitem_oneline);
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int pos = position;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rv = inflater.inflate(R.layout.listitem_oneline, parent, false);
rv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
boolean checked = friendsListView.isItemChecked(pos);
friendsListView.setItemChecked(pos, !checked);
}
});
WordsterUser u = getItem(position);
TextView itw = (TextView) rv.findViewById(R.id.ItemTextView);
itw.setText(u.userName + " (" + u.loginName + ")");
ImageView iv = (ImageView) rv.findViewById(R.id.SelectButton);
if (friendsListView.isItemChecked(position)) {
iv.setImageResource(R.drawable.downbutton);
} else {
iv.setImageResource(R.drawable.upbutton);
}
return rv;
}
}
I found it very useful this little code: http://alvinalexander.com/java/jwarehouse/apps-for-android/RingsExtended/src/com/example/android/rings_extended/CheckableRelativeLayout.java.shtml
It is a great addition to #ferdy182 's http://www.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/ content.
Got the solution ... You can get the clicks on the views (like checkboxes in custom layouts of row) by adding listener to each of them in the adapter itself while you return the converted view in getView(). You may possibly have to pass a reference of list object if you intent to get any list specific info. like row id.
I want to confirm that the Pritam's answer is correct. You need an onClickListener on each list's item (define it in the adapter's getView()).
You can create a new onClickListener() for each item, or have the adapter implement onClickListener() - in this case the items must be tagged for the listener to know, which item it is operating on.
Relying on the list onItemClickListener() - as someone advised in another thread - will not work as the CheckBox will intercept the click event so the list will not get it.
And finally #Rahul and JVitella:
The situation is that the CheckBox on a list item must be clickable and checkable independently from the list item itself. Therefore the solution is as I just described above.