Android: Get clicked view inside Listview item - android

I read many threads and did a lot of tests but without success.
In an Activity I have a listview connected to an adapter of type ResourceCursorAdapter.
In the method (bindView) of the adapter I defined the custom layout of every row of the listview.
Every row has 2 ImageButton, 2 TextView and 1 Checkbox: the layout is below.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="100"
tools:context=".Main"
android:descendantFocusability="blocksDescendants">
<CheckBox
android:id="#+id/chebag"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="8" />
<TextView
android:id="#+id/texbag"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="68"
android:gravity="left|center_vertical"
android:lines="3"
android:maxLines="3"
android:text="Item"
android:textSize="21sp" />
<ImageButton
android:id="#+id/butdec"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="6"
android:background="#null"
android:src="#drawable/meno"
android:clickable="false"/>
<TextView
android:id="#+id/texnum"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="12"
android:gravity="center_vertical|center_horizontal"
android:text="100"
android:textSize="20sp" />
<ImageButton
android:id="#+id/butinc"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="6"
android:background="#null"
android:src="#drawable/piu"
android:clickable="false" />
</LinearLayout>
In the Activity I prepared the listener of the listview of name (lismain); with the syntax of the layout every click
in the list item triggers the method (onItemClick).
AdapterView.OnItemClickListener mainlistener= new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View view,final int position, long id) {
}
};
lismain.setOnItemClickListener(mainlistener);
I need these 2 things:
a) when in (onItemClick) I cannot identify which child view was clicked by the user
(the row itself or the ImageButtons inside: to have a different processing)
b) I prefer to handle the click events in the Activity and not in the adapter: it would be also fine if the ImageButtons trigger for example the (onClick) method, but I tried also without success.
Thanks for the support

Thanks for the answer, but at the end I solved the problem with the example here:
http://wiresareobsolete.com/2011/08/clickable-zones-in-listview-items/
The fundamental steps of the link are:
a) the class with the Adapter of the listview is an inner class of the calling Activity.
For example we can call Main the Activity class and MyAdapter the class with type Adapter.
b) The ImageButton in the row layout of the listview is called (butinc):
we call butinc.setOnClickListener(Main.this) in the method (bindView).
public class MyAdapter extends ResourceCursorAdapter {
#Override
public void bindView(View view, Context context, Cursor cursor) {
ImageButton butinc = (ImageButton) view.findViewById(R.id.butinc);
butinc.setOnClickListener(Main.this);
}
}
c) When the ImageButton is pressed by the user in the listview row and the right properties are set in the layout,
for example android:clickable, the click is handled in the OnClick method of Main activity:
#Override
public void onClick (View v) {
if(v.getId() == R.id.butinc) {
// the code here handles the button click
}
}
d) The activity class is for example declared as:
public class Pres extends AppCompatActivity implements View.OnClickListener {
// the Onclick method handles the interface View.OnClickListener
}
Doing all these steps, the button click is handled by methods in the class of type Activity.

Related

Inflated View doesn't catch onClick event

I have an template view it looks like ;
template.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/contentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/view_background"
android:gravity="center_vertical"
tools:context=".MainActivity" >
<Button
android:id="#+id/btnMultiple"
android:layout_width="0dp"
android:layout_height="150dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_weight="1"
android:background="#drawable/button_backgroundblue"
android:onClick="btnMultiple_clicked"
android:tag="4"
android:text="#string/mc_abcd"
android:textColor="#drawable/button_textcolor"
android:textSize="#dimen/h2" />
I'm creating this view programmatically and then i'm adding this View into ViewFlipper as like ;
activity_main.java
public void btnCreateView_clicked(View view) {
ViewFlipper viewFlipper = (ViewFlipper)findViewById(R.id.flipper);
View myView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.template, viewFlipper, false);
viewFlipper.addView(myView);
viewFlipper.showNext(); // Showing lastly created myView successfully. }
Also my activity has public function as like ;
activity_main.java
public void btnMultiple_clicked(View view) {
//Save the world !
}
When i press btnCreateView button from first view it's successfully creating template (myView) and then adding it into ViewFlipper. When i click btnMultiple i'm getting error and my application crashes ; "Could not find a method btnMultiple_clicked(View) in the activity class android.app.Application for onClick handler on view class android.widget.Button with id 'btnMultiple'" i'm sure there is existing method btnMultiple_clicked(View).
PS! If i add listener to btnMultiple programmatically it's gonna work but i wonder why "android:onClick="btnMultiple_clicked"" doesn't work ?
Android looks for the method defined in the onClick attribute in the Activity. The problem in your code is that you used for the LayoutInflater initialization the Application's Context and not the Context of the Activity so the method will not be found there(the Context is passed to the View). Use:
View myView = LayoutInflater.from(this).inflate(R.layout.template, viewFlipper, false);
or any other reference that points to the Activity where those buttons will be used.
This is a good example why you should use in most cases the Context of the Activity.

