# SmartTouchLayout **Repository Path**: lv003s0/SmartTouchLayout ## Basic Information - **Project Name**: SmartTouchLayout - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-03-01 - **Last Updated**: 2022-03-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 智能识别手势布局-SmartTouchLayout ============================ 需求来源: ------- 产品需要实现跟微信朋友圈看大图可下滑退出的效果,
但项目中不仅有大图,图中还有按钮,有文本,有视频等等,总之布局很复杂
反正这些他不管,就是要下滑退出,再缩回上一层界面小图片位置。

找了不少DEMO,基本都是只满足图片,视频实现这个功能, 那就自己来


SmartTouchLayout ----------------- 多手势识别的布局, 只要在布局里的VIEW,就支持:
双指、双击缩放;单指滑动;下滑退出;单击退出;不影响子控件事件;与ViewPage不冲突;
无脑调用:
FrameLayout怎么用,它就怎么用。



疑车不能无据,直接上图 ----------------- ![](https://github.com/evening424/resource/blob/master/Gif/Video_20210129_055202_927.gif)
双指、双击缩放;下滑退出到指定位置;

![](https://github.com/evening424/resource/blob/master/Gif/Video_20210129_055219_439.gif)
不影响子控件事件;不指定位置时,下滑到底部消失;

![](https://github.com/evening424/resource/blob/master/Gif/Video_20210129_055226_655.gif)
与ViewPage不冲突;


\----------------------------------------\ 美图看完了,可否打赏个 ⭐️
\----------------------------------------\

下载体验 -----------------
![](https://github.com/evening424/resource/blob/master/images/SmartTouchLayout_Demo_v1.0.4_download.png?raw=true)
如何使用 ----------------- 1.引用
``` implementation 'com.jagger:SmartTouchLayout:1.0.4' ``` 2.直接在layout.xml文件中使用
使用方式跟FrameLayout一样 ``` ... ``` 3.属性设置
设置结束时动画飞到哪去,可指定位置和大小,效果如图1;不设置,则飞到底部如图2; ``` /** * 设置结束时,动画回到什么位置和大小 * @param w view.getWidth() 结束时的宽 * @param h view.getHeight() 结束时的高 * @param left view location[0] 结束时相对屏幕的X坐标 * @param top view location[1] 结束时相对屏幕的Y坐标 * @param scaleSide 结束时以宽/高拉伸 */ public void setEndViewLocalSize(int w, int h, int left, int top, EndViewScaleSide scaleSide) ``` 设置是否需要支持下滑关闭 ``` smartTouchLayout.setMoveExitEnable(true); ``` 设置是否需要支持缩放 ``` smartTouchLayout.setZoomEnable(true); ``` 4.最后要把归属的Activity设置为透明 ``` ```

代码解读 ----------------- 通过计算点击的时间差,判断是单击还是双击 ```java private void checkClickDown(MotionEvent ev){ if (0 == mInTouchEventCount.touchCount) { // 第一次按下时,开始统计 //Log.i(TAG , "checkClickDown 第一次按下时,开始统计" ); postDelayed(mInTouchEventCount, DOUBLE_CLICK_TIME_OFFSET); } } private void checkClickUp(float clickX, float clickY){ //Log.i(TAG , "checkClickUp clickX:" + clickX + ",clickY:" + clickY); // 一次点击事件要有按下和抬起, 有抬起必有按下, 所以只需要在ACTION_UP中处理 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 ){ //Double click 成立 //Log.i(TAG , "checkClickUp Double click 成立"); }else{ //Double click 不成立,当单击处理 mInTouchEventCount.touchCount = 1; //Log.i(TAG , "checkClickUp Double click 不成立,当单击处理"); } }else{ mInTouchEventCount.touchCount = 0; //Log.i(TAG , "checkClickUp 复原"); } }else { // 长按复原 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 结束:" + 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); } } } ```
如果单击,判断把事件向子VIEW传递还是自已处理 ```java public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getAction(); if(action == MotionEvent.ACTION_MOVE && mTouchState == TOUCH_MYSELF){ //Log.i(TAG, "拦截 为自己处理"); 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; case MotionEvent.ACTION_MOVE: // 是否进行了滑动,设置滑动状态 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; } ```
处理缩放 ```java @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); //返回 true 会一闪一闪的 return false; } @Override public void onScaleEnd(ScaleGestureDetector detector) { super.onScaleEnd(detector); //Log.i(TAG, "onScaleEnd" ); scaleEnd(); } }; ```
在 onTouchEvent() 中处理滑动事件, 缩放时自己处理滑动事件, 非缩放时把滑动事件向父传递,所以 ViewPage 会处理左右滑动事件 ``` if(isZooming){ //缩放时, 自己处理MOVE事件 getParent().requestDisallowInterceptTouchEvent(true); }else { //非缩放时, 由父控件处理MOVE事件 getParent().requestDisallowInterceptTouchEvent(false); ... } ```
滑动和缩放过程中,处理边界回弹 checkBorder() ```java float overRightOffset = location[0] - (mCurrentScale*getWidth() - originalRight)*-1; float overBottomOffset = location[1] - (mCurrentScale*getHeight() - originalBottom)*-1; if(location[0] > 0){ //是否越入左边界 moveX4Zooming = ((1-mCurrentScale) * getWidth())/2 * -1; //放大后相对于 原来大小的 X 坐标的偏移 //是否越入上,下边界 checkTopBottomBorder(location[1], overBottomOffset); animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming); }else if(overRightOffset < 0){ //是否越入右边界 moveX4Zooming += Math.abs(overRightOffset); //放大前的 X=0 //是否越入上,下边界 checkTopBottomBorder(location[1], overBottomOffset); animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming); }else{ //是否越入上,下边界 checkTopBottomBorder(location[1], overBottomOffset); animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming); } ```
如果为哥你节省了几天宝贵的时间,为何不打赏一杯柠檬茶呢? -----------------
![](https://github.com/evening424/resource/blob/master/images/WechatIMG1.jpeg)