Android Custom Control series 10: Use the Add custom layout to take care of the distribution of touch events, and address specific controls in the composite interface in response to specific directions of events

Source: Internet
Author: User
Tags gety

This example is more useful, basically can say, after writing this time, in many cases, come directly to AddView , and then Addinterceptorview a bit, You can easily reach specific controls in the composite interface to respond to touch events in a particular direction.


Please respect the original labor results, reproduced please specify the source:http://blog.csdn.net/cyp331203/article/details/45198549, Do not allow for commercial or profit-making purposes, and violators are required to do so.


In the process of writing Android applications, often encounter the situation: the interface contains a number of controls, we want to touch the interface of the different sliding actions can be received by different controls, or in different positions of the interface sliding action can be received by different controls, in other words, Can I assign a touch event in a specific direction to a particular child view? A typical example is the combination of a ListView and a Header :




problems encountered:


In the example, one problem is that when the finger is sliding on the top carousel, if we want to slide the carousel, we can only turn the carousel when the finger is very level, and when the finger slips a little bit tilted, the touch event is found to be the ListView Given the response, it becomes a swipe up and down the ListView, and this experience is obviously not very good.


If we want a simple implementation now: Maybe the entire application has a lot of pages, now want to in the current specific interface, so that when the finger in the range of the rotation graph, when the finger trajectory angle <45 degrees (direction level), then let the Carousel map response to touch events, So that the top picture can slide horizontally, so that when the finger gesture trajectory angle >45 degrees (vertical in the direction), can the ListView to respond to touch events, so that the entire ListView can swipe up and down, how can this effect be achieved?


Workaround:

Column of the previous article, detailed analysis of the Android touch event distribution process and ViewGroup source code (unfamiliar friends can look at:Android custom control series nine: see the Android Touch event distribution mechanism from the source ). After reading the previous article, it should be understood that the distribution of the Andrioid event is carried out on a layer-by-level level, from the top to the bottom, from the active activity to the Decorview, and then to the layout we wrote, And then another component in the layout, the workaround for this article is to customize a viewgroup, wrapped around the original ListView, and placed on this particular interface. Since event distribution is performed at a level, we can override the dispatchtouchevent method of this outer layer of custom ViewGroup to implement an event distribution mechanism that controls all child view This allows us to implement the response mechanism of the touch events we want in this particular interface.


Write a custom framelayout called interceptorframelayout, overriding the dispatchtouchevent (motionevent ev) method, Mainly solve several problems:


1, in the event of distribution, we get the Motionevent event, how to determine whether the event falls on the control area we want it?

Idea: You can use a map collection in interceptorframelayout to store the view and corresponding directional parameters that we want to control the touch event, exposing the add and remove method to add and remove blocked view objects. After receiving the event, call event.getrawx and Event.getrawy to get the absolute coordinates of the upper-left corner of the screen, and then traverse the view The map set determines whether the absolute coordinates of the touch are within the range of the View and the direction parameters to intercept are consistent. To determine if the touch is on the view, you can use the view.getlocationonscreen (int[]) method to get an int array, the first element represents the x-coordinate of the upper-left corner of the view, and the second element represents the view the coordinates of the upper right corner, the specific method is as follows:


