So I have a custom ListView object. The list items have two textviews stacked on top of each other, plus a horizontal progress bar that I want to remain hidden until I actually do something. To the far right is a checkbox that I only want to display when the user needs to download updates to their database(s). When I disable the checkbox by setting the visibility to Visibility.GONE, I am able to click on the list items. When the checkbox is visible, I am unable to click on anything in the list except the checkboxes. I've done some searching but haven't found anything relevant to my current situation. I found this question but I'm using an overridden ArrayAdapter since I'm using ArrayLists to contain the list of databases internally. Do I just need to get the LinearLayout view and add an onClickListener like Tom did? I'm not sure.
Here's the listview row layout XML:
<?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="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="#+id/UpdateNameText"
android:layout_width="wrap_content"
android:layout_height="0dip"
android:layout_weight="1"
android:textSize="18sp"
android:gravity="center_vertical"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:id="#+id/UpdateStatusText"
android:singleLine="true"
android:ellipsize="marquee"
/>
<ProgressBar android:id="#+id/UpdateProgress"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:indeterminateOnly="false"
android:progressDrawable="#android:drawable/progress_horizontal"
android:indeterminateDrawable="#android:drawable/progress_indeterminate_horizontal"
android:minHeight="10dip"
android:maxHeight="10dip"
/>
</LinearLayout>
<CheckBox android:text=""
android:id="#+id/UpdateCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
And here's the class that extends the ListActivity. Obviously it's still in development so forgive the things that are missing or might be left laying around:
public class UpdateActivity extends ListActivity {
AccountManager lookupDb;
boolean allSelected;
UpdateListAdapter list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lookupDb = new AccountManager(this);
lookupDb.loadUpdates();
setContentView(R.layout.update);
allSelected = false;
list = new UpdateListAdapter(this, R.layout.update_row, lookupDb.getUpdateItems());
setListAdapter(list);
Button btnEnterRegCode = (Button)findViewById(R.id.btnUpdateRegister);
btnEnterRegCode.setVisibility(View.GONE);
Button btnSelectAll = (Button)findViewById(R.id.btnSelectAll);
btnSelectAll.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
allSelected = !allSelected;
for(int i=0; i < lookupDb.getUpdateItems().size(); i++) {
lookupDb.getUpdateItem(i).setSelected(!lookupDb.getUpdateItem(i).isSelected());
}
list.notifyDataSetChanged();
// loop through each UpdateItem and set the selected attribute to the inverse
} // end onClick
}); // end setOnClickListener
Button btnUpdate = (Button)findViewById(R.id.btnUpdate);
btnUpdate.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
} // end onClick
}); // end setOnClickListener
lookupDb.close();
} // end onCreate
#Override
protected void onDestroy() {
super.onDestroy();
for (UpdateItem item : lookupDb.getUpdateItems()) {
item.getDatabase().close();
}
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
UpdateItem item = lookupDb.getUpdateItem(position);
if (item != null) {
item.setSelected(!item.isSelected());
list.notifyDataSetChanged();
}
}
private class UpdateListAdapter extends ArrayAdapter<UpdateItem> {
private List<UpdateItem> items;
public UpdateListAdapter(Context context, int textViewResourceId, List<UpdateItem> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = null;
if (convertView == null) {
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = li.inflate(R.layout.update_row, null);
} else {
row = convertView;
}
UpdateItem item = items.get(position);
if (item != null) {
TextView upper = (TextView)row.findViewById(R.id.UpdateNameText);
TextView lower = (TextView)row.findViewById(R.id.UpdateStatusText);
CheckBox cb = (CheckBox)row.findViewById(R.id.UpdateCheckBox);
upper.setText(item.getName());
lower.setText(item.getStatusText());
if (item.getStatusCode() == UpdateItem.UP_TO_DATE) {
cb.setVisibility(View.GONE);
} else {
cb.setVisibility(View.VISIBLE);
cb.setChecked(item.isSelected());
}
ProgressBar pb = (ProgressBar)row.findViewById(R.id.UpdateProgress);
pb.setVisibility(View.GONE);
}
return row;
}
} // end inner class UpdateListAdapter
}
edit: I'm still having this problem. I'm cheating and adding onClick handlers to the textviews but it seems extremely stupid that my onListItemClick() function is not being called at all when I am not clicking on my checkbox.
The issue is that Android doesn't allow you to select list items that have elements on them that are focusable. I modified the checkbox on the list item to have an attribute like so:
android:focusable="false"
Now my list items that contain checkboxes (works for buttons too) are "selectable" in the traditional sense (they light up, you can click anywhere in the list item and the "onListItemClick" handler will fire, etc).
EDIT: As an update, a commenter mentioned "Just a note, after changing the visibility of the button I had to programmatically disable the focus again."
In case you have ImageButton inside the list item you should set the descendantFocusability value to 'blocksDescendants' in the root list item element.
android:descendantFocusability="blocksDescendants"
And the focusableInTouchMode flag to true in the ImageButton view.
android:focusableInTouchMode="true"
I've had a similar issue occur and found that the CheckBox is rather finicky in a ListView. What happens is it imposes it's will on the entire ListItem, and sort of overrides the onListItemClick. You may want to implement a click handler for that, and set the text property for the CheckBox as well, instead of using the TextViews.
I'd say look into this View object as well, it may work better than the CheckBox
Checked Text View
use this line in the root view of the list item
android:descendantFocusability="blocksDescendants"
Related
I have a ListView with multiple elements for each row as follows:
list_element.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/global_container"
android:descendantFocusability="blocksDescendants"
android:layout_width="match_parent"
android:layout_height="match_parent">
<HorizontalScrollView
android:id="#+id/scrollview"
android:clickable="true"
android:focusable="false"
android:focusableInTouchMode="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
Fragment1.java is inflating the layout that contains the listview which has the elements above.
I need to be able to capture click events for each item to change the color of an element when the item is clicked and when another item is clicked restore it to its original color as the new item changes the element color and so on.
This means that I tried to capture OnItemClickListener inside Fragment1.java:
if (rootView != null) {
myList = (ListView) rootView.findViewById(R.id.my_list);
}
ArrayList l = new ArrayList();
//Bogus list
for (int i=0; i<20; i++) {
l.add(new Object());
}
adapter = new MyListAdapter(l, getActivity());
myList.setAdapter(adapter);
final MyListAdapter ad = adapter;
myList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View arg1, int position, long id) {
ad.setClickedPosition(position);
ad.notifyDataSetChanged();
}
});
But (I assume) all the Touch events of HorizontalScrollView are capturing the click/touch... event and I get no response. As you can see I tried android:descendantFocusability="blocksDescendants" and changing focusability back and forth with no results.
I also tried capturing different events inside the list adapter:
vi = inflater.inflate(R.layout.list_element, null, true);
vi.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d("aaa", "aaa");
}
});
And/OR
horizontalScroll = vi.findViewById(R.id.scrollview);
horizontalScroll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d("aaa", "aaa");
}
});
Both options without any results. How shall I then capture a click event for an item on the list and keeping to be able to work with the HorizontalScrollView ?
Ok, I solved the problem as follows:
To be able to click on a list item this two things needed to be done.
First:
android:descendantFocusability="blocksDescendants"
needs to be present on the top-most element of the row element layout.
Second:
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
these 3 properties need to be present on the HorizontalScrollView.
The problem with that solution is that HorizontalScrollView won't work per se, so you need to activate it once the click event is detected. I do it on the getView() method of the adapter.
When the element is clicked I pass the position to the adapter:
fragment.java
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View arg1, int position, long id) {
//Private method inside the adapter
ad.setClickedPosition(position);
ad.notifyDataSetChanged();
}
});
adapter.java
public void setClickedPosition(int clickedPosition) {
this.clickedPosition = clickedPosition;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (clickedPosition == position)
holder.scrollview.setScrollingEnabled(true);
else
holder.scrollview.setScrollingEnabled(false);
}
CustomHorizontalScrollview.java
public void setScrollingEnabled (boolean enabled) {
this.isScrollEnabled = enabled;
}
//...
#Override
public boolean onTouchEvent(MotionEvent ev) {
//...
if (isScrollEnabled) {
return super.onTouchEvent(ev);
}
//...
}
Something to be considered is that this solution works for me since the behaviour "first click then swipe" is what I was looking for, so be aware this might not be exactly what you want but should help you in your quest to make that work.
try to change your list_element.xml as below:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/global_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<HorizontalScrollView
android:id="#+id/scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none" />
</RelativeLayout>
hope this helps you
I have a ListView that gets populated with custom view items. The custom view consists of an icon, a label and a checkbox. When I first create the list, everything displays as it should. If I scroll down the list, the icons and labels continue to be correct further down the list but the checkbox states start to get mixed up, displaying other items as checked besides the ones I chose.
Example: My list starts with no checkboxes set as checked for any items. I see 10 items on screen. I toggle the checkbox on item 10. It updates appropriately. If I scroll down the list, I find that the checkbox for item 20, item 30, etc. start with the checkbox already toggled even though they were never visible to interact with. If I scroll back and forth repeatedly, more and more items in a non-identifiable pattern appear checked.
List setup in my activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.default_list);
profile = (Profile) i.getParcelableExtra("profile");
ArrayList<Application> apps = new ApplicationListRetriever(this).getApplications(true);
adapter = new ApplicationsAdapter(this, R.layout.application_list_item, apps, getPackageManager(), profile);
setListAdapter(adapter);
}
ApplicationsAdapter:
public class ApplicationsAdapter extends ArrayAdapter<Application> {
private ArrayList<Application> items;
private PackageManager pm;
private Profile profile;
private ArrayList<ApplicationListener> listeners = new ArrayList<ApplicationListener>();
public ApplicationsAdapter(Context context, int textViewResourceId,
ArrayList<Application> objects, PackageManager pm, Profile profile) {
super(context, textViewResourceId, objects);
this.pm = pm;
items = objects;
this.profile = profile;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater li = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(R.layout.application_list_item, null);
}
final Application info = items.get(position);
if (info != null) {
TextView text = (TextView) v.findViewById(R.id.label);
CheckBox check = (CheckBox) v.findViewById(R.id.check);
ImageView img = (ImageView) v.findViewById(R.id.application_icon);
//see if the app already is associated and mark checkbox accordingly
for (Application app : profile.getApps()) {
if (info.getPackageName().equals(app.getPackageName())) {
check.setChecked(true);
break;
}
}
check.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
for (ApplicationListener listener : listeners) {
listener.applicationReceived(info, isChecked);
}
}
});
try {
text.setText(info.getName());
}
catch (Exception ex) {
Log.e("ApplicationsAdapter", "Label could not be set on adapter item", ex);
}
if (img != null) {
try {
img.setImageDrawable(pm.getApplicationIcon(info.getPackageName()));
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
}
return v;
}
}
List item layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageView android:id="#+id/application_icon"
android:layout_centerVertical="true" android:layout_alignParentLeft="true"
android:layout_width="36dp" android:layout_height="36dp" android:paddingRight="3dp" android:adjustViewBounds="true" />
<TextView android:layout_width="wrap_content"
android:layout_centerVertical="true" android:layout_toRightOf="#+id/application_icon"
android:layout_height="wrap_content" android:id="#+id/label"
android:text="Application Name" />
<CheckBox android:id="#+id/check"
android:layout_centerVertical="true" android:layout_alignParentRight="true"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" />
</RelativeLayout>
Also worth noting is if I set a breakpoint on the line where I call check.setChecked(true); it only hits that point if the original item I checked is on screen, never for any of the other items that display as checked.
Any ideas why the later items would display as checked or what I can try to fix it?
It is caused by View recycling. You should completely refresh the state of your View when getView is called. Although you are refreshing almost everything, you've forgotten one little thing.
Suppose the list shows 5 items on screen and the second item is checked. The user then scrolls down a further 5 items - however due to view recycling they're really just the same 5 views as before the user scrolled, so the one of the items on-screen will still be checked even though it shouldn't be, because in your code above if no package is matched you are not setting the checkbox to unchecked (so it will stay checked), you are assuming it is already unchecked (which due to View recycling it may not be).
The fix is simple: simply set the checkbox to unchecked before your logic to check if it need be checked:
// Do not assume the checkbox is unchecked to begin with, it might not be
// if this view was recycled, so force it to be unchecked by default and only
// check it if needed.
check.setChecked(false);
for (Application app : profile.getApps()) {
if (info.getPackageName().equals(app.getPackageName())) {
check.setChecked(true);
break;
}
}
I have a ListView with an ArrayList adapter. The rows are not very complex (an Image on the left, a LinearLayout with TextViews inside, and a CheckBox on the right ... the layout is copied in below.) The goal is to have a QuickAction bar come up if the user clicks on the image or text, and have the CheckBox change state if the user clicks on the CheckBox. I have each part working independently, but not when they're together in the layout - somehow, I'm losing the onItemClick event.
The "QuickAction" bar is activated by OnItemClickListener(), and it works fine - unless I have the CheckBox in the layout, in which case the CheckBox works fine (using onClick()) but the onItemClickListener is never fired if the user clicks in the row but outside the CheckBox. The layout (minus some style stuff) is:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/vw1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView android:id="#+id/img"
android:layout_alignParentLeft="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox android:id="#+id/ckb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="right"
android:checked="false" />
<!-- stack text in middle section of the row -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content""/>
<TextView android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>
The code behind it isn't complicated:
public class ListGroupsActivity extends BaseActivityForList {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
// add QuickAction bar
ListView lv = getListView();
lv.setOnItemClickListener(new QAListener(this));
}
}
//inner classes of the ListGroupsActivity
class CbOnClickListener implements OnClickListener {
// ...
// test the checkbox isChecked() and keep state in 'selectedItems' array
class GroupListAdapter extends ArrayAdapter<Group> {
super(/*...*/)
CbOnClickListener cblistener = null; // common listener for all the CheckBoxes
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// here do normal testing for convertView being null etc
// heres the view inflation from the layout into the ViewHolder
holder.tv1 = (TextView) convertView.findViewById(R.id.text1);
holder.tv2 = (TextView) convertView.findViewById(R.id.text2);
holder.img = (ImageView) convertView.findViewById(R.id.img);
holder.ckb = (CheckBox) convertView.findViewById(R.id.ckb);
Group g = groups.get(position); // this is the data obj for the row
holder.tv1.setText(g.getName());
holder.tv2.setText("child groups");
Bitmap bm = BitmapFactory.decodeFile(g.getIconUri());
holder.img.setImageBitmap(bm);
Integer key = (Integer) g.getId();
holder.ckb.setChecked(selectedItems.contains(key));
holder.ckb.setOnClickListener(cblistener); // hook onClick to cblistener
return convertView;
}
The QAListener is in a superclass for all my list activities:
public class BaseActivityForList extends
OrmLiteBaseListActivity<DatabaseHelper> {
// QAListener inner class, constructs new quick action bar when item clicked
class QAListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// has three ActionItem instances, 'copyAction', 'delAction', 'editAction'
ActionItem copyAction = new ActionItem();
copyAction.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// ... does some stuff
mQuickAction.dismiss();
}
}); // end setOnClickListener
} // end onItemClick
} // end QAListener
} end BaseActivityForList
So, how do I arrange for the Checkbox to pick up the onClick, and the rest of the list item to pick up the onItemClick()?
After few hours of debugging and research. I got it working by
setting following properties on the checkbox:
android:focusable="false"
android:focusableInTouchMode="false"
Also ImageView may need to be configured as described in http://blog.sachin.name/?p=62 (Thanks to that person)
On my side, I discovered that item view MUST NOT be clickable and text views should not be clickable, either. It took me a while to realize that I called setClickable(true) on my item view somewhere in the code.
Just for complementing this accepted answer: in case of ImageButton instead of CheckBox it is not enough to set in xml focusable and focusableInTouchMode to false but during runtime focusable need to be set to false. That means to make sure onItemClick will be detected one must do smth like:
button.setFocusable(false);
so things work completely.. Original credits to author here . hope it will help to someone.
Cheers ;)
Edit: There is even more elegant solution try to add android:descendantFocusability="blocksDescendants" in root layout of list element. That will make clicks onListItem possible and separately u can handle Button or ImageButton clicks
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.
I have a simple ListActivity that uses a custom ListAdapter to generate the views in the list. Normally the ListAdapter would just fill the views with TextViews, but now I want to put a button there as well.
It is my understanding and experience however that putting a focusable view in the list item prevents the firing of onListItemClick() in the ListActivity when the list item is clicked. The button still functions normally within the list item, but when something besides the button is pressed, I want onListItemClick to be triggered.
How can I make this work?
as I wrote in previous comment solution is to setFocusable(false) on ImageButton.
There is even more elegant solution try to add android:descendantFocusability="blocksDescendants" in root layout of list element. That will make clicks onListItem possible and separately u can handle Button or ImageButton clicks
Hope it helps ;)
Cheers
I hope I can help here. I assume that you have custom layout for listView items, and this layout consists of button and some other views - like TextView, ImageView or whatever. Now you want to have different event fired on button click and different event fired on everything else clicked.
You can achieve that without using onListItemClick() of your ListActivity. Here is what you have to do:
You are using custom layout, so probably you are overriding getView() method from your custom adapter. The trick is to set the different listeners for your button and different for the whole view (your row). Take a look at the example:
private class MyAdapter extends ArrayAdapter<String> implements OnClickListener {
public MyAdapter(Context context, int resource, int textViewResourceId,
List<String> objects) {
super(context, resource, textViewResourceId, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String text = getItem(position);
if (null == convertView) {
convertView = mInflater.inflate(R.layout.custom_row, null);
}
//take the Button and set listener. It will be invoked when you click the button.
Button btn = (Button) convertView.findViewById(R.id.button);
btn.setOnClickListener(this);
//set the text... not important
TextView tv = (TextView) convertView.findViewById(R.id.text);
tv.setText(text);
//!!! and this is the most important part: you are settin listener for the whole row
convertView.setOnClickListener(new OnItemClickListener(position));
return convertView;
}
#Override
public void onClick(View v) {
Log.v(TAG, "Row button clicked");
}
}
Your OnItemClickListener class could be declared like here:
private class OnItemClickListener implements OnClickListener{
private int mPosition;
OnItemClickListener(int position){
mPosition = position;
}
#Override
public void onClick(View arg0) {
Log.v(TAG, "onItemClick at position" + mPosition);
}
}
Of course you will probably add some more parameters to OnItemClickListener constructor. And one important thing - implementation of getView shown above is pretty ugly, normally you should use ViewHolder pattern to avoid findViewById calls.. but you probably already know that.
My custom_row.xml file is RelativeLayout with Button of id "button", TextView of id "text" and ImageView of id "image" - just to make things clear.
Regards!
When a custom ListView contains focusable elements, onListItemClick won't work (I think it's the expected behavior). Just remove the focus from the custom view, it will do the trick:
For example:
public class ExtendedCheckBoxListView extends LinearLayout {
private TextView mText;
private CheckBox mCheckBox;
public ExtendedCheckBoxListView(Context context, ExtendedCheckBox aCheckBoxifiedText) {
super(context);
…
mText.setFocusable(false);
mText.setFocusableInTouchMode(false);
mCheckBox.setFocusable(false);
mCheckBox.setFocusableInTouchMode(false);
…
}
}
I have the same problem: OnListItemClick not fired ! [SOLVED]
That's happen on class that extend ListActivity,
with a layout for ListActivity that content TextBox and ListView nested into LinearLayout
and another layout for the rows (a CheckBox and TextBox nested into LineraLayout).
That's code:
res/layout/configpage.xml (main for ListActivity)
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="#+id/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="pippo" />
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="false"
android:background="#aaFFaa" >
</ListView>
<LinearLayout>
res/layout/row.xml (layout for single row)
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="#+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
**android:focusable="false"**
**android:focusableInTouchMode="false"** />
<TextView
android:id="#+id/testo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
**android:focusable="false"**
**android:focusableInTouchMode="false"** />
</LinearLayout>
src/.../.../ConfigPage.java
public class ConfigPage extends ListActivity
{
TextView selection;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.configpage);
// loaded from res/value/strings
String[] azioni = getResources().getStringArray(R.array.ACTIONS);
setListAdapter(new ArrayAdapter<String>(this, R.layout.row,
R.id.testo, azioni));
selection = (TextView) findViewById(R.id.selection);
}
public void onListItemClick(ListView parent, View view, int position, long id)
{
selection.setText(" " + position);
}
}
This begin to work when I added on row.xml
android:focusable="false"
android:focusableInTouchMode="false"
I use Eclipse 3.5.2
Android SDK 10.0.1
min SDK version: 3
I hope this is helpful
... and sorry for my english :(
just add android:focusable="false" as one of the attributes of your button
I've had the same problem with ToggleButton. After half a day of banging my head against a wall I finally solved it.
It's as simple as making the focusable view un-focusable, using 'android:focusable'. You should also avoid playing with the focusability and clickability (I just made up words) of the list row, just leave them with the default value.
Of course, now that your focusable views in the list row are un-focusable, users using the keyboard might have problems, well, focusing them. It's not likely to be a problem, but just in case you want to write 100% flawless apps, you could use the onItemSelected event to make the elements of the selected row focusable and the elements of the previously selected row un-focusable.
ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// When clicked, show a toast with the TextView text
Toast.makeText(getApplicationContext(), ((TextView) view).getText(),
Toast.LENGTH_SHORT).show();
}
});
I used the getListAdapter().getItem(position) instantiating an Object that holds my values within the item
MyPojo myPojo = getListAdapter().getItem(position);
then used the getter method from the myPojo it will call its proper values within the item .