Android ListView select items to delete with 3 column layout

I'd like to implement a Listview in android in which I have the possibility to enable a delete mode, in which the user can select the entries to delete. It should be similar to the message application in android.
I already have a ListActivity which has an icon on the left and text on the right side. I now like to add a CheckBox floating on the right side of the list entry. The ListActivity is listed in another question by a friend of mine: android listactivity background color .
The layout should be:
Left Picture
Center List item
Right Checkbox for delete selection
How can I achieve this? Is there a standard ListView item in the framework I could use?
I guess you want a CheckBox to appear(only) when is time to delete items from the ListView. Assuming you use the layout from the other question:
<?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:padding="5dp"
android:background="#color/darkbluelogo" >
<ImageView android:id="#+id/list_image"
android:layout_width="48dip"
android:layout_height="48dip"
android:contentDescription="#id/list_image"
/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="fill_parent"
android:padding="5dp"
android:background="#color/darkbluelogo"
android:scrollingCache="false"
android:cacheColorHint="#00000000" >
<TextView
android:id="#+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#+id/title" >
</TextView>
<TextView
android:id="#+id/datetime"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#+id/datetime" >
</TextView>
</LinearLayout>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
</LinearLayout>
When the ListView starts the CheckBox will not be present and the content TextViews will occupy all the space. Add a flag in the getView method of your adapter that will signal that the CheckBox must be shown(here you will set the CheckBox's visibility from the layout to visible). When its time to delete items modify the flag and then call notifyDataSetChanged() so the ListView redraws its children, this time with the CheckBox present.
Note:
You'll have to store the status of the CheckBoxes yourself.
First of all you need a custom layout for your list entries. A simple RelativeLayout including an ImageView , a TextView and a CheckBox should be enough.
Then you might want to build your own custom adapter which can extend BaseAdapter (or SimpleAdapter or CursorAdapter or ArrayAdapter or...). The adapter will bind the list's data to your custom layout. If for example your data is contained in a Cursor it will look like:
private class MyCustomAdapter extends CursorAdapter {
public MyCustomAdapter(Context context) {
super(context, null);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//Return a list item view
return getLayoutInflater().inflate(R.layout.my_custom_list_item_layout, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
//Get views from layout
final ImageView imageView = (ImageView) view.findViewById(R.id.list_item_image);
final TextView textView = (TextView) view.findViewById(R.id.list_item_text);
final CheckBox checkBox = (CheckBox) view.findViewById(R.id.list_item_checkbox);
//Get data from cursor
final String text = cursor.getString(...);
//Add listener to the checkbox
checkBox.setOnClickListener(new OnClickListener() {...});
//Bind data
textView.setText(text);
}
}

Display contents from ListView defined in regular Activity Android

I wanted to create a search view like the one Google uses. For this I created the following XML layout, which basically is a search bar and a button in the upper section of the screen and a ListView at the bottom of it.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/LinearLayoutSearch"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:background="#FF394952">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true" >
<EditText android:layout_height="wrap_content" android:id="#+id/searchTextBar" android:layout_width="wrap_content" android:layout_weight="1">
<requestFocus></requestFocus>
</EditText>
<Button android:layout_height="fill_parent" android:layout_width="wrap_content" android:id="#+id/searchButton" android:text="Buscar"></Button>
</LinearLayout>
<ListView
android:id="#+id/searchResultList"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentBottom="true"
android:layout_weight="1.0" />
</LinearLayout>
And this is the code of the textViewResource that the ArrayAdapter demands on its constructor:
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</TextView>
Now, this is the code of the activity. So far, I just want to display the view with the contents (that's why I'm using a static String array for now).
public class SearchActivity extends Activity{
static final String[] COUNTRIES = new String[] {
"Afghanistan", "Albania", "Algeria"};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.searchview);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.list_item, COUNTRIES);
ListView lv = (ListView)this.findViewById(R.id.searchResultList);
lv.setAdapter(adapter);
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();
}
});
}
}
However, when I run the activity I see the search bar but it doesn't display the ListView.
I've tried changing the extension of SearchActivity to ListActivity, but the program just crashes when I try to start it. I'm also aware of the existence of the Search Interface, but I just want to see this particular method work.
Why it doesn't display the contents of the ListView? Is there a way to fix this?
Thanks in advance
If you are going to use ListActivity you should be aware that ListActivity already has a ListView instance. You need to call its setListAdapter method to set the adapter for its ListView instead of instantiating your own ListView and setting the adapter on it. You can call getListView to get a handle on ListActvity's ListView and then set the click listener on that.
If you want to extend ListActivity then you must have a ListView with id #android:id/list. Change the id of your ListView, that should fix the crash when extending ListActivity.

