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;
}
}
Related
I am using Spinner in one of my activity. Problem is that it shows its zero index element as title. because of this it appears twice, first in title and second is as first element of spinner. I don't want to give the selected option Which is in title in spinner drop down because its already selected So whats the use of it to give it as spinner first option. I want the selected option in title and rest of the option in Spinner list. Have a look at my code -
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal|center_vertical"
android:orientation="horizontal" >
<Spinner
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="center_horizontal|center_vertical" />
</LinearLayout>
and adapter class is -
public class SpinnerAdapter extends BaseAdapter {
Context mContext;
List<SpinnerContent> list;
public SpinnerAdapter(Context context, List<SpinnerContent> list) {
mContext = context;
this.list = list;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
SpinnerContent item = list.get(position);
if (convertView == null) {
LayoutInflater inflator = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflator.inflate(R.layout.spinner_item_row_image,
null);
}
TextView tvTitle = (TextView) convertView
.findViewById(R.id.tvSpinnerItem);
ImageView imgSpinnerContent = (ImageView) convertView
.findViewById(R.id.imgSpinnerItem);
if (item.getCollectionName().equalsIgnoreCase("Home")) {
imgSpinnerContent.setVisibility(View.VISIBLE);
imgSpinnerContent.setImageResource(R.drawable.icon_home);
tvTitle.setText("Home");
tvTitle.setTextColor(mContext.getResources().getColor(
R.color.white_text));
convertView.setBackgroundColor(mContext.getResources().getColor(
R.color.text_color_light_gray));
} else if (item.getCollectionName().equalsIgnoreCase("One Level Up")) {
imgSpinnerContent.setVisibility(View.VISIBLE);
imgSpinnerContent.setImageResource(R.drawable.icon_spinner_up);
tvTitle.setText("One Level Up");
tvTitle.setTextColor(mContext.getResources().getColor(
R.color.white_text));
convertView.setBackgroundColor(mContext.getResources().getColor(
R.color.text_color_light_gray));
} else if (item.getCollectionName().equalsIgnoreCase("One Level Down")) {
imgSpinnerContent.setVisibility(View.VISIBLE);
imgSpinnerContent.setImageResource(R.drawable.icon_spinner_down);
tvTitle.setText("One Level Down");
tvTitle.setTextColor(mContext.getResources().getColor(
R.color.white_text));
convertView.setBackgroundColor(mContext.getResources().getColor(
R.color.text_color_light_gray));
} else {
if (position == 0) {
convertView.setBackgroundColor(mContext.getResources()
.getColor(android.R.color.transparent));
imgSpinnerContent.setVisibility(View.GONE);
tvTitle.setText(item.getCollectionName());
tvTitle.setTextColor(mContext.getResources().getColor(
R.color.white_text));
} else {
tvTitle.setText(item.getCollectionName());
applyTheme(convertView, mContext);
}
}
return convertView;
}
How can I achieve it. I want first element of list (Which is passed to spinner) as title but don't want to show it as first element of spinner.
Thanks in advance.
Its not possible with spinner. Spinner always shows the first element as title. If you want to add title then you better display your title at 0th position. In validation part if user selects 0th position set validation message to user.
I think you should use Kevin Chris solution.
Still if you want to achieve then try following trick.I never used this but it should work.
spinner.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
View convertView = spinner.getSelectedView();
View selectedView= adapter.getDropDownView(selectedPosition, convertView, spinner);
selectedView.setVisibility(View.GONE);
}
});
In my Android application, my goal I thought would be very simple - To generate a list of installed applications and place a tick box along side each, acting as a 'tick to exclude' list.
To generate the list of installed applications, I'm using the standard Android example code, demonstrated inside a fragment here. I won't repost it all to keep this post as concise as possible.
The performance is terrible and my first question on this subject would be requesting example code that LazyLoads the application icons. The implementation of LazyLoading icons into a ListView appears to only be a concern when the images are being downloaded. Since Android does not use this method when generating a list of applications, then I'm wondering if this is therefore overkill?
The problems start when a CheckedTextView is checked and as the views are recycled in the list, further boxes become ticked down the list (out of the initial view) or they 'forget' they have been ticked.
To combat this problem, I had to keep a reference to which items were ticked and use the following code in getView()
// store CheckTextView's
private static HashMap<Integer, CheckedTextView> mCheckedList = new HashMap<Integer, CheckedTextView>();
// store state
private static HashMap<Integer, Boolean> mIsChecked = new HashMap<Integer, Boolean>();
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
View view;
if (convertView == null) {
view = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
} else {
view = convertView;
}
final AppEntry item = getItem(position);
((ImageView) view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());
CheckedTextView ctv = (CheckedTextView) view.findViewById(R.id.text1);
ctv.setText(item.getLabel());
// set current state
if (mIsChecked.get(position) != null) {
if (mIsChecked.get(position)) {
ctv.setChecked(true);
}
} else {
ctv.setChecked(false);
}
ctv.setTag(position);
mCheckedList.put(position, ctv);
ctv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
CheckedTextView ct = mCheckedList.get(view.getTag());
if (DE.BUG) {
MyLog.d("ct text: " + ct.getText().toString());
}
ct.toggle();
mIsChecked.put((Integer) view.getTag(), ct.isChecked());
}
});
return view;
}
}
That works, but the performance is terrible due to the work done for each view being refreshed/recycled and the OnClickListener placed on each item (more on this below) - Eclipse also tells me there's more something else to change:
Use new SparseArray(...) instead for better
performance
My woes don't end there though.. to help the user get to the application they want quickly, I implemented a filter as follows:
#Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
View searchView = SearchViewCompat.newSearchView(getActivity());
if (searchView != null) {
SearchViewCompat.setOnQueryTextListener(searchView,
new OnQueryTextListenerCompat() {
#Override
public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has changed. Since this
// is a simple array adapter, we can just have it do the filtering.
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
mAdapter.getFilter().filter(mCurFilter);
return true;
}
});
item.setActionView(searchView);
}
}
I assume when a filter is typed in, the ListView is redrawn and the references to positions become messed up? This results in boxes becoming ticked based on their position on the display.
I had to implement an OnClickListener for each entry above, as I cannot get a reference to the CheckedTextView from onListItemClick. Here are some of my many attempts:
#Override
public void onListItemClick(final ListView listView, final View view, final int position, final long id) {
// View v = (View) listView.getChildAt(position);
// CheckedTextView ctv = (CheckedTextView)
// v.findViewById(R.id.text1);
// ctv.toggle();
// RelativeLayout r = (RelativeLayout) view;
// CheckedTextView ctv = (CheckedTextView)
// r.findViewById(R.id.text1);
// CheckedTextView ctv = (CheckedTextView) view;
// ((CheckedTextView)
// listView.getItemAtPosition(position)).setChecked(!((CheckedTextView)
// listView
// .getItemAtPosition(position)).isChecked());
// CheckedTextView ctv = (CheckedTextView) view.getTag(position);
// ctv.toggle();
As #CommonsWare replied in this topic, the CheckedTextView reference to findViewById(R.id.text1) is not what I'm after.
EDIT - XML Layout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingRight="6dip" >
<CheckedTextView
android:id="#+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/icon"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:paddingLeft="4dip"
android:paddingTop="4dip"
android:textStyle="bold"
android:duplicateParentState="true" />
<ImageView
android:id="#+id/icon"
android:layout_width="48dip"
android:layout_height="48dip"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:paddingLeft="2dip" />
</RelativeLayout>
I'm about ready to give up and implement my own layout with a separate text view and check box, but I can't help thinking I'll be reinventing the wheel if I do that? Am I making this much harder than it should be!?
In an ideal world:
A list of installed applications that LazyLoad the application icons.
A reference to the actual CheckedTextView from the onListItemClick.
The correct way to store and reference which items have been checked.
I hope someone can help and I thank you in advance.
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.
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.