Android ListView Setonitemclicklistener Click Invalid Reason analysis _android

Source: Internet
Author: User
Tags gety

Objective

Recently in the process of doing the project, in the use of ListView encountered when setting the item to listen to events when there is no callback Onitemclick method of the problem. My situation is that there is a button in the item. So there is no callback. On Baidu found a solution there are two kinds, as follows:

1. Add android:focusable= "false" to the view where the checkbox or button corresponds

Copy Code code as follows:

Android:clickable= "false" Android:focusableintouchmode= "false"

2, in the item outermost add attribute android:descendantfocusability= "Blocksdescendants"

The rationale for most postings on the web is that when ListView contains controls such as Button,checkbox, Android will default to the focus to these controls, which means that ListView's item simply does not get the point. So the Onitemclick time cannot be triggered.

Because I want to verify, all have this article. Okay, here we go.

Where are the onitemclicklistener we set up for ListView the callback?

To figure this out, let's start with the Android event distribution mechanism, where the event distribution mechanism has written some particularly detailed and excellent articles on the Internet, and here's a brief introduction:

Three important methods of event distribution

Copy Code code as follows:

public boolean dispatchtouchevent (motionevent ev)

This method is used for event distribution, which is invoked when an event is passed to the current view, and the result is affected by the ontouchevent of the current view and the Dispatchtouchevent method of the subordinate view.

Copy Code code as follows:

public boolean onintercepttouchevent (motionevent ev)

The method is called in the previous method dispatchtouchevent, and the result indicates whether the current event is blocked, and false is returned by default, which is not blocked.

Copy Code code as follows:

public void Ontouchevent (Motionevent event)

Called in the Dispatchtouchevent method, which handles the Click event, and returns whether the current event is consumed by the result.

When the Click event triggers the process

After understanding the event distribution mechanism, we certainly need to do event handling after Setonitemclick, which says that event blocking is not intercepted by default, so we suspect that the ItemClick event will be handled in the ListView Ontouchevent method. To find you will find ListView there is no Ontouchevent method. Then we'll go to his father's abslistview to find it. And there's really:

