Android sliding component nesting general idea, multi-task gesture idea, touch transfer idea, [Example] listview nested viewpager

Source: Internet
Author: User

Android sliding component nesting general idea, multi-task gesture idea, touch transfer idea, [Example] listview nested viewpager

In android UI development, we often encounter this requirement:

Two slide-supported components. For example, if the listview is nested with multiple listview, The listview item is a viewpager or gallary? Or nested scrollview.

In general, you may also need to support the following features:

The two-layer components of ingress can be slide.

The consumer does not let two components slide at the same time, or allows the two components to slide at the same time and can be adjusted by themselves

Clicking does not affect the click events of the subview of the underlying view and the subview of the nested view.

We often encounter some problems when implementing the above functions:

Click events are blocked.

Sliding of lateral view is not smooth (not discussed here)

The sliding condition logic of lateral view does not conform to the design.

The handler does not know how to rewrite the function to satisfy the logic.

Here I will introduce the general idea of solving this kind of requirement, as well as a case study of listview and viewpager supporting multi-task gestures.

Touch transmission ideas:

If you want to embed two slide views together, pay attentionProblem:

Slide components such as listview, scrollview, and viewpager have their own sliding rules, we 'd better not override how to slide them (that is, we 'd better not listen to the coordinates of the touch and slide them with code ). We only need to write the non-sliding business we need. Of course, we cannot block the execution of default sliding rules.

BytesThe event distribution and transfer mechanism of viewgroup and view must be clear. You Need To Know That listview inherits from viewgroup. When a touch event occurs, the current activity receives the event and dispatch the event to the top-layer viewgroup, the top-level viewgroup first calls the dispatchTouchEvent. The onInterceptTouchEvent function first determines whether to intercept the event. If the onTouchEvent is selected, the Child view will not receive the TouchEvent. If the event is not truncated, this viewGroup will be distributed to all subviews within the click range (if you do not want to distribute them to subviews within the click range, you need to rewrite more dispatchEvent sections ), that is, call the dispatchTouchEvent of the sub-view. If one of the sub-views returns true (indicating that the sub-view consumes this event), The viewGroup will not execute this TouchEvent. If no t is returned Rue: The OnTouchEvent () of the viewGroup is called. If false is returned, the event is not consumed, and onClick is called. If the event is still not consumed, the dispatch function of the viewgroup returns false.

This logic may be complex, except that the view does not have onInterceptTouch, which can be summarized as follows:

Each view receives an event. If you determine whether to block the event and whether a subview needs to consume the event, if not, run onTouchEvent to consume the event, if you do not consume the view, the view's dispatch returns false, indicating that the view does not consume the event under the branch of the view.

Idea needs to be very clear about the business, because it needs to be clearly written into the code form, but also in the correct place (sometimes you may hesitate to execute a multi-task gesture of the parent view, whether to return a false value in the dispatch of the parent view or the sub-view)

Parent view monopoly parent view event: this. getParent (). requestDisallowInterceptTouchEvent (true); this method disables the parent view from blocking events, that is, the event is acceptable. Remember to close the event when you complete a set of touch.

Multitasking gestures:

It is believed that it is not very difficult for readers to design a multi-task gesture of a view (rewrite onTouchEvent, record the coordinates of pressing, moving, and lifting for some corresponding operations ), however, you can consider this issue by putting it on viewq nesting. You may encounterProblem:

If the child view consumes touchEvent, any behavior of the parent view will not be called.


1. If you want the Parent and Child views to consume this event at the same time, you need to override the dispatch of the parent view and forcibly call onTouchEvent.

2. in this case, you do not want the sub-view to consume this event. There are two solutions: rewrite the intercept of the parent view, check the gesture, block this event, override the dispatch of the sub-view, and check the gesture, returns false. One is forced blocking by the parent view, and the other is forced not to consume the child view.

3. if you are not sure which view should be used to consume this event, you can leave it alone, until you determine whether to block or consume (because your business needs do not clearly define this status, you do not need to define how to deal with it ).

A simple case with some source code:

Requirement Description:

Listview nested viewpager. Each item of listview is defined by the xml layout, which includes a viewpager and other parts.

Supported operations:

The preview viewpager can slide between the left and right. The listview can slide up and down without moving at the same time. Naturally, only the viewpager from the start point can slide and other viewpager cannot be sliding.

¤ Listview supports double-finger narrowing.

Listview supports itemClick (do not click an image in viewpager ).

You can click an image in the specified viewpager.


OnItemClick defines my own business events. It is triggered when each item is clicked and not consumed by viewpager's Image view.

In dispatchTouchEvent (), if the current hand index is greater than or equal to 2, that is, when the dual-finger operation is performed, the onTouchEvent is forcibly called and returned, and super is not called at this time. dispatchTouchEvent (), that is, the event will not go to the Child view.

In onTouchEvent (), I can simply implement the business needs of multi-task gestures.