public static Boolean Istouchinview (Motionevent ev, view view) {//Determine if EV occurs in the range of View static int[] touchlocation = new Int[2] ; View.getlocationonscreen (touchlocation);//Through the Getlocationonscreen method, get the coordinates of the upper left corner of the current sub-view float MotionX = EV.GETRAWX (); float motiony = Ev.getrawy ();//returns whether or not in the range, by touching the coordinates of the event and the coordinates of the lower-left upper-right side of the book view, to determine whether to fall within the view return MotionX >= touchlocation [0]&& MotionX <= (touchlocation[0] + view.getwidth ()) && motiony >= touchlocation[1]&& Motiony <= (touchlocation[1] + view.getheight ());}


/** find the view of the corresponding event and direction parameter in the collection, find the return, not found return null */private view Findtargetview (motionevent ev, int orientation) {// Mviewandorientation is a collection of sub-View and corresponding direction parameters for storing the touch event to be monitored set<view> KeySet = mviewandorientation.keyset (); For (view view: KeySet) {Integer ori = mviewandorientation.get (view);//Because all the directional parameters are binary and the operation is 0//So the use and operation here to determine whether the direction of compliance//here all the judging conditions are:// ① the child view is in the Mviewandorientation set//② direction consistent//③ touch events fall within the range of the child view//④ The sub view can consume this event//and satisfy the above four conditions, then the sub view is the child view we are looking for, So return if (ori & orientation) = = Orientation && istouchinview (EV, view) && view.dispatchtouchevent (EV) {return view;}} return null;}


2. Rewrite the Dispatchtouchevent method:
① How to handle the relationship between the down event and move and the cancel and up events.

The link in this relationship is actually mfirsttouchtarget, if you read the previous blog post:Android custom Control series nine: see the Android Touch event distribution mechanism from the sourceStill have the impression, the source codeMfirsttouchtargetThe child view that can consume events at the down event, and then respond to other events after the down event, can be based onMfirsttouchtargetState to make further judgments about subsequent actions. Here we also imitate the source of the way, define aMfirsttarget。 At every step into thedispatchtouchevent, you need to judge it first.Mfirsttargetis empty, ifMfirsttargetis not empty, the previous down event can be consumed by a sub-view in one of the monitoring sets, so we can continue to invokeBoolean flag = Mfirsttarget.dispatchtouchevent ()Method that will follow the events (Move,cancel,upetc.) bydispatchtoucheventPassed to this corresponding sub-view--ThatMfirsttargetThis time, if flag returns True, it indicates that the childview (mfirsttarget)has completely consumed the event, then it should beMfirsttargetReset to empty to facilitate the distribution of the next event, or the touch event is cancel or up, then also indicates the termination of this event, so alsoMfirsttargetEmpty. The value of the flag is then returned.


@Overridepublic boolean dispatchtouchevent (motionevent ev) {int action = ev.getaction ();//meaning should be the shortest distance to trigger a moving event, If less than this distance does not trigger the mobile control,//As Viewpager is to use this distance to determine whether the user page mtouchslop = configuration.getscaledtouchslop (); if (mfirsttarget! = NULL) {///Mfirsttarget is not NULL, indicating that the most recent down event has been responded to by a child view in the Mviewandorientation collection///so that subsequent events continue to be distributed to the child Viewboolean flag = Mfirsttarget.dispatchtouchevent (EV);//If flag=true, indicates that this event quilt view consumption, if the event is Action_cancel or action_up,//also represents the end of the event, The mfirsttarget is then placed empty to facilitate the response of the next down event if (flag&& (action = = Motionevent.action_cancel | | action = = MOTIONEVENT.ACTION_UP)) {mfirsttarget = null;} return to Flagreturn flag;}    ...}


② handling the down event:

When the down event occurs, we don't know the direction of the next move, so at this point we can only pass the event and return the view.dispatchtouchevent of the qualifying sub -View The result of the () method, if the child view in the eligible collection can be found, and the child View.dispatchtouchevent can return true, the representative finds the child view that matches the condition. So assign the value to Mfirsttarget. During the down event, the x, y coordinates of this down event need to be recorded for subsequent move events to be used for judgment.


To get the coordinates of this event, GetX can also final float CurrentX = Ev.getx (), because only the difference is calculated, and final float currenty = ev.gety (); switch (Ev.getaction () {Case MotionEvent.ACTION_DOWN:mFirstTarget = Findtargetview (EV, orientation_all);d ownx = Currentx;downy = CurrentY; Break



③move Event:

When the MOVE event occurs, we retrieve the current x, Y coordinates again, and then compare with the down event to find out in which direction the current direction of the slide is heading, and then you can follow this direction and touch events, Find out if there is a child view that meets the requirements, then assign to mfirsttarget:


Case MotionEvent.ACTION_MOVE:if (Math.Abs (CURRENTX-DOWNX) > Math.Abs (currenty-downy) && Math.Abs (CurrentX -Downx) > Mtouchslop) {System.out.println ("swipe left and right"),///swipe left (Currentx-downx > 0) {//R-Slide Mfirsttarget = Findtargetv Iew (EV, orientation_right);} else {//Zoli Mfirsttarget = Findtargetview (EV, orientation_left);}} else if (Math.Abs (Currenty-downy) > Math.Abs (CURRENTX-DOWNX) && math.abs (currenty-downy) > Mtouchslop) {System.out.println ("swipe up and Down");//swipe up or down if (Currenty-downy > 0) {//Down Mfirsttarget = Findtargetview (EV, Orientation_down) ;} else {//Up Mfirsttarget = Findtargetview (EV, ORIENTATION_UP);} Mfirsttarget = null;} Break


④ handling Cancel or up events:

If the event is cancel or up, it means that the touch event is over, and the mfirsttarget is empty to facilitate receiving the next down event:

Case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:mFirstTarget = null;break;}


Then, if mfirsttarget is not empty, it means that the corresponding child view is found to receive, no continuation of the distribution event, or trueif mfirsttarget is empty at this time. Indicates that there is no child view in the collection that responds to this event, then it is given to super.dispatchtouchevent (EV) processing:

Walk here, as long as the mfirsttarget is not empty, then the corresponding sub-view,//is found in the collection returns True, indicating that the event is consumed and does not continue to distribute if (mfirsttarget! = null) {return true;} else { return super.dispatchtouchevent (EV);}


After the rewrite, you can add the place that you originally added the listview with the interceptorframelayout we wrote, and then add the ListView to the AddView Interceptorframelayout 's children. So that you can achieve the goal, look at the effect:





Here is the complete code for interceptorframelayout :

Package Com.example.viewpagerlistview.view;import Java.util.hashmap;import Java.util.set;import Com.example.viewpagerlistview.application.baseapplication;import Android.content.context;import Android.util.attributeset;import Android.view.motionevent;import Android.view.view;import Android.view.viewconfiguration;import android.widget.framelayout;/** * @author: Bitter Coffee * * @version: 1.0 * * @date: 2015 April 19 * * @blog: http://blog.csdn.net/cyp331203 * * @desc: */public class Interceptorframelayout extends Framelayout { /** represents the slide direction up */public static final int orientation_up = 0x1;//0000 0001/** represents the slide direction down */public static final int ORIENTATION _down = 0x2;//0000 0010/** represents the slide direction left */public static final int orientation_left = 0x4;//0000 0100/** represents the sliding direction right */public St atic final int orientation_right = 0x8;//0000 1000/** represents all orientations of the slide direction */public static final int orientation_all = 0x10;//000 1 0000/** stores the x and y coordinates of the upper-left corner of the view */static int[] touchlocation = new int[2];/** is used to represent the shortest distance to trigger a move event and does not trigger a move control if it is less than this distance, such as VIewpager is to use this distance to determine whether the user is paging */private int mtouchslop;/** is used to record the x-coordinate when the down event occurs */private float downx;/** is used to record the y-coordinate when the down event occurs */ The private float downy;/** is used to store sub-View that requires autonomous control of event distribution, as well as its corresponding sliding direction */private Hashmap<view, integer> mviewandorientation = New Hashmap<view, integer> ();/** indicates that when an event occurs, the found mviewandorientation in the Eligible child view */private view mfirsttarget = null; Private Viewconfiguration Configuration;public Interceptorframelayout (context context, AttributeSet Attrs,int DEFSTYLEATTR) {Super (context, attrs, defstyleattr); init ();} Public Interceptorframelayout (context context, AttributeSet Attrs) {Super (context, attrs); init ();} Public Interceptorframelayout (Context context) {super (context); init ();} private void init () {configuration = Viewconfiguration.get (GetContext ());} @Overridepublic boolean dispatchtouchevent (motionevent ev) {int action = ev.getaction ();//meaning should be the shortest distance to trigger a moving event, If less than this distance does not trigger the mobile control,//As Viewpager is to use this distance to determine whether the user page mtouchslop = configuration.getscaledtouchslop (); if (mfirsttarget! = NULL) {//MFiRsttarget is not empty, indicating that the most recent down event has been responded to by a sub-view in the Mviewandorientation collection//So that subsequent events continue to be distributed to the child Viewboolean flag = Mfirsttarget.dispatchtouchevent (EV);//If flag=true, indicates that the event is fully consumed and ended if the event is Action_cancel or action_up,//also represents the end of the event, The mfirsttarget is then placed empty to facilitate the response of the next down event if (flag&& (action = = Motionevent.action_cancel | | action = = MOTIONEVENT.ACTION_UP)) {mfirsttarget = null;} return to Flagreturn flag;} To get the coordinates of this event, GetX can also final float CurrentX = Ev.getx (), because only the difference is calculated, and final float currenty = ev.gety (); switch (Ev.getaction () {Case MotionEvent.ACTION_DOWN:mFirstTarget = Findtargetview (EV, orientation_all);d ownx = Currentx;downy = CurrentY; Break;case MotionEvent.ACTION_MOVE:if (Math.Abs (CURRENTX-DOWNX)/Math.Abs (currenty-downy) > 0.5f&& math.a BS (CURRENTX-DOWNX) > Mtouchslop) {System.out.print ("swipe left and right"),///swipe left (Currentx-downx > 0) {//R-Slide Mfirsttarget = f Indtargetview (EV, orientation_right); System.out.println ("mfirsttarget=" +mfirsttarget); else {//Zoli Mfirsttarget = Findtargetview (EV, Orientation_left); System.out.println ("mfirsttarget=" +mfirsttarget);} else if (Math.Abs (currenty-downy)/Math.Abs (CURRENTX-DOWNX) > 0.5f&& math.abs (currenty-downy) > MTouc Hslop) {System.out.print ("swipe up and Down");//swipe up or down if (Currenty-downy > 0) {//Down Mfirsttarget = Findtargetview (EV, Orientation_d OWN); System.out.println ("mfirsttarget=" +mfirsttarget); else {//Up Mfirsttarget = Findtargetview (EV, ORIENTATION_UP); System.out.println ("mfirsttarget=" +mfirsttarget); Mfirsttarget = null;} Break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:mFirstTarget = null;break;} Walk here, as long as the mfirsttarget is not empty, then the corresponding sub-view,//is found in the collection returns True, indicating that the event is consumed and does not continue to distribute if (mfirsttarget! = null) {return true;} else { return super.dispatchtouchevent (EV);}} /** find the view of the corresponding event and direction parameter in the collection, find the return, not found return null */private view Findtargetview (motionevent ev, int orientation) {// Mviewandorientation is a collection of sub-View and corresponding direction parameters for storing the touch event to be monitored set<view> KeySet = mviewandorientation.keyset (); For (view view: KeySet) {Integer ori = mviewandorientAtion.get (view);//Because all the directional parameters are binary and the operation is 0//So the use and operation here to determine whether the direction of compliance//here all the judging conditions are://① The sub-view in the Mviewandorientation set// ② direction consistent//③ Touch event falls within the range of the sub-view//④ the child view can consume this event//and satisfy the above four conditions, then the child view is the child view we are looking for, then return if (ori & orientation) = = Orientation && istouchinview (EV, view) && view.dispatchtouchevent (EV)) {return view;}} return null;} public static Boolean Istouchinview (Motionevent ev, view view) {View.getlocationonscreen (touchlocation); float MotionX = EV.GETRAWX (); Float motiony = Ev.getrawy ();//Returns whether the return is within range MotionX >= touchlocation[0]&& MotionX <= ( Touchlocation[0] + view.getwidth ()) && motiony >= touchlocation[1]&& motiony <= (touchLocation[1] + view.getheight ());} /** add intercept */public void Addinterceptorview (Final view view, final int orientation) {//To main thread execution Baseapplication.getmainthread Handler (). Post (new Runnable () {@Overridepublic void run () {if (!mviewandorientation.containskey (view)) { Mviewandorientation.put (view, orientation);}});} /** removing interception effects */public void Removeinterceptorview (Final View v) {///to the main thread executes Baseapplication.getmainthreadhandler (). Post (New Runnable ( {@Overridepublic void Run () {if (!mviewandorientation.containskey (v)) {mviewandorientation.remove (v);}}});}}


Demo Project Source Download: has been uploaded, and so on the batch down on the post



Please respect the original labor results, reproduced please indicate the source: http://blog.csdn.net/cyp331203/article/details/45198549 , do not allow for commercial or profit-making purposes, violators are required to investigate.




Android Custom Control series 10: Use the Add custom layout to take care of the distribution of touch events, and address specific controls in the composite interface in response to specific directions of events

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.