Override ViewGroup and use ViewDragHelper to implement various drag-and-drop interactions,

Source: Internet
Author: User

Override ViewGroup and use ViewDragHelper to implement various drag-and-drop interactions,

1. If the previous SlideUpLayout control contains a list such as ListView or RecyclerView during use, the following problems may occur:

The xml file is as follows:

 
          <framelayout android:background="#1f00ffff" android:id="@+id/slide_layout_up" android:layout_height="match_parent" android:layout_width="match_parent">            
          
  </framelayout>        <framelayout android:background="#1fffff00" android:id="@+id/slide_layout_down" android:layout_height="match_parent" android:layout_width="match_parent">            
              
           
   
  </framelayout>        <framelayout android:background="#1fff00ff" android:clickable="true" android:id="@+id/slide_layout_slide" android:layout_height="100dp" android:layout_width="match_parent">            
          
  </framelayout>    
 

Ii. Cause Analysis: there is a problem with the event dispatch interception. Remember that in the previous article, the event interception was completely handled by ViewDragHelper, as shown below:

@ Override public boolean onInterceptTouchEvent (MotionEvent ev) {// handle the event to ViewDragHelper return mHelper. shouldInterceptTouchEvent (ev );}
Because the interception is handed over to ViewDragHelper, the contained ListView and so on will not be able to consume the event and thus will not be able to scroll normally. Therefore, the solution is to first judge the ListView to see whether consumption is required. If the event needs to be consumed, the event is handed over to the ListView for processing.

3. The principles of event dispatching interception in popular science are interspersed here: (Copy here to help you understand it)

DispatchTouchEvent
It is used to distribute touch events. In layman's terms, it is used to determine who to handle the current touch event (whether it is the current View or the parent View)
When a touch event occurs, the dispatchTouchEvent (MotionEvent ev) method of the Activity is passed down from the root element until the inmost layer sub-element or the event is intercepted or consumed in the middle of a mona1 element.
If return true, the event is distributed to the current View and consumed by the dispatchTouchEvent method. At the same time, the event stops being passed down. In this way, the onTouchEvent of the View will not receive a response. if return false, the event is returned to the onTouchEvent of the parent View for consumption. If return super. dispatchTouchEvent (ev) is returned, the event is distributed to the onInterceptTouchEvent method of the current View for processing.

OnInterceptTouchEvent
It is used to intercept touch events. In layman's terms, it is used to determine who should handle the touch event that the dispatchTouchEvent has just been thrown to me (whether it is the current View or the subview). Note the difference between it and the dispatchTouchEvent.
If return true, the event is intercepted and the intercepted event is handed over to the onTouchEvent of the View for processing. If return false, the event is passed to the subview, the dispatchTouchEvent of the sub-View is used to process the event. If return super. onInterceptTouchEvent (ev), the event is intercepted, and the event is handed over to the onTouchEvent of the View for processing.

OnTouchEvent
It is used to process touch events. In layman's terms, it is the decision to process the touch events that have just been thrown to me by onInterceptTouchEvent.
If return false, the event will be transmitted from the View to the parent View, and the onTouchEvent of the parent View will be received. If the parent View is also return false, the event will be passed up and received and processed by onTouchEvent. if retrun is true, the event is received and consumed. If retrun super. onTouchEvent (ev) is the same as false.

4. If you have the idea, the solution is to determine whether the ListView needs to consume the event before intercepting the event, that is, whether the event can continue to scroll down, that is, whether it has reached the top. The optimized SlideUpLayout is as follows:

Package com. dway. testwork. viewdrag; import android. content. context; import android. support. v4.widget. viewDragHelper; import android. util. attributeSet; import android. view. motionEvent; import android. view. view; import android. view. viewGroup;/*** can be switched up or down, similar to the vertical ViewPager, And the menu effect * Created by dway on 2018/1/23 is displayed in the upper menu. */public class SlideUpLayout extends ViewGroup {private View mUpView; private View mDownView; Private View mSlideView; // private RecyclerView mListView; private ViewDragHelper mHelper; // The degree of decline. 0 indicates upView, 1 indicates downView private float mSlidePercent = 0; private boolean mInLayout = false; private boolean mFirstLayout = true; public SlideUpLayout (Context context, AttributeSet attrs) {super (context, attrs); mHelper = ViewDragHelper. create (this, 1.0f, new ViewDragHelper. callback () {@ Override p Ublic int clampViewPositionVertical (View child, int top, int dy) {if (child = mUpView) {return Math. max (-mUpView. getMeasuredHeight () + mSlideView. getMeasuredHeight (), Math. min (top, 0);} else if (child = mDownView) {return Math. max (mSlideView. getMeasuredHeight (), Math. min (top, mUpView. getMeasuredHeight ();} else if (child = mSlideView) {return Math. max (-mSlideView. getMeasuredHeight (), Math. min (Top, 0);} return 0;} @ Override public boolean tryCaptureView (View child, int pointerId) {return child = mUpView | child = mDownView ;} @ Override public void onViewReleased (View releasedChild, float xvel, float yvel) {if (releasedChild = mUpView) {int upViewHeight = mUpView. getMeasuredHeight (); int slideViewHeight = mSlideView. getMeasuredHeight (); float offset = (upViewHeight + releasedChi Ld. getTop ()-slideViewHeight) * 1.0f/(upViewHeight-slideViewHeight); mHelper. settleCapturedViewAt (releasedChild. getLeft (), yvel> 0 | yvel = 0 & offset> 0.5f? 0:-upViewHeight + slideViewHeight); invalidate ();} else if (releasedChild = mDownView) {int downViewHeight = mDownView. getMeasuredHeight (); int slideViewHeight = mSlideView. getMeasuredHeight (); float offset = (releasedChild. getTop ()-slideViewHeight) * 1.0f/downViewHeight; mHelper. settleCapturedViewAt (releasedChild. getLeft (), yvel> 0 | yvel = 0 & offset> 0.5f? MUpView. getMeasuredHeight (): slideViewHeight); invalidate () ;}@override public void onViewPositionChanged (View changedView, int left, int top, int dx, int dy) {if (changedView = mUpView) {mDownView. setTop (top + mUpView. getMeasuredHeight (); mSlidePercent = (float) (-top)/mDownView. getMeasuredHeight (); if (mSlidePercent> 0.9f) {mSlideView. setTop (-mSlideView. getMeasuredHeight () + (int) (mSlid EPercent-0.9f)/(1-0.9) * mSlideView. getMeasuredHeight ();} else {mSlideView. setTop (-mSlideView. getMeasuredHeight ();} requestLayout (); if (mOnSlideListener! = Null) {mOnSlideListener. onSlide (mSlidePercent) ;}} else if (changedView = mDownView) {mUpView. setTop (top-mUpView. getMeasuredHeight (); mSlidePercent = (float) (mUpView. getMeasuredHeight ()-top)/mDownView. getMeasuredHeight (); if (mSlidePercent> 0.9f) {mSlideView. setTop (-mSlideView. getMeasuredHeight () + (int) (mSlidePercent-0.9f)/(1-0.9) * mSlideView. getMeasuredHeight ();} else {mSlideView. SetTop (-mSlideView. getMeasuredHeight ();} requestLayout (); if (mOnSlideListener! = Null) {mOnSlideListener. onSlide (mSlidePercent) ;}}@ Override public int getViewVerticalDragRange (View child) {return child = mUpView? MUpView. getMeasuredHeight ()-mSlideView. getMeasuredHeight (): child = mDownView? MDownView. getMeasuredHeight (): child = mSlideView? MSlideView. getMeasuredHeight (): 0 ;}}) ;}@ Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {int widthSize = MeasureSpec. getSize (widthMeasureSpec); int heightSize = MeasureSpec. getSize (heightMeasureSpec); trim (widthSize, heightSize); mUpView = getChildAt (0); mDownView = getChildAt (1); mSlideView = getChildAt (2); // mListView = mDownView. findViewById (R. id. rv_select_course); // up MarginLayoutParams lp = (MarginLayoutParams) mUpView. getLayoutParams (); int widthSpec = MeasureSpec. makeMeasureSpec (widthSize-lp. leftMargin-lp. rightMargin, MeasureSpec. EXACTLY); int heightSpec = MeasureSpec. makeMeasureSpec (heightSize-lp. topMargin-lp. bottomMargin, MeasureSpec. EXACTLY); mUpView. measure (widthSpec, heightSpec); // slide lp = (MarginLayoutParams) m SlideView. getLayoutParams (); widthSpec = getChildMeasureSpec (widthMeasureSpec, lp. leftMargin + lp. rightMargin, lp. width); heightSpec = getChildMeasureSpec (heightMeasureSpec, lp. topMargin + lp. bottomMargin, lp. height); mSlideView. measure (widthSpec, heightSpec); // down lp = (MarginLayoutParams) mDownView. getLayoutParams (); widthSpec = MeasureSpec. makeMeasureSpec (widthSize-lp. leftMargin-lp. right Margin, MeasureSpec. EXACTLY); heightSpec = MeasureSpec. makeMeasureSpec (heightSize-lp. topMargin-lp. bottomMargin-mSlideView. getMeasuredHeight (), MeasureSpec. EXACTLY); mDownView. measure (widthSpec, heightSpec);} @ Override protected void onLayout (boolean changed, int l, int t, int r, int B) {mInLayout = true; MarginLayoutParams lp = (MarginLayoutParams) mUpView. getLayoutParams (); int upTop; if (MFirstLayout) {upTop = lp. topMargin;} else {upTop = mUpView. getTop ();} mUpView. layout (lp. leftMargin, upTop, lp. leftMargin + mUpView. getMeasuredWidth (), upTop + mUpView. getMeasuredHeight (); lp = (MarginLayoutParams) mDownView. getLayoutParams (); int downTop; if (mFirstLayout) {downTop = mUpView. getMeasuredHeight () + lp. topMargin;} else {downTop = mDownView. getTop ();} mDownView. layout (lp. leftMargi N, downTop, lp. leftMargin + mDownView. getMeasuredWidth (), downTop + mDownView. getMeasuredHeight (); lp = (MarginLayoutParams) mSlideView. getLayoutParams (); int slideTop; if (mFirstLayout) {slideTop =-mSlideView. getMeasuredHeight () + lp. topMargin;} else {sshortetop = mSlideView. getTop ();} mSlideView. layout (lp. leftMargin, slideTop, lp. leftMargin + mSlideView. getMeasuredWidth (), slideTop + mSlideView. GetMeasuredHeight (); mInLayout = false; mFirstLayout = false;} @ Override public void requestLayout () {if (! MInLayout) {super. requestLayout () ;}}@ Override protected void onDetachedFromWindow () {super. onDetachedFromWindow (); mFirstLayout = true ;}@ Override protected void onAttachedToWindow () {super. onAttachedToWindow (); mFirstLayout = true ;}@ Override public boolean dispatchTouchEvent (MotionEvent ev) {return super. dispatchTouchEvent (ev) ;}@ Override public boolean onInterceptTouchEvent (MotionEvent Ev) {// return mHelper. shouldInterceptTouchEvent (ev); // Method 1: you also need to control whether the coordinates are the range of mListView. This method is more direct and theoretically more efficient/* if (isInViewArea (mListView, ev. getRawX (), ev. getRawY () & mListView. canScrollVertically (-1) {// If the listview can be rolled, return false is returned to the subview;} else {// if it has been rolled to the top, return mHelper to ViewDragHelper. shouldInterceptTouchEvent (ev);} * // Method 2: Convert the above processing to a more general situation and obtain each sub-view recursively for processing. if (hasViewCanScrollUp (mDownView, ev. getRawX (), ev. getRawY () {return false;} else {return mHelper. shouldInterceptTouchEvent (ev) ;}@ Override public boolean onTouchEvent (MotionEvent event) {mHelper. processTouchEvent (event); return true ;}@ Override public void computeScroll () {if (mHelper. continueSettling (true) {invalidate () ;}@ Override protected LayoutParams generatedefalalayoutparams () {return new MarginLayoutParams (LayoutParams. MATCH_PARENT, LayoutParams. MATCH_PARENT) ;}@ Override public LayoutParams callback (AttributeSet attrs) {return new MarginLayoutParams (getContext (), attrs);} @ Override protected LayoutParams callback (LayoutParams p) {return new MarginLayoutParams (p);}/*** determines whether the view of the specified vertex is not on top. You can continue to draw, if the view is a ViewGroup, You have to recursively judge the sub-view */private boolean hasViewCanScrollUp (View view, float x, float y) {if (view instanceof ViewGroup) {ViewGroup viewGroup = (ViewGroup) view; for (int I = 0; I
 
  
Local [0] & x <local [0] + view. getMeasuredWidth () & y> local [1] & y <local [1] + view. getMeasuredHeight ();} public float getSlidePercent () {return mSlidePercent;} public boolean isSlideUp () {return floatCompare (mSlidePercent, 0);} public boolean isSlideDown () {return floatCompare (mSlidePercent, 1);} public void s0000etodown () {mHelper. smoothSlideViewTo (mDownView, mDownView. getLeft (), mSlideView. getMeasuredHeight (); invalidate ();} public void slideToUp () {mHelper. smoothSlideViewTo (mUpView, mUpView. getLeft (), 0); invalidate ();} private OnSlideListener mOnSlideListener = null;/*** you can set the position callback for listening to slide externally */public void setOnSlideListener (OnSlideListener listener) {mOnSlideListener = listener;} public interface OnSlideListener {/*** slide callback * @ param percent value range [0, 1], 0 indicates sliding to the top, 1 indicates sliding to the bottom */void onSlide (float percent);} private boolean floatCompare (float f1, float f2) {return Math. abs (f1-f2) <Float. MIN_VALUE ;}}
 
Annotations are all in the Code. There are two methods: one is more direct, and the ListView is directly judged. The second is to generalize the method and recursively judge each sub-view. Pay attention to the judgment of the view position and whether the view can be rolled. In addition, the Code also adds a rolling listener.

V. Final: Perfect

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.