Where should I place the onClickListener on a Custom ListView?
android, android-listview, onclicklistener
Solution
If you want to add a special action for when you click the `TextView` or/and `CheckBox` from any of the rows in your `ListView` then add a `OnCLickListener` for those `Views` in the `getView` method of your custom `Adapter`:
@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.puzzles_row, null);
}
ListedPuzzle lp = items.get(position);
if (lp != null) {
TextView title = (TextView) v.findViewById(R.id.listTitles);
//set as the tag the position parameter
title.setTag(new Integer(position));
title.setOnclickListener(new OnCLickListener(){
@Override
public void onClick(View v) {
// Do the stuff you want for the case when the row TextView is clicked
// you may want to set as the tag for the TextView the position paremeter of the `getView` method and then retrieve it here
Integer realPosition = (Integer) v.getTag();
// using realPosition , now you know the row where this TextView was clicked
}
});
title.setText(lp.getTitle());
CheckBox star = (CheckBox) v.findViewById(R.id.star_listed);
star.setChecked(lp.isStarred());
}
return v;
}
If you want to do an action when a row is clicked(no matter what `View` from that row was clicked(if one was clicked)) just use the `OnItemClickListener` on your `ListView`(or the callback `onListItemClick` in the case of a `ListActivity`).
Also, I hope you set `android:focusable="false"` for the `CheckBox`(in `R.layout.puzzles_row`) because I don't think `onListItemClick` will work otherwise.
Edit :
You start the new `Activity` in the `onListItemClick`(in the case of the `ListActivity`) callback if you want to start the new activity no matter where the user clicks a row:
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent i = new Intent(this, PuzzleQuestionActivity.class);
i.putExtra(PuzzlesDbAdapter.KEY_ROWID, id);
startActivity(i);
}
If, for some reason, you want to start the new `Activity` when the user clicks only(for example) the `TextView` in a `ListView` row then start the new activity in the `onClick` method from my code above:
//...
title.setOnclickListener(new OnCLickListener(){
@Override
public void onClick(View v) {
Integer realPosition = (Integer) v.getTag();
ListedPuzzle obj = items.get(realPosition);
Intent i = new Intent(this, PuzzleQuestionActivity.class);
i.putExtra(PuzzlesDbAdapter.KEY_ROWID, obj.getTheId());//see below
startActivity(i);
}
//...
For this to work you'll have to modify `ListedPuzzle` to also add the `PuzzlesDbAdapter.KEY_ROWID` column from the `puzzlesCursor` cursor in the `fetchData()` method:
//...
while (!puzzlesCursor.isAfterLast()) {
lp = new ListedPuzzle();
lp.setTitle(puzzlesCursor.getString(puzzlesCursor
.getColumnIndex(PuzzlesDbAdapter.KEY_TITLE)));
lp.setStarred(puzzlesCursor.getInt(puzzlesCursor
.getColumnIndex(PuzzlesDbAdapter.KEY_STARRED)) > 0);
lp.setTheId(puzzlesCursor.getLong(puzzlesCursor
.getColumnIndex(PuzzlesDbAdapter.KEY_ROWID)));
listedPuzzles.add(lp);
//...
Problem
I am making a custom `ListView` of rows containing a `CheckBox` and a `TextView`. Before I used custom `ListViews` with SimpleCursorAdapter, my `onListItemClick()` worked fine. I've read I have to add an `onClickListener` to my `TextViews` but WHERE? And WHY? I am still extending `ListActivity` and passing an `Adapter` to `setListAdapter(listedPuzzleAdapter);`, am I not? ``` public class PuzzleListActivity extends ListActivity { private PuzzlesDbAdapter mDbHelper; private Cursor puzzlesCursor; private ArrayList<ListedPuzzle> listedPuzzles = null; private ListedPuzzleAdapter listedPuzzleAdapter; private class ListedPuzzleAdapter extends ArrayAdapter<ListedPuzzle> { private ArrayList<ListedPuzzle> items; public ListedPuzzleAdapter(Context context, int textViewResourceId, ArrayList<ListedPuzzle> items) { super(context, textViewResourceId, items); this.items = 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.puzzles_row, null); } ListedPuzzle lp = items.get(position); if (lp != null) { TextView title = (TextView) v.findViewById(R.id.listTitles); title.setText(lp.getTitle()); CheckBox star = (CheckBox) v.findViewById(R.id.star_listed); star.setChecked(lp.isStarred()); } return v; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.puzzles_list); // Create database helper to open connection mDbHelper = new PuzzlesDbAdapter(this); mDbHelper.open(); fetchData(); } private void fetchData() { puzzlesCursor = mDbHelper.fetchAllPuzzles(); startManagingCursor(puzzlesCursor); listedPuzzles = new ArrayList<ListedPuzzle>(); ListedPuzzle lp; puzzlesCursor.moveToFirst(); while (!puzzlesCursor.isAfterLast()) { lp = new ListedPuzzle(); lp.setTitle(puzzlesCursor.getString(puzzlesCursor .getColumnIndex(PuzzlesDbAdapter.KEY_TITLE))); lp.setStarred(puzzlesCursor.getInt(puzzlesCursor .getColumnIndex(PuzzlesDbAdapter.KEY_STARRED)) > 0); listedPuzzles.add(lp); puzzlesCursor.moveToNext(); } listedPuzzleAdapter = new ListedPuzzleAdapter(this, R.layout.puzzles_row, listedPuzzles); setListAdapter(listedPuzzleAdapter); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); Intent i = new Intent(this, PuzzleQuestionActivity.class); i.putExtra(PuzzlesDbAdapter.KEY_ROWID, id); startActivity(i); } ``` EDIT: My question was towards making the whole item of a custom `ListView` clickable so I found the best answer was the one given by @Luksprog. The `onListItemClick` from my `ListActivity` was enough. I just needed to set the `android:focusable='false'` to make it work. Now, the `CheckBox` on each item of the `ListView` should "star" that item, which means, accesing the DB. ``` 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.puzzles_row, null); } ListedPuzzle lp = items.get(position); if (lp != null) { TextView title = (TextView) v.findViewById(R.id.listTitles); title.setText(lp.getTitle()); CheckBox star = (CheckBox) v.findViewById(R.id.star_listed); star.setChecked(lp.isStarred()); star.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Integer realPosition = (Integer) v.getTag(); ListedPuzzle obj = items.get(realPosition); obj.getId(); } }); } return v; } ``` But the `v.getTag()` refers to a non-final variable and if I change it the `v = vi.inflate(R.layout.puzzles_row, null)` cannot be assigned. What's the best way to solve this? I never really understood the whole final deal.