简体   繁体   中英

Where should I place the onClickListener on a Custom ListView?

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.

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);
//...

you can assign an onClickListener in the adapter, but it's bad practice.

what you should do, is to implement onItemClick like this:

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) {
    TextView text = parent.getItemAtPosition(position);
    // DO SOMETHING or in your case 
    //startActivity(new Intent(<the correct intent>);
}

You should implement onItemClickListener on your ListView.

ListView lv = getListView();


  lv.setOnItemClickListener(new OnItemClickListener() {


  });

Use onClick Listner in your adapter only. In your adapter you are returning v which is view kiond of object. Put there your onCLickListener.

egvsetOnCLickListener.

I am keeping in mind that you want to open an activity on click of view. And yes if your ListedPuzzle class is serializable or parcelable you can forward entire object using putextra method of intent from same.

If you are not clear with answer tell me I will provide you samll code snippet

 lstviewemojis.setOnItemClickListener(new AdapterView.OnItemClickListener() {

      @Override
      public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
      }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM