I have really searched a lot on google, stack overflow , and found this : OnItemCLickListener not working in listview ANDROID. But seems sunshine's answer not works for my case. Other answers are all similar ones.
I have tried the following approaches:
add android:focusable="false" to my list item xml
add TextView.setFocusable(false) and TextView.setClickable(false) in ViewHolder
using the xml as described in the above link.
But none of them work.
Here's my xml and java code:
list_item.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"
android:baselineAligned="false"
android:descendantFocusability="blocksDescendants"
android:focusable="false"
android:paddingTop="2dp"
android:gravity="center_vertical" >
<TextView
android:id="#+id/ninegrid_number_list_choice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/ninegrid_number_listchoice_text_size"
android:gravity="center"
>
</TextView>
</LinearLayout>
getView int list adapter.java:
#Override
public View getView(int position, View convertView, final ViewGroup parent) {
ViewHolder holder;
if (convertView == null ) {
convertView = mInflater.inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.mTextView = (TextView)convertView.findViewById(R.id.ninegrid_number_list_choice);
holder.mTextView.setFocusable(false);
holder.mTextView.setClickable(false);
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
holder.mTextView.setText(mList.get(position));
holder.mTextView.setTextColor(mTextColor);
holder.mTextView.setFocusable(false);
holder.mTextView.setClickable(false);
return convertView;
}
Edit:
in my activity:
listchoice.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
listChoice.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Log.v(tag, "sdf");
}
});
On method getView() of the adapter, you can set the onclick event of convertView before returning it.
That event handler can be treated as onItemClick :)
The listview click listener not working when we use buttons,imagebutton etc. As you use only textview so there will not be problem like that...
As you used linearlayout no need to use android:focusable="false" in your linear layout.
We used this code only during usage of buttons. Also no need to use code holder.mTextView.setClickable(false);
As text is not a button so it takes no any focus. When u clicked it will click on the list cell not on the textview...
So Simply inflate and then after setting custom adapter to the listview...
setonitemclicklistner to the list view....
UPDATED ANSWER
convertView.setOnClickListener(new OnItemClickListener( position ));
This will surely works...
I just ran into the same problem, and tried all suggested solutions but none worked for me. After trying out different things, I found out there was another reason for my problem. I was wrapping my list_item inside a ScrollView, which was causing my onItemClick to not be called. I hope this helps someone.
One thing that seems to override the row clicks is if you have textviews, make sure they don't have an android:inputtype associated with them
remove the inputtype and you can click the row
This is in addition of course to the above answers on
Related
I deleted almost all code in my project to find a hiding bug. There was a GridView that containing a frame layout, and the layout contained CheckBox. But I couldn't check the first check box.(others worked)
Finally (I think) I found an answer. But this is so weird. When I deleted lines for recycling convertView, the bug was gone. I changed from :
if(convertView == null) {
layout = (FrameLayout)View.inflate(maincon, R.layout.taste_brand, null);
} else {
layout = (FrameLayout) convertView;
}
to FrameLayout layout = (FrameLayout)View.inflate(maincon, R.layout.taste_brand, null);.
I really have no idea of this stuation. I attach rest codes.
TasteGridAdapter.java:
public class TasteGridAdapter extends BaseAdapter {
Context maincon;
public TasteGridAdapter(Context context) {
maincon = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
FrameLayout layout;
if(convertView == null) {
layout = (FrameLayout)View.inflate(maincon, R.layout.taste_brand, null);
} else {
layout = (FrameLayout) convertView;
}
layout.setLayoutParams(new GridView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
return layout;
}
#Override
public int getCount() {
return 3;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
}
onCreate of the activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.taste);
TasteGridAdapter adapter = new TasteGridAdapter(this);
GridView grid = (GridView) findViewById(R.id.taste_grid);
grid.setAdapter(adapter);
}
taste.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="match_parent"
android:orientation="vertical">
<GridView
android:layout_weight="1"
android:id="#+id/taste_grid"
android:layout_width="match_parent"
android:layout_height="0dip"
android:columnWidth="87dip"
android:gravity="center"
android:horizontalSpacing="4dip"
android:numColumns="auto_fit"
android:padding="2dip"
android:stretchMode="columnWidth"
android:verticalSpacing="4dip" />
</LinearLayout>
taste_brand.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="87dp"
android:layout_height="58dp">
<CheckBox
android:id="#+id/taste_brand_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</FrameLayout>
I encountered similar problem with first item in GridView. To resolve issue, remove 'new' keyword, and change existing views LayoutParams like that:
LayoutParams lp = layout.getLayoutParams();
lp.height = someHeight;
...do something with these LayoutParams. This hack resolves my issues. Conclusion, try to avoid creation of new LayoutParams object through "new".
layout.setLayoutParams(new GridView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
put this line in this condition,
if(convertView == null) {
}
I was faced same problem, but i try this and its work for me. I hope it also work for you.
What you're experiencing has to do with the way Android recycles views in ListView, GridView, etc. You mention that your first checkbox is uncheckable, while your others remain working. I think you'll notice that the others only appear to work properly, since you haven't handled the recycling properly.
The reason your line
FrameLayout layout = (FrameLayout)View.inflate(maincon, R.layout.taste_brand, null);
seems to fix the problem is because this now inflates the views again each time they are used. I'll admit, when I started with this, re-inflating the views seemed to be the best solution; it entirely defeats the purpose of recycling, however, and you lose all the performance benefits otherwise gained.
So now to fix your problem:
First, I highly recommend using the ViewHolder pattern in conjunction with your BaseAdapter. More information on that can be found here.
Second, you should probably create a boolean array to match all the items in your GridView, and use it to determine whether or not an item should be clicked. Set the value of the corresponding boolean inside your checkbox listener and use that value inside getView(..) to check or uncheck that particular box.
An overall better solution might be to use an array (or list) of models inside your adapter class, each of these containing a boolean field accessible through isChecked and setChecked(boolean). Again, you would use this inside your getView(..) to display the views properly and change the value inside your checkbox OnCheckedChangeListener.
Hope that helps.
As jonstaff says, it's to do with View recycling.
If you're using a custom Adapter class for your GridView View binding, try modifying its getView() method to always instantiate a new View like:
public View getView(int position, View convertView, ViewGroup parent){
SomeView v = new SomeView(context); // <--- here
...
return v;
}
Instead of the typical:
public View getView(int position, View convertView, ViewGroup parent){
SomeView v;
if (convertView == null)
v = new SomeView (context);
else
v= (SomeView)convertView;
...
return v;
}
This may affect performance, but it solved my problem for a small GridView of Buttons.
I am trying to create an item in a ListView that has multiple options; view and edit. I would like to create it in exactly the same way as android's contact system - see below:
I have added the red boxes to illustrate the behaviour I want. If you press within the left red-box, you call the contact. If you press within the right red-box, you send a text message to the contact. I have already created a similar layout in XML, but I am having trouble implementing this functionality in code.
I have tried to create custom android:onClick function calls for the separate layouts within the item, but calling an onClick method only allows you to pass in the View as a parameter, but not the position. Needing the position to use listview.getItemAtPosition function, I tried to use listview.getPositionForView to return the position but found this was extremely unstable and was very easy to return incorrect positioning due to recycling of views.
I then tried to set the item's position as the 'tag' in the getView method of my adapter, like so: convertView.setTag(position). But on the onClick method of my activity, I try and use getTag and cast it back to an integer, and it always returns null, which I find puzzling.
What is the best way of implementing a list populated by items with multiple buttons/layouts on each item?
You can create an onClick event on each views in your row like this :
<?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:orientation="horizontal" >
<TextView
android:id="#+id/text_id"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:onClick="textOnClickEvent"/>
<ImageButton
android:id="#+id/button_id"
android:layout_width="#dimen/width_button"
android:layout_height="match_parent"
android:onClick="imageOnClickEvent"
android:src="#android:drawable/ic_menu_delete" />
</LinearLayout>
Or even, add onClick listeners on each views in the getView method...
more info on this here.
In the list view when you define getview method, this is where you provide all the details of the single list item. There you can mention onlick event of each of the views.
in adapter class, add View.OnClickListener to the getView method:
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if(view == null) {
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
view = layoutInflater.inflate(R.layout.grid_vendor_item, null);
}
final TextView textName = (TextView) view.findViewById(R.id.text_id);
final ImageButton imageProfil = (TextView) view.findViewById(R.id.button_id);
textName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// add your edit codes
}
});
imageProfil.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// add your open prodil codes
}
});
return view;
}
I'm using a Spinner with a custom adapter class called AlgorithmAdapter and in principle everything's working fine, meaning the spinner popup appears and all containing views are properly inflated. However, what I'm not able to find out is how I "tell" the spinner when the selection is made. Of course I know about setSelection(), but that doesn't dispose the popup and return focus to the Activity.
So here's some of the relevant code:
In my activity, I create my adapter like this:
SpinnerAdapter spinnerAdapter = new AlgorithmAdapter(
this,
algorithmSpinner,
R.layout.algo_spinner_item_detail,
res.getStringArray(R.array.anaglyph_algorithm_titles),
res.getStringArray(R.array.anaglyph_algorithm_descriptions),
res.obtainTypedArray(R.array.anaglyph_algorithm_icons),
algorithmSpinner.getSelectedItemPosition()
);
algorithmSpinner.setAdapter(spinnerAdapter);
The constructor of AgorithmAdapter has the following signature:
public AlgorithmAdapter(Context context, Spinner spinner, int viewResourceId, String[] titles,
String[] descriptions, TypedArray icons, int selectedPosition) {
The getDropDownView() method of AlgorithmAdapter:
#Override
public View getDropDownView(final int position, View convertView, ViewGroup parent) {
if(convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(viewResourceId, parent, false);
}
final ImageView icon = (ImageView) convertView.findViewById(R.id.algoItemIcon);
final TextView title = (TextView) convertView.findViewById(R.id.algoItemTitle);
final TextView description = (TextView) convertView.findViewById(R.id.algoItemDescription);
final RadioButton radio = (RadioButton) convertView.findViewById(R.id.algoItemRadio);
icon.setImageDrawable(icons.getDrawable(position));
title.setText(titles[position]);
description.setText(descriptions[position]);
radioGroup.add(radio);
/* it's essential to uncheck in case the positions do not match, because
* the system reuses views that could still have a checked radio button */
radio.setChecked(position == selectedPosition);
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
// uncheck every other radio button
for(RadioButton each : radioGroup)
each.setChecked(false);
// inform adapter and user that this button is selected now
radio.setChecked(true);
selectedPosition = position;
spinner.setSelection(position);
}
});
return convertView;
}
where radioGroup is a list of all RadioButtons from all drop down views in order to manage which one is currently checked and which ones have to be unchecked therefore.
That all works very well except for the fact that the popup doesn't close on setSelection() (which itself is working though).
I also tried to attach an OnItemClickListener to the Spinner and manage the selection stuff in there, but that throws an exception saying setOnItemClickListener cannot be used with a spinner, which is really annoying since it feels as if the framework stands in my way instead of helping me at this point. But anyway, that's the reason I have to pass the Spinner through to the adapter which I would be glad I had a different solution for…
So the only thing I'm missing is how I "close" or "dispose" the Spinners drop down. Could anybody help me with that? Is this even anywhere near the way do to it? It cannot be that difficult since writing custom adapters for Spinners is a pretty basic task.
EDIT: No idea if this helps, but maybe someone finds an error in the layout file of my drop down view item. algo_spinner_item_detail.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:background="#android:drawable/list_selector_background" >
<ImageView
android:id="#+id/algoItemIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:contentDescription="#string/algoIconDescription"
android:layout_marginRight="8dp" />
<RadioButton
android:id="#+id/algoItemRadio"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dp"
android:layout_centerVertical="false" />
<TextView
android:id="#+id/algoItemTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/algoItemIcon"
android:layout_alignTop="#id/algoItemIcon"
android:textStyle="bold"
android:layout_marginBottom="1dp" />
<TextView
android:id="#+id/algoItemDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/algoItemTitle"
android:layout_alignLeft="#id/algoItemTitle"
android:layout_toLeftOf="#id/algoItemRadio" />
</RelativeLayout>
How the heck do you close/dispose the f***ing Spinner drop down after you selected an item?
Here's what did the trick for me (WARNING: don't show this code to your kids, you'd certainly go to hell for it):
In the onClick method of my convertView I put this line of code:
((View) parent.getParent().getParent().getParent().getParent()).setVisibility(View.GONE);
which not only gets rid of the LinearLayout in which the convertViews are contained, but also makes the com.android.internal.policy.impl.PhoneWindow$DecorView invisible, that was there to darken the Activity screen while a dialog/drop down is shown.
I'm still after a better solution.
I'm developing an application for Android.
How could I put a button in a group of a ExpandableListView?
By clicking the button a dialog will be displayed instead of open or close the group. Click outside the button, the group should behave normally opening and closing.
The image below shows where I would like to insert the button.
http://img193.imageshack.us/img193/2060/expandablelistviewbutto.png
Android ExpandableListView can have any buttons in Group or child.
Make sure that the button is not focusable like below in adapter.
editButton.setFocusable(false);
this will help to click on Group and Button inside group.parent seperately
You need to inflate the groupView with a custom XML file containing a button, like this one ( e.g inflate_xml_groupview.xml ) :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/FrameLayoutGroupView"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="#+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ButtonOfMyExpandableListGroupView"
android:visibility="visible" />
</FrameLayout>
Then you have to create a custom ExpandableListAdapter that extends BaseExpandableListAdapter and get the Button on the getGroupView() method, like this :
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.inflate_xml_groupview, null);
holder = new ViewHolder();
holder.Button = (Button) convertView.findViewById(R.id.myButton);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.position = ListOfItems.get(groupPosition).getPosition();
Button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(), "Button " + groupPosition + " is clicked !", Toast.LENGTH_SHORT).show();
// DO STUFF
}
});
}
Hope this helps.
To offer an XML based solution, you simply need to add the following line to the control.
android:focusable="false"
Example:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"/>
I created my own ExpandableListView. I used layouts in XML and classes to build the component.
And surprisingly it was very easy to do.
It was much easier to understand than the standard ExpandableListView because I created a class and a layout for each element of the list (for the list itself, for the group and for the items). There was no need messing with lists of lists of maps, which in my opinion decreases the expressivity and the readability of the code.
Additionally, the list becomes extremely flexible and customizable. I can easily add and remove groups and items at runtime. Now I can freely modify the appearance and internal components of the list.
The ExpandableListView I created can do the same as the standard and more. Just can not tell if the performance was impaired, but did not notice any visible problem.
I am trying to create a ListView that will be populated with the entries from an array.
So this is my item layout:
<?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="60dip" >
<ImageView android:id="#+id/list_item_image"
android:layout_height="wrap_content"
android:padding="2dip"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_width="50dip"/>
<TextView android:id="#+id/list_item"
android:layout_height="fill_parent"
android:textSize="25sp"
android:layout_width="fill_parent"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:padding="5dip" >
</TextView>
</LinearLayout>
I tried changing the layout_height of the LinearLayout but I ran into some problems. If I keep the height at wrap_content, my list is displayed with the correct entries -- Item 1, Item 2, Item 3, and so on until Item 12. However if I change the height to 60dip, the entries repeat after the sixth entry (I get Item 1, Item 2, Item 3, Item 4, Item 5, Item 6, Item 1, Item 2, Item 3...). If I keep on making it larger, the entries repeat more frequently.
This is a snippet from the ListAdapter where I set the list entries:
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout layout;
if (convertView == null){
layout = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.items_list_item, parent, false);
TextView title = (TextView) layout.findViewById(R.id.list_item);
title.setText(menuItems[position]);
ImageView icon = (ImageView) layout.findViewById(R.id.list_item_image);
int logo = getResources().getIdentifier(menuIcons[position], "drawable", getPackageName());
icon.setImageResource(logo);
} else {
layout = (LinearLayout) convertView;
}
return layout;
}
Anybody else encountered this problem? I do not understand what is going on since I thought it should be straight-forward grabbing from the array.
EDIT: included the whole of my getView() method. Pardon the ugly way of getting the icons, I haven't figured it out yet,
You didn't post enough code, but in your Adapter's getView(...) try to make use of the convertView.
public View getView(int position, View convertView, ViewGroup parent){
if(convertView == null){
convertView = mInflater.inflate(R.layout.my_listitem_row, parent, false);
}
//...fill the TextViews on your layout
return convertView;
}
Fetching the icons should be as easy as
icon.setImageResource(R.drawable.my_icon); //the res/drawable folder has the my_icon.png file
From the little snippet of code I'm going to guess that it's something to do with the views being reused and the text not getting updated. I'm not certain though without seeing all of the code for the ListAdapter.
Take a look at this session from Google I/O 2010 for loads of really helpful information on how to use ListViews (and by extension adapters). It contains lots of tips and advice on the best way of using them. If you have time watch the video, if not the slides are avaliable.
Good Luck :)