@ Overridepublic void onItemClick (AdapterView
 Arg0, View arg1, int arg2, long arg3) {// TODO Auto-generated method stubif (mSet. get (arg2 ). size ()> 1) this. downGranularity (arg2);}/*** custom listview is used to process event distribution. * @ Author ipip * August 4, 2014 10:46:53 */private class MyListView extends ListView {public MyListView (Context context) {super (context); this. setDivider (null); // TODO Auto-generated constructor stub}/*** process the listView touch event */@ Overridepublic boolean onTouchEvent (MotionEvent ev) {switch (ev. getAction () & MotionEvent. ACTION_MASK) {case MotionEvent. ACTION_DOWN: case MotionEvent. ACTION_POINTER_DOWN: if (ev. getPointerCount () = 2) dst = measureFingers (ev); break; case MotionEvent. ACTION_UP: case MotionEvent. ACTION_POINTER_UP: if (ev. getPointerCount () = 2 & ndst <dst) {upGranularity (); dst =-1 ;} break; case MotionEvent. ACTION_MOVE: if (ev. getPointerCount ()> = 2) {ndst = measureFingers (ev);} break;} if (ev. getPointerCount ()> = 2) return true; return super. onTouchEvent (ev);}/*****/@ Overridepublic boolean dispatchTouchEvent (MotionEvent ev) {if (ev. getPointerCount ()> = 2) {return onTouchEvent (ev);} return super. dispatchTouchEvent (ev );}}

Next ViewpagerCode:

First, let's explain my onTouchEvent (). Because my viewpager row can accommodate four images (rewrite getPageWidth () in the adapter), when the number of images is smaller than 4, I don't want to deal with slide events. Otherwise, the image will flash in a twinkling of an eye (in fact, this is interesting, but I still don't know what the principle is ).

In dispatch, first determine whether a dual-finger operation is performed. The next step is to record the location of the first operation and the judgment of each movement.

Public class MyViewPager extends ViewPager {public MyViewPager (Context context) {super (context); // TODO Auto-generated constructor stub} public MyViewPager (Context context, AttributeSet attrs) {super (context, attrs);} private float xDown; // record the abscissa when the finger is pressed. Private float xMove; // record the abscissa when the finger moves. Private float yDown; // record the ordinate when the finger is pressed. Private float yMove; // record the ordinate value when the finger moves. Private boolean viewPagerScrolling = false; private boolean fatherScrolling = false; @ Overridepublic boolean onTouchEvent (MotionEvent ev) {if (this. getChildCount () <4) return false; return super. onTouchEvent (ev) ;}@ Overridepublic boolean dispatchTouchEvent (MotionEvent ev) {// TODO Auto-generated method stubif (ev. getPointerCount ()> = 2) return false; switch (ev. getAction () & MotionEvent. ACTION_MASK) {case MotionEvent. ACTION_DOWN: case MotionEvent. ACTION_POINTER_DOWN: xDown = ev. getRawX (); yDown = ev. getRawY (); fatherScrolling = false; break; case MotionEvent. ACTION_MOVE: xMove = ev. getRawX (); yMove = ev. getRawY (); if (fatherScrolling) {return false;} if (viewPagerScrolling) {return super. dispatchTouchEvent (ev);} if (Math. abs (yMove-yDown) <10 & Math. abs (xMove-xDown)> 3) {this. getParent (). requestDisallowInterceptTouchEvent (true); viewPagerScrolling = true;} else if (Math. abs (yMove-yDown) >=10) {fatherScrolling = true; return false;} elsereturn false; break; case MotionEvent. ACTION_UP: case MotionEvent. ACTION_POINTER_UP: viewPagerScrolling = false; if (ev. getPointerCount () = 1) this. getParent (). requestDisallowInterceptTouchEvent (false); break;} return super. dispatchTouchEvent (ev );}}

The above key sentence:

if (fatherScrolling) {return false;}if (viewPagerScrolling) {return super.dispatchTouchEvent(ev);}if (Math.abs(yMove - yDown) < 10 && Math.abs(xMove - xDown) > 3) {this.getParent().requestDisallowInterceptTouchEvent(true);viewPagerScrolling = true;} else if (Math.abs(yMove - yDown) >= 10) {fatherScrolling = true;return false;} elsereturn false;

If there is obvious horizontal movement at this point and the vertical direction must be within the given value, I decided that I would perform the horizontal sliding operation of viewpager. At this time, I assigned viewPagerScrolling to true, all subsequent moves will call super by default. dispatchTouchEvent (), and deny the block of the parent view, the default sliding method will be applied until the end of a series of events. If the vertical movement exceeds the range, and the previous horizontal movement is not obvious, I determine that the listview of the parent view is to slide. If the value of fatherScrolling is true, false is returned for each move, that is, all events are not consumed.

Of course, after all the fingers are raised, these statuses are reset.

Then, you can set an onClickListener () for each imageView of viewpager. The parent view and parent view do not block its click events.

Briefly describe why:
Why is the click of itemClick and imageView not blocked?

The premise is that the ancestor view does not block its events, that is, the ancestor view does not consume down-up, and does not consume down-move-up. Simply put, two levels of view will not consume the click event like pressing and immediately raising it. It can be smoothly transmitted to the sub view by default.

Why does the cursor need to determine whether there is a certain horizontal movement in viewpager?

If viewpager is directly used to consume this event, the parent view will not be able to consume the event. I can only determine whether it is a vertical slide or a horizontal slide based on a certain amount of movement.

You will say that I can first let the sub-view consume, when the vertical direction is too large, it will be transferred to the listview for consumption. In fact, I still need to determine the horizontal movement distance. If the horizontal movement is PX, and then because the vertical movement is 15 PX, The viewpager consumption will not be different from the business needs. Since we also need to calculate the horizontal distance, the best way is to wait first. When the time is right, lock the view that consumes the event until the event chain ends.

Why does struct determine whether the dual-pointer is used in listview dispatch instead of intercept?

Yeah, actually, it's okay...

The following figure shows how to use the new adt's Screen Recording to Screen Recording. It seems that all ADTs starting with kitkat support Screen Recording:

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: 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.