@Override public boolean ontouchevent (motionevent ev) {if (!isenabled ()) {//A disabled view This is clickable still co
Nsumes the touch/events, it just doesn ' t respond to them. Return isclickable () | |
Islongclickable (); } if (Mpositionscroller!= null) {Mpositionscroller.stop ();} if (Misdetaching | |!isattachedtowindow ()) {//something I
SN ' t right. Since we rely on being attached to get data set change notifications,//don ' t risk doing where we anything try T
o Resync and find things//in a bogus state.
return false;
} startnestedscroll (scroll_axis_vertical);
if (mfastscroll!= null && mfastscroll.ontouchevent (EV)) {return true;} initvelocitytrackerifnotexists ();

Final Motionevent Vtev = motionevent.obtain (EV);
Final int actionmasked = ev.getactionmasked ();
if (actionmasked = = Motionevent.action_down) {mnestedyoffset = 0;} vtev.offsetlocation (0, Mnestedyoffset); Switch (actionmasked) {case Motionevent.action_down: {ontouchdown (EV), break,} case MotionEvent.action_move: {ontouchmove (EV, VTEV); break;} case MOTIONEVENT.ACTION_UP: {ontouchup (EV); break;} Case Motioneven  T.action_cancel: {ontouchcancel (); break;} case MOTIONEVENT.ACTION_POINTER_UP: {Onsecondarypointerup (EV); final int x =
Mmotionx;
Final int y = mmotiony;
Final int motionposition = pointtoposition (x, y); if (motionposition >= 0) {//Remember where the motion event started final View child = Getchildat (MOTIONPOSITION-MF
Irstposition);
Mmotionvieworiginaltop = Child.gettop ();
Mmotionposition = motionposition;
} mlasty = y;
Break 
Case Motionevent.action_pointer_down: {//New pointers take over dragging duties final int index = Ev.getactionindex ();
Final int id = ev.getpointerid (index);
Final int x = (int) ev.getx (index);
Final int y = (int) ev.gety (index);
mmotioncorrection = 0;
Mactivepointerid = ID;
Mmotionx = x;
Mmotiony = y;
Final int motionposition = pointtoposition (x, y); if (motionposition >= 0) {//Remember where the motion event started final VIew child = Getchildat (motionposition-mfirstposition);
Mmotionvieworiginaltop = Child.gettop ();
Mmotionposition = motionposition;
} mlasty = y;
Break
} if (Mvelocitytracker!= null) {mvelocitytracker.addmovement (Vtev);} vtev.recycle ();
return true; }

The code is longer, we mainly look at 46 lines of MOTIONEVENT.ACTION_UP, because the Onitemclick event is triggered at the moment our fingers are lifted from the screen, at Motionevent.action_ Up in the case of the ontouchup (EV); then we can think that the reason for the problem is in this method.

private void Ontouchup (motionevent ev) {switch (mtouchmode) {case Touch_mode_down:case touch_mode_tap:case Touch_mode
_done_waiting:final int motionposition = mmotionposition;
Final View child = Getchildat (motionposition-mfirstposition); if (child!= null) {if (Mtouchmode!= touch_mode_down) {child.setpressed (false);} final Float x = Ev.getx (); final bool
EAN inList = x > Mlistpadding.left && x < getwidth ()-mlistpadding.right; if (inList &&!child.hasfocusable ()) {if (Mperformclick = null) {Mperformclick = new PerformClick ();} final Ab
Slistview.performclick PerformClick = Mperformclick;
Performclick.mclickmotionposition = motionposition;
Performclick.rememberwindowattachcount ();
Mresurrecttoposition = motionposition; if (Mtouchmode = = Touch_mode_down | | mtouchmode = = TOUCH_MODE_TAP) {removecallbacks (Mtouchmode = = Touch_mode_down? mPend
ingcheckfortap:mpendingcheckforlongpress);
Mlayoutmode = Layout_normal; if (!mdatachanged && madapter.isEnabled (motionposition)) {mtouchmode = Touch_mode_tap; Setselectedpositionint (mmotionposition); LayoutChildren ();
Child.setpressed (TRUE);
Positionselector (mmotionposition, child);
Setpressed (TRUE);
if (mselector!= null) {drawable d = mselector.getcurrent (); if (d!= null && D instanceof transitiondrawable) {
((transitiondrawable) d). Resettransition ();
} mselector.sethotspot (x, Ev.gety ()); } if (Mtouchmodereset!= null) {removecallbacks (mtouchmodereset);} mtouchmodereset = new Runnable () {@Override public V OID Run () {mtouchmodereset = null; mtouchmode = Touch_mode_rest; child.setpressed (false); setpressed (false); if (!mdatach
anged &&!misdetaching && Isattachedtowindow ()) {Performclick.run ();}}
};
Postdelayed (Mtouchmodereset, Viewconfiguration.getpressedstateduration ());
else {mtouchmode = Touch_mode_rest; Updateselectorstate ();} return;
else if (!mdatachanged && madapter.isenabled (motionposition)) {Performclick.run ();}} } Mtouchmode =Touch_mode_rest;
Updateselectorstate ();
Break }

This is mainly to see 7 lines to 18 lines, get our item view, and in 15 lines of code to determine whether the item's view is in the scope of the focus (Hasfocusable ()), where the hasfocusable () to take a counter judgment, that is to say, We will need our Itemview hasfocusable () method to return False in order to execute the method, the following method is the method of clicking the event. So let's see if Mperformclick really is the execution of our ItemClick event.

PerformClick and related code are as follows:

Private class PerformClick extends Windowrunnnable implements Runnable {
int mclickmotionposition;
@Override public
Void Run () {
//The data has changed since we posted this action in the event queue,
//bail Out before bad things happen
if (mdatachanged) return;
Final ListAdapter adapter = madapter;
Final int motionposition = mclickmotionposition;
if (adapter!= null && mitemcount > 0 &&
motionposition!= invalid_position &&
motio Nposition < Adapter.getcount () && Samewindow ()) {
final view view = Getchildat (motionposition-mfirstpos ition);
If There is no view, something bad happened (the "view scrolled off
"//screen, etc.) and we should cancel the C Lick
if (view!= null) {
Performitemclick (view, Motionposition, Adapter.getitemid (motionposition));
}
}
}
}

The 18th line of code gets the item View we clicked and invokes the Performitemclick method. Let's look at the Performitemclick method of Abslistview:

@Override public boolean Performitemclick (view view, int position, long ID) {Boolean handled = false; Boolean Dispatchit
Emclick = true; if (Mchoicemode!= choice_mode_none) {handled = true; Boolean checkedstatechanged = false; if (Mchoicemode = = Choice_mode
_multiple | | (Mchoicemode = = Choice_mode_multiple_modal && mchoiceactionmode!= null))  {Boolean checked =!mcheckstates.get (position, false); Mcheckstates.put (position, checked); if (mcheckedidstates!= null && Madapter.hasstableids ()) {if (checked) {Mcheckedidstates.put (Madapter.getitemid (position), position);
else {mcheckedidstates.delete (Madapter.getitemid (position));} if (checked) {mcheckeditemcount++;} else {mcheckeditemcount--} if (Mchoiceactionmode!= null) {MMULTICHOICEMODECALLB
Ack.onitemcheckedstatechanged (Mchoiceactionmode, position, ID, checked);
Dispatchitemclick = false;
} checkedstatechanged = true; else if (Mchoicemode = = Choice_mode_single) {Boolean checked =!mcheckstates.get (position, false); if (checked) {mcheckstates.clear (); Mcheckstates.put (position, true); if (mcheckedidstates!= null && mAdapter.h
Asstableids ()) {mcheckedidstates.clear (); Mcheckedidstates.put (Madapter.getitemid (position), position);
Mcheckeditemcount = 1;
else if (mcheckstates.size () = = 0 | |!mcheckstates.valueat (0)) {mcheckeditemcount = 0;} checkedstatechanged = true;
} if (checkedstatechanged) {updateonscreencheckedviews ();}}
if (Dispatchitemclick) {handled |= Super.performitemclick (view, position, id);} return handled; }

See line 54th calls the Performitemclick method of the parent class:

public Boolean Performitemclick (view view, int position, long ID) {
final boolean result;
if (Monitemclicklistener!= null) {
playsoundeffect (soundeffectconstants.click);
Monitemclicklistener.onitemclick (this, view, position, id);
result = true;
} else {result
= false;
}
if (view!= null) {
view.sendaccessibilityevent (accessibilityevent.type_view_clicked);
}
return result;
}

Well, it's been a long time, finally the point. 3rd

The line code is obvious, that is, if there is a Itemclicklistener, execute his Onitemclick method, and finally callback to our common method.

Here, I believe you already know, the key code is just above the one we analyzed the If judgment

if (inList &&!child.hasfocusable ()) {
if (Mperformclick = null) {
Mperformclick = new PerformClick ();
}
.....
}

That is, only item's view hasfocusable () method returns false to perform Onitemclick.

View and ViewGroup's hasfocusable

ViewGroup's hasfocusable

@Override Public
Boolean hasfocusable () {
if (Mviewflags & Visibility_mask)!= VISIBLE) {return
false;
}
if (isfocusable ()) {return
true;
}
Final int descendantfocusability = Getdescendantfocusability ();
if (descendantfocusability!= focus_block_descendants) {
final int count = Mchildrencount;
Final view[] children = Mchildren;

for (int i = 0; i < count; i++) {
final View child = Children[i];
if (child.hasfocusable ()) {return
true;
}
}} return false;
}

Look at the source we can know:

Returns True if the ViewGroup visiable and focusable are both true, even if the focus can be obtained.
If we set the Descendantfocusability property for ViewGroup and are equal to focus_block_descendants, return false. Cannot get focus.
If you do not set the Descendantfocusability property, just one child view hasfocusable returns True,viewgroup hasfocusable.

And see the hasfocusable of view.

ViewGroup's hasfocusable

public Boolean hasfocusable () {
if (!isfocusableintouchmode ()) {for
(viewparent p = mparent p instanceof Viewgro Up p = p.getparent ()) {
final ViewGroup g = (viewgroup) p;
if (G.shouldblockfocusfortouchscreen ()) {return
false;
}}} Return (Mviewflags & visibility_mask) = = VISIBLE && isfocusable ();
}

If the focus is not available in touch mode, then all the parent nodes of the view are traversed first, and if a parent node has a blocked child View to get the focus, then the view cannot get the focus
If the focus is not available in touch mode, and no parent node is set to block the child View gain focus, and if the focus is available in touch mode, then the Visiable and Focusable properties of the View itself are judged to determine whether the focus can be obtained, only visiable and fo Cusable true at the same time, the view is likely to get focus.

Okay, so here's the analysis. Let's go back and look at two solutions.

Add android:focusable= "False" to the view where the checkbox or button corresponds

Copy Code code as follows:

Android:clickable= "false" Android:focusableintouchmode= "false"

Add attributes to the outermost item android:descendantfocusability= "Blocksdescendants"

In the first case, the item is not set to descendantfocusability= "Blocksdescendants", traversing all of the child view, because all of the child view is not available focus, all item also does not have the focus, So the code that says the conditional judgment of callback to sex is also:

if (inList &&!child.hasfocusable ()) {
if (Mperformclick = null) {
Mperformclick = new PerformClick (); 
   } ...
}

If the condition is set, all callbacks are executed.

In the second case, item, set the Descendantfocusability= "Blocksdescendants", all without traversing the child view,child.hasfocusable () directly returns FALSE.

The above is the article to share the Android ListView Setonitemclicklistener Click Invalid Reason analysis, I hope you like.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.