onItemClickListener not firing on custom ArrayAdapter

I have an Activity that retrieves data from a web service. This data is presented in a ListView via an ArrayAdapter which inflates a RelativeLayout with three TextViews inside, nothing fancy and it work fine.
Now I want to implement a Details Activity that should be called when a user clicks an item in the ListView, sounds easy but I can't for the life of me get the onItemClickListener to work on my ArrayAdapter.
This is my main Activity:
public class Schema extends Activity {
private ArrayList<Lesson> lessons = new ArrayList<Lesson>();
private static final String TAG = "Schema";
ListView lstLessons;
Integer lessonId;
// called when the activity is first created.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// can we use the custom titlebar?
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
// set the view
setContentView(R.layout.main);
// set the title
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.titlebar);
// listview called lstLessons
lstLessons = (ListView)findViewById(R.id.lstLessons);
// load the schema
new loadSchema().execute();
// set the click listeners
lstLessons.setOnItemClickListener(selectLesson);
}// onCreate
// declare an OnItemClickListener for the AdapterArray (this doesn't work)
private OnItemClickListener selectLesson = new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v, int i, long l) {
Log.v(TAG, "onItemClick fired!");
}
};
private class loadSchema extends AsyncTask<Void, Void, Void> {
private ProgressDialog progressDialog;
// ui calling possible
protected void onPreExecute() {
progressDialog = ProgressDialog.show(Schema.this,"", "Please wait...", true);
}
// no ui from this one
#Override
protected Void doInBackground(Void... arg0) {
// get some JSON, this works fine
}
#Override
protected void onPostExecute(Void result) {
progressDialog.dismiss();
// apply to list adapter
lstLessons.setAdapter(new LessonListAdapter(Schema.this, R.layout.list_item, lessons));
}
My ArrayAdapter code:
// custom ArrayAdapter for Lessons
private class LessonListAdapter extends ArrayAdapter<Lesson> {
private ArrayList<Lesson> lessons;
public LessonListAdapter(Context context, int textViewResourceId, ArrayList<Lesson> items) {
super(context, textViewResourceId, items);
this.lessons = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_item, null);
}
Lesson o = lessons.get(position);
TextView tt = (TextView) v.findViewById(R.id.titletext);
TextView bt = (TextView) v.findViewById(R.id.timestarttext);
TextView rt = (TextView) v.findViewById(R.id.roomtext);
v.setClickable(true);
v.setFocusable(true);
tt.setText(o.title);
bt.setText(o.fmt_time_start);
rt.setText(o.room);
return v;
}
}// LessonListAdapter
The main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="#+id/main"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent" android:layout_width="fill_parent"
android:screenOrientation="portrait"
>
<!-- student name -->
<TextView
android:id="#+id/schema_view_student"
android:text="Name" android:padding="4dip"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:gravity="center_vertical|center_horizontal"
style="#style/schema_view_student"
/>
<!-- date for schema -->
<TextView
android:id="#+id/schema_view_title"
android:layout_height="wrap_content"
android:layout_margin="0dip"
style="#style/schema_view_day"
android:gravity="center_vertical|center_horizontal"
android:layout_below="#+id/schema_view_student"
android:text="Date" android:padding="6dip"
android:layout_width="fill_parent"
/>
<!-- horizontal line -->
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="#55000000"
android:layout_below="#+id/schema_view_title"
/>
<!-- list of lessons -->
<ListView
android:id="#+id/lstLessons"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="#+id/schema_view_title"
/>
</RelativeLayout>
The list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="60px"
android:padding="12dip">
<TextView
android:id="#+id/timestarttext"
android:text="09:45"
style="#style/LessonTimeStartText"
android:layout_width="60dip"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_height="fill_parent" android:gravity="center_vertical|right" android:paddingRight="6dip"/>
<TextView
android:id="#+id/titletext"
android:text="Test"
style="#style/LessonTitleText"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_toRightOf="#+id/timestarttext"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true" android:gravity="center_vertical|center_horizontal"/>
<TextView
android:id="#+id/roomtext"
android:text="123"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
style="#style/LessonRoomText"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:gravity="center_vertical" />
</RelativeLayout>
Been messing with this for the last couple of hours and I can't seem to get my head around what the problem is. My problem looks very similar to this question, but I'm not extending ListActivity, so I still don't know where my onListClickItem() should go.
UPDATE: Now I've puzzled with this for several days and still can't find the issue.
Should I rewrite the activity, this time extending ListActivity instead of Activity? Because it provides the onItemClick method itself and is probably easier to overwrite.
Or, should I bind a listener directly in each getView() in my ArrayAdapter? I believe I have read this is bad practice (I should do as I tried and failed in my post).
Found the bug - it seems to be this issue. Adding android:focusable="false" to each of the list_item.xml elements solved the issue, and the onclick is now triggered with the original code.
I've encountered the same issue and tried your fix but couldn't get it to work. What worked for me was adding android:descendantFocusability="blocksDescendants" to the <RelativeLayout> from the item layout xml, list_item.xml in your case. This allows onItemClick() to be called.
What worked for me :
1) Adding android:descendantFocusability="blocksDescendants" to Relative Layout tag.
The result is shown below :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants" >
2) Adding android:focusable="false" to every element in in list_item.xml
example :
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:focusable="false" />
Once I had a similar problem. Every list item had a text view and a checkbox, and just because the checkbox, the whole listitem wasn't 'enabled' to fire the event. I solved it by making a little trick inside the adapter when I was getting the view.
Just before returning the view I put:
v.setOnClickListener(listener);
(The object listener is an onItemClickListener I gave to the Adapter's constructor).
But I have to tell you, the problem is because the platform, it is a bug.
I had the same problem and I tried to solve it by adding
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false"
to my item.xml but it still doesn't work !!! Infact I found the issue in the relative layout witch contains
android:focusableInTouchMode="true" android:focusable="true"
And when I removed it All things is ok
protected void onPostExecute(Void result) {
progressDialog.dismiss(); stLessons.setAdapter(new LessonListAdapter(Schema.this, R.layout.list_item, lessons));
//add this
ListView lv = getListView(); lv.setOnItemClickListener(new ListView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int i, long l) {
//do stuff
}
});
}

