Source of demand:

The product needs to realize the effect of sliding out when you look at the big picture with wechat moments, but there are not only big pictures in the project, but also buttons, texts, videos and so on. In short, the layout is very complicated. Anyway, he doesn’t care about these, but he needs to slide out and retract the small picture position on the interface.

Found a lot of DEMO, basically only meet the picture, video to achieve this function, then come by yourself

SmartTouchLayout

Multiple gesture recognition layout, as long as the VIEW in the layout, support:

Double finger, double click zoom; One-finger sliding; Slide out; Click Exit; Does not affect child control events; Does not conflict with ViewPage;

Mindless call: FrameLayout works the way it does.

Suspected car can not be without evidence, directly above

Double finger, double click zoom; To slide back to a specified position;

Does not affect child control events; When the position is not specified, the slide to the bottom disappears;

Does not conflict with ViewPage;

Technical points:

  • Determine whether to click or double click by calculating the time difference between clicks
private float firstClickX, firstClickY, secondClickX, secondClickY; / / statistics? Private TouchEventCount mInTouchEventCount = new TouchEventCountThread(); // According to the number of hits counted by TouchEventCountThread, Private TouchEventHandler mTouchEventHandler = new TouchEventHandler(); Private void checkClickDown (MotionEvent ev) {if (0 = = mInTouchEventCount. TouchCount) {/ / press for the first time, start/statistics/Log. I (TAG, "CheckClickDown starts counting when first pressed"); postDelayed(mInTouchEventCount, DOUBLE_CLICK_TIME_OFFSET); } } private void checkClickUp(float clickX, float clickY){ //Log.i(TAG , "checkClickUp clickX:" + clickX + ",clickY:" + clickY); // A click event must be pressed and lifted, so just handle if (! mInTouchEventCount.isLongClick) { mInTouchEventCount.touchCount++; if(mInTouchEventCount.touchCount == 1){ firstClickX = clickX; firstClickY = clickY; // log. I (TAG, "checkClickUp "); }else if(mInTouchEventCount.touchCount == 2){ secondClickX = clickX; secondClickY = clickY; float xOff = Math.abs(firstClickX - secondClickX); float yOff = Math.abs(firstClickY - secondClickY); If (xOff < 60 && yOff < 60){log. I (TAG, "checkClickUp Double click "); } else {/ / Double click is not established, when click the processing mInTouchEventCount. TouchCount = 1; // log. I (TAG, "checkClickUp Double click not valid "); } }else{ mInTouchEventCount.touchCount = 0; // log. I (TAG, "checkClickUp undo "); }} else {/ / long press recovery mInTouchEventCount isLongClick = false; // log. I (TAG, "checkClickUp "); } } private class TouchEventCountThread implements Runnable { public int touchCount = 0; public boolean isLongClick = false; @Override public void run() { Message msg = new Message(); if(0 == touchCount){ // long click isLongClick = true; } else { msg.arg1 = touchCount; mTouchEventHandler.sendMessage(msg); touchCount = 0; } // log. I (TAG, "TouchEventCountThread end :" + touchCount); } } private class TouchEventHandler extends Handler { @Override public void handleMessage(Message msg) { //Log.i(TAG, "touch " + msg.arg1 + " time."); if(msg.arg1 == 1){ onSingleClicked(oldX, oldY); }else{ onDoubleClicked(oldX, oldY); }}}Copy the code
  • If you click, you pass the event to the child VIEW

    public boolean onInterceptTouchEvent(MotionEvent ev) {

    final int action = ev.getAction(); If (action == motionEvent.action_move && mTouchState == TOUCH_MYSELF){// log. I (TAG, "intercept for yourself "); return true; } switch (action) {case motionEvent. ACTION_DOWN: checkClickDown(ev); // oldX = ev.getRawX(); oldY = ev.getRawY(); mTouchState = (isZooming || isMoving) ? TOUCH_MYSELF : TOUCH_TO_CHILDREN; //Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN oldX:" + oldX + ",mTouchState:" + mTouchState); break; Float tMoveX = ev.getrawx () -oldx; float tMoveX = ev.getrawx () -oldx; final float xDiff = Math.abs(tMoveX); float tMoveY = ev.getRawY() - oldY; final float yDiff = Math.abs(tMoveY); if (yDiff > mTouchSlop || xDiff > mTouchSlop) { mTouchState = TOUCH_MYSELF; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mTouchState = TOUCH_TO_CHILDREN; break; } // origin do return mTouchState ! = TOUCH_TO_CHILDREN;Copy the code

    }

  • Deal with zoom

    private void setScaleDetector(){ ScaleGestureDetector.OnScaleGestureListener scaleListener = new ScaleGestureDetector .SimpleOnScaleGestureListener() {

    @Override public boolean onScale(ScaleGestureDetector detector) { float scaleFactor = detector.getScaleFactor(); if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) return false; // isZooming = true; mCurrentScale *= scaleFactor; if(mCurrentScale < MIN_SCALE){ mCurrentScale = MIN_SCALE; } setScaleX(mCurrentScale); setScaleY(mCurrentScale); // Return true with blinking false; } @Override public void onScaleEnd(ScaleGestureDetector detector) { super.onScaleEnd(detector); //Log.i(TAG, "onScaleEnd" ); scaleEnd(); }}; mScaleDetector = new ScaleGestureDetector(getContext(), scaleListener); }Copy the code
  • Handle the slide event in onTouchEvent() when not zooming

  • Handle border bounceback during sliding and scaling

  • AnimXXX () handles various slides through animation

Summary, the key is to gesture recognition and distribution, there are detailed comments in the code, some places may be handled improperly, please give advice.

How to use

  1. reference

    Implementation 'com. Jagger: SmartTouchLayout: 1.0.1'Copy the code
  2. Use it directly in the layout. XML file, just like FrameLayout

    <com.jagger.smartviewlibrary.SmartTouchLayout
            android:id="@+id/stl"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
    
            ...
    
    </com.jagger.smartviewlibrary.SmartTouchLayout>
    Copy the code
  3. When setting the end of the animation to fly to where, you can specify the location and size, the effect is shown in Figure 1; If no, fly to the bottom as shown in Figure 2.

    /** * at the end of the setup, * @param w view.getwidth () width at the end * @param h view.getheight () height at the end * @param left View location[0] End X coordinates relative to the screen * @param Top View location[1] End Y coordinates relative to the screen * @param scaleSide end wide/high stretch */ public void setEndViewLocalSize(int w, int h, int left, int top, EndViewScaleSide scaleSide)Copy the code
  4. Sets whether you need to support slide off

    martTouchLayout.setMoveExitEnable(true);
    Copy the code
  5. Sets whether scaling is required

    smartTouchLayout.setZoomEnable(true);
    Copy the code
  6. Finally, set the owning Activity to transparent

    <! - AppCompatActivity transparent Theme - > < style name = "MyTranslucentTheme" parent "=" Theme. AppCompat. Light. NoActionBar "> < item name="android:windowNoTitle">true</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item> </style>Copy the code

Download experience

Making address:

Github.com/evening424/…