博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义可拖拽LinearLayout(ViewGroup),防页面刷新回到原点
阅读量:5737 次
发布时间:2019-06-18

本文共 6943 字,大约阅读时间需要 23 分钟。

前段时间有需求要做一个活动,入口是一个悬浮可拖拽的按钮。如果只是一个可拖拽的View也好办,搜文章也能搜到很多自定义可拖动的View,而且项目中也有一个自定义可拖拽的ImageView。但是现在需求是这个按钮可以关闭,多加了一个关闭按钮,那只能用ViewGroup了,最后我自定义了一个LinearLayout

第一个碰到的问题,是要处在LinearLayout中的活动图片和关闭按钮,防止在移动的时候触发点击事件。根据事件分发机制我们要在onInterceptTouchEvent() 方法里做一下判断是否要进行事件拦截,如果是处在MotionEvent.ACTION_MOVE中就进行拦截

@Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        int action = ev.getAction() & MotionEvent.ACTION_MASK;        switch (action) {            case MotionEvent.ACTION_DOWN:                mLastTouchX = ev.getX();                mLastTouchY= ev.getY();                return false;            case MotionEvent.ACTION_MOVE:                mMoveX = ev.getX();                mMoveY = ev.getY();                //移动很小的一段距离也视为点击                if(Math.abs(mMoveX - mLastTouchX) < 5 || Math.abs(mMoveY - mLastTouchY) < 5)                    //不进行事件拦截                    return false;                else                    return true;        }        return false;    }复制代码

第二个碰到的问题,选择在拖动自定义LinearLayout控件过程中用哪些方法来移动。因为是根据项目中可拖动自定义ImageView来做的,这个控件中是用了layout(l,t,r,b)在拖动过程中重新布局,但是有一个问题,在有banner或者下拉刷新、上拉加载的这些页面中,拖动之后只要banner进行轮播或者上拉加载数据的时候会重走onLayout(boolean changed, int l, int t, int r, int b)方法,这个时候传过来的参数是控件的起始位置,所以又回到了原点。

本来我想在onLayout()方法中用layout()方法,传的参数为移动后的位置,但是有个问题,可以点开layout(),这个方法最后走的还是onLayout()所以就造成死循环了。这是个死结,后来我还想着在onLayout()进行判断,如果是拖动后手势抬起就不再走layout(),加上判断之后果然是可行的,但是后来测试的时候我来回切Fragment回到页面中的时候死循环又开始了。。。具体什么原因我也没弄很明白。

onLayout()方法中用layout()重新布局是不可行的了,解决这个bug的时候把同事拉过来看了一下这个问题,同事说可以用其他的方式移动,试了几种方法最后用了offsetTopAndBottom/offsetLeftAndRight完美解决。

最后一个问题,就是拖动之后,要进行吸附在左右两侧,并且控件要限制在屏幕内不要越界,这个还比较好解决的。解决这个问题前要记住getTop()、getRight()、getBottom()、getLeft()这个四个方法分别是控件顶部到屏幕顶部的距离、控件右边界到屏幕左边界的距离、控件底部到屏幕顶部的距离、控件左边界到屏幕左边界的距离。

思路就是在MotionEvent.ACTION_UP中判断,获取一下当前的相对位置,如果超过了屏幕的1/2说明吸附屏幕右侧,那么再移动控件右边界到屏幕右边界的距离,就是屏幕宽度减去getRight(),同理吸附左侧是一样的。限制超过上下边界我是在拖动后手势抬起后,把超过的那一部分再滑动回来这样解决的。或者你可以在move过程中直接限制控件不超过屏幕。完整代码如下

package com.dudou.demo;import android.content.Context;import android.support.annotation.Nullable;import android.support.v4.view.MotionEventCompat;import android.util.AttributeSet;import android.view.MotionEvent;import android.widget.LinearLayout;public class DragLinearLayout extends LinearLayout {    private float mLastTouchX = 0;    private float mLastTouchY= 0;    private float mMoveX = 0;    private float mMoveY = 0;    private float mLeft;    private float mTop;    private float mRight;    private float mBottom;    private int mDx;    private int mDy;    private boolean isLeft = false;    boolean moveRight = false;    boolean moveLeft = false;    //屏幕宽度    private static final int screenWidth = ScreenUtil.getScreenWidth();    //屏幕高度    private static final int screenHeight = ScreenUtil.getScreenHeight()    public TouchLinearLayout(Context context) {        super(context);    }    public TouchLinearLayout(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public TouchLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        int action = ev.getAction() & MotionEvent.ACTION_MASK;        switch (action) {            case MotionEvent.ACTION_DOWN:                mLastTouchX = ev.getX();                mLastTouchY= ev.getY();                return false;            case MotionEvent.ACTION_MOVE:                mMoveX = ev.getX();                mMoveY = ev.getY();                //移动很小的一段距离也视为点击                if(Math.abs(mMoveX - mLastTouchX) < 5 || Math.abs(mMoveY - mLastTouchY) < 5)                    //不进行事件拦截                    return false;                else                    return true;        }        return false;    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        final int action = MotionEventCompat.getActionMasked(ev);        switch (action) {            case MotionEvent.ACTION_DOWN: {                break;            }            case MotionEvent.ACTION_MOVE: {                moveRight = false;                moveLeft = false;                final float x = ev.getX();                final float y = ev.getY();                final float dx = x - mLastTouchX;                final float dy = y - mLastTouchY;                mLeft = getLeft() + dx;                mTop = getTop() + dy;                mRight = getRight() + dx;                mBottom = getBottom() + dy;                if(mLeft < 0){                    moveLeft = true;                    mLeft = 0;                    mRight = mLeft + getWidth();                }                if(mRight > screenWidth){                    moveRight = true;                    mRight = screenWidth;                    mLeft = mRight - getWidth();                }                if(mTop < 0){                    mTop = 0;                    mBottom = mTop + getHeight();                }                if(mBottom > screenHeight){                    mBottom = screenHeight;                    mTop = mBottom - getHeight();                }                mDx += dx;                mDy += dy;                offsetLeftAndRight((int)dx);                offsetTopAndBottom((int)dy);                if(moveLeft){                    offsetLeftAndRight(-getLeft());                }                if(moveRight){                    offsetLeftAndRight(screenWidth-getRight());                }                break;            }            case MotionEvent.ACTION_UP: {                int upX = (int) ev.getRawX();                if (upX > (screenWidth / 2)) {                    isLeft = false;                    offsetLeftAndRight(screenWidth-getRight());                    invalidate();                } else {                    isLeft = true;                    offsetLeftAndRight(-getLeft());                    invalidate();                }                if(getTop()<0){                    mDy += -getTop();                    offsetTopAndBottom(-getTop());                }                if(getBottom()>screenHeight){                    mDy += screenHeight-getBottom();                    offsetTopAndBottom(screenHeight-getBottom());                }                break;            }            case MotionEvent.ACTION_CANCEL: {                break;            }            case MotionEvent.ACTION_POINTER_UP: {                break;            }        }        return true;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        offsetTopAndBottom(mDy);        if(isLeft){            offsetLeftAndRight(-getLeft());        }else {            offsetLeftAndRight(screenWidth-getRight());        }    }}复制代码

转载于:https://juejin.im/post/5c3c5efd51882524b333af46

你可能感兴趣的文章
iOS - Regex 正则表达式
查看>>
第 68 章 Logical Volume Manager (LVM)
查看>>
膝盖中了一箭之康复篇-第八个月暨2月份目标总结
查看>>
IPA提交APPStore问题记录(一)
查看>>
有利于seo优化的网站地图不能取巧
查看>>
快照产品体验优化
查看>>
ASCII
查看>>
ibatis SqlMap not found
查看>>
Android SD卡创建文件和文件夹失败
查看>>
Ubuntu 14.04 vsftp refusing to run with writable root inside chroot问题解决方法
查看>>
Intellij IDEA远程调试tomcat
查看>>
hadoop的学习论坛
查看>>
Struts2 学习小结
查看>>
烂泥:wordpress迁移到docker
查看>>
.扒渣机的性能及优势 
查看>>
Linux下磁盘保留空间的调整,解决df看到的空间和实际磁盘大小不一致的问题
查看>>
RSA 生成公钥、私钥对
查看>>
C# ASP.NET 权限设计 完全支持多数据库多语言包的通用权限管理系统组件源码
查看>>
测试工具综合
查看>>
asp.net中调用COM组件发布IIS时常见错误 80070005解决方案
查看>>