How do I get the row ID of the row from a ListView, which contains a clickable item?

Before I added a button to the layout XML for my rows, the row ID was returned in the callback onListItemClick() upon a list item being clicked. Now that I added a button to the list row layout, this callback doesn't work anymore. I read that this is normal. I have been able to get the text and the new button to refer to new callbacks via the inclusion of this sort of thing in the layout XML file for my list row:
<Button
android:onClick="newCallBackFunctionName"/>
The problem is that I can't seem to find a way to retrieve the row id number corresponding to the list item in which the particular button that was pressed lies. In the case of onListItemClick() this was passed as part of the callback, but in the above case only the View object that is clicked is passed back to the callback newCallBackFunctionName. What can I do about this?
*Edit: My list is populated by a SimpleCursorAdaptor, in case that's important.
*Edit: My list and list row XML layouts are as follows:
List:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/basic_linear_layout_v1">
<TextView
android:id="#+id/category_selection_page_name"
style="#style/page_heading_v1"/>
<ListView
android:id="#id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:drawSelectorOnTop="false"/>
<TextView
android:id="#id/android:empty"
style="#style/problem_text_v1"
android:text="#string/search_list_empty" />
<Button
android:id="#+id/select_category_button"
android:textSize="20sp"
android:layout_weight="10"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:onClick="reload"/>
Row:
<?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="wrap_content"
android:orientation="horizontal">
<TextView android:id="#+id/category_browse_name" style="#style/basic_list_item_v1"
android:layout_weight="1"
android:layout_gravity="left"
android:focusable="true"
android:clickable="true"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_gravity="right">
<Button
android:text="..."
android:id="#+id/subcategories_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:focusable="true"
android:onClick="onSubcategoryButtonClick">
</Button>
</LinearLayout>
You can tag your Buttons with the appropriate id or position in your adapter's getView method. For example:
myButton.setTag(id);
Then in your onClick handler, retrieve the tag from the view that was clicked. For example:
public void newCallBackFunctionName(View v) {
long id = (Long) v.getTag();
// ...
}
you have to take little diff aproach
you have to create your own cursorAdapter if it SimpleCursorAdapter then code will be some thing like this
first create a subclass of cursoradapter then override getView Method and then define onclicklistener for them
public class SMSimpleCursorAdapter extends SimpleCursorAdapter{
Context context;
Activity activity;
public SMSimpleCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
this.context=context;
this.activity=(Activity) context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
View view = super.getView(position, convertView, parent);
long id=getItemId(position);
Button button = (Button)view.findViewById(R.id.btnDelete);
//pass id in your handler
button.setOnClickListener(new DeleteItemHandler(id, activity,this));
}
and your handle class will be something like this
public class DeleteItemHandler implements OnClickListener,
android.view.View.OnClickListener {
long id;
Activity activity;
SMSimpleCursorAdapter smSimpleCursorAdapter;
public DeleteItemHandler(long id, Activity activity,
SMSimpleCursorAdapter smSimpleCursorAdapter) {
super();
this.id = id;
this.activity = activity;
this.smSimpleCursorAdapter = smSimpleCursorAdapter;
}
#Override
public void onClick(View v) {
//your own item click code
}
}

Categories

Resources