工作了几年,最开始做的是安卓开发,后面的做了一段时间逆向和sdk开发,一直没有系统的整理自己的知识,打算从本篇博客开始,陆续复习并记录一下自己的安卓知识

一直不知道怎么排版,先凑合着弄下

实现效果,gif上传被压扁了

 

ViewDragHelper的用法

viewDragHelper是一个安卓自带的处理拖拽的工具

先看一下viewDragHelper的创建步骤

 public static ViewDragHelper create(@NonNull ViewGroup forParent, @NonNull ViewDragHelper.Callback cb)

ViewGroup传的就是需要操作的View容器,一般我们把代码写在自定义View内,这里也就直接传this

ViewDragHelper.Callback 这个是处理拖动逻辑的核心模块,具体的方法有

public boolean tryCaptureView(@NonNull View view, int i)

这是判定规则,只有return true的时候才会去执行后续的拖动操作

这里的view是容器内被touch到的字view,只有这个view和我们需要拖动的view为同一个的时候我们才认为是匹配的

也就是 return  myView == view;

public int getViewHorizontalDragRange(@NonNull View child);public int getViewVerticalDragRange(@NonNull View child);

容器内可以拖动的区间,只有大于0的时候才可以执行相应方向的操作,一般没有特殊要求,我们会把这个返回值设为当期容器的宽和高

 public int clampViewPositionHorizontal(@NonNull View child, int left, int dx); public int clampViewPositionVertical(@NonNull View child, int top, int dy);

这个返回的是被操作的view在横向或纵向所能滑出的最大距离,或者说,在x或y方向的最左或最右,最上或最下所能达到的位置,这个可以有正负,比如x方向负就直接超出左边屏幕了哈

 public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel);

这个是view被释放时候执行的操作,我们可以操作view回到指定的位置或者保持不变等

在介绍ViewDragHelper的部分方法

public boolean settleCapturedViewAt(int finalLeft, int finalTop)

把我们所操作的view平滑的滑动到指定的位置

public boolean smoothSlideViewTo(@NonNull View child, int finalLeft, int finalTop)

和上面的settleCapturedViewAt效果类似,这个可以传被滑动的指定view

但是单纯用这两个方法会发现view并没有变动,这就需要搭配下面的方法一起使用

public boolean continueSettling(boolean deferCallbacks);public boolean shouldInterceptTouchEvent(@NonNull MotionEvent ev);public void processTouchEvent(@NonNull MotionEvent ev);

拖动处理三件套,需要让viewDragHelper处理手势和scroller圆滑过渡

实现

首先我们定义一些扩展的变量

private View dragView;                //被拖拽的viewprivate ViewDragHelper viewDragHelper;private int mWidth;                 //容器的宽度private int mHeight;                //容器的高度private int mChildWidth;            //拖拽的View宽度private int mChildHeight;           //拖拽的View高度private boolean onDrag = true;      //是否正在被拖拽private boolean dragEnable = true;  //是否是可以拖拽的private boolean sideEnable = true;  //是否吸边private final int NONE = -1;private int topFinalOffset;        //拖拽超出上边界后,释放后回到的top位置private int bottomFinalOffset;     //拖拽超出下边界后,释放后回到的bottom位置private int leftFinalOffset;       //拖拽超出左边界后,释放后回到的left位置private int rightFinalOffset;      //拖拽超出右边界后,释放后回到的right位置    private int leftDragOffset = NONE;            //能向左拖拽的最大距离private int rightDragOffset = NONE;            //能向右拖拽的最大距离private int topDragOffset = NONE;             //能向上拖拽的最大距离private int bottomDragOffset = NONE;             //能向下拖拽的最大距离

先初始化并获取一些参数

private void init() {        viewDragHelper = ViewDragHelper.create(this, new MyDragCallBack());} @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        //获取装载容器的宽高以及拖拽view的宽高        mWidth = getMeasuredWidth();        mHeight = getMeasuredHeight();        mChildHeight = dragView.getMeasuredHeight();        mChildWidth = dragView.getMeasuredWidth();        //默认最多可以拖拽1/2的view出屏幕        leftDragOffset = leftDragOffset == NONE ? mChildWidth / 2 : leftDragOffset;        rightDragOffset = rightDragOffset == NONE ? mChildWidth / 2 : rightDragOffset;        topDragOffset = topDragOffset == NONE ? mChildHeight / 2 : topDragOffset;        bottomDragOffset = bottomDragOffset == NONE ? mChildHeight / 2 : bottomDragOffset;    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        if (lastChildX == 0 && lastChildY == 0) {            calLayoutOffset();        }        //把view布局到相应的位置,当然第一次就是在左上角,后续位置会发生变化        dragView.layout(lastChildX, lastChildY, lastChildX + mChildWidth, lastChildY + mChildHeight);    }    public void calLayoutOffset() {        //把x,y初始化设置为最终要停留在左上角的位置        lastChildX =leftFinalOffset;        lastChildY =topFinalOffset;    }@Override    protected void onFinishInflate() {        super.onFinishInflate();        if (getChildCount() != 1) {            throw new RuntimeException("child size must be 1");        }        dragView = getChildAt(0);        dragView.bringToFront();    }
 private Rect mRect = new Rect();    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        if (dragEnable) {            switch (ev.getAction()) {                case MotionEvent.ACTION_DOWN:                    int x = (int) ev.getX();                    int y = (int) ev.getY();                    dragView.getHitRect(mRect);                    onDrag = mRect.contains(x, y);                    //如果按下的点在dragView内,则认为是拖动有效,执行viewDragHelper的方法                    break;            }            if (onDrag) return viewDragHelper.shouldInterceptTouchEvent(ev);        }        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        if (dragEnable) {            if (onDrag) {                viewDragHelper.processTouchEvent(event);                return true;            }        }        return super.onTouchEvent(event);    }    @Override    public void computeScroll() {        if (dragEnable) {            if (viewDragHelper.continueSettling(true)) {                invalidate();            }        }    }    public static int dp2px(Context context, float dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                d

准备做完了,那么就要进行操作的代码了

 private class MyDragCallBack extends ViewDragHelper.Callback {        @Override        public boolean tryCaptureView(@NonNull View view, int i) {            return dragView == view;        }        //以横向拖动为例        //left是当前拖动view的左边的坐标        //我们要做的就是让 left >= 最左的距离  同时  left <= 最右的距离        //就是我们设置的leftDragOffset 和 rightDragOffset         @Override        public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {            leftDragOffset = leftDragOffset > mChildWidth ? mChildWidth : leftDragOffset;            rightDragOffset = rightDragOffset > mChildWidth ? mChildWidth : rightDragOffset;            return clamp(left, -leftDragOffset, mWidth - mChildWidth + rightDragOffset);        }        @Override        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {            topDragOffset = topDragOffset > mChildHeight ? mChildHeight : topDragOffset;            bottomDragOffset = bottomDragOffset > mChildHeight ? mChildHeight : bottomDragOffset;            return clamp(top, -topDragOffset, mHeight - mChildHeight + bottomDragOffset);        }        @Override        public int getViewVerticalDragRange(@NonNull View child) {//            return super.getViewVerticalDragRange(child);            return mHeight;        }        @Override        public int getViewHorizontalDragRange(@NonNull View child) {//            return super.getViewHorizontalDragRange(child);            return mWidth;        }        @Override        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {            if (sideEnable) {                super.onViewReleased(releasedChild, xvel, yvel);            //如果top小于topFinOffset则取topFinalOffset            //如果bottom大于最大的offset则取限制的最大bottom                int finalTop = dragView.getTop() <= topFinalOffset ? topFinalOffset : dragView.getBottom() >= mHeight - bottomFinalOffset ? mHeight - dragView.getMeasuredHeight() - bottomFinalOffset : dragView.getTop();                lastChildY = finalTop;                //根据left和view的一半进行界定,选择是最终停留在左边还是右边                if (Math.abs(dragView.getLeft()) <= (getMeasuredWidth() - dragView.getMeasuredWidth()) / 2) {                    lastChildX = leftFinalOffset;                    //平滑过渡到相应位置                    viewDragHelper.settleCapturedViewAt(lastChildX, finalTop);                } else {                    lastChildX = getMeasuredWidth() - dragView.getMeasuredWidth() - rightFinalOffset;                    viewDragHelper.settleCapturedViewAt(lastChildX,                            finalTop);                }                invalidate();            } else {                lastChildX = dragView.getLeft();                lastChildY = dragView.getTop();            }        //把拖拽的标记定为false            onDrag = false;        }    }    //其实就是 当前和最大值取最小 同时和最小值取最大    //如果value在两者之间直接返回value    //如果value比最小值小,则返回min    //如果value比最大值大,则返回max  所以是满足我们的条件的    private int clamp(int value, int min, int max) {        return Math.max(min, Math.min(max, value));    }

然后建立布局文件

<?xml version="1.0" encoding="utf-8"?>                

引用代码

 @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        floatLayout = findViewById(R.id.layout);        mView = findViewById(R.id.view);        floatLayout.enableDrag(true);        floatLayout.enableSide(true);        //设置最大可拖拽的偏移量        floatLayout.setFinalDragOffsets(80,80,80,80);        //设置最终停留的位置偏移        floatLayout.setFinalOffsets(-50);        floatLayout.requestLayout();        floatLayout.invalidate();        mView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(FloatLaytoutActivity.this,"view被点击了",Toast.LENGTH_LONG).show();            }        });    }

完整的代码

public class FloatLayout extends FrameLayout {    private final int NONE = -1;    private View dragView;    private ViewDragHelper viewDragHelper;    private int mWidth;    private int mHeight;    private int mChildWidth;    private int mChildHeight;    private boolean onDrag = true;    private boolean dragEnable = true;    private boolean sideEnable = true;  //是否吸边    private int lastChildX;    private int lastChildY;    private int topFinalOffset;    private int bottomFinalOffset;    private int leftFinalOffset;    private int rightFinalOffset;    private int leftDragOffset = NONE;    private int rightDragOffset = NONE;    private int topDragOffset = NONE;    private int bottomDragOffset = NONE;    public FloatLayout(@NonNull Context context) {        this(context, null);    }    public FloatLayout(@NonNull Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public FloatLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        viewDragHelper = ViewDragHelper.create(this, new MyDragCallBack());    }    public void setBottomDragOffset(int dpValue) {        this.bottomDragOffset = dp2px(getContext(), dpValue);    }    public void setTopDragOffset(int dpValue) {        this.topDragOffset = dp2px(getContext(), dpValue);    }    public void setLeftDragOffset(int dpValue) {        this.leftDragOffset = dp2px(getContext(), dpValue);    }    public void setRightDragOffset(int dpValue) {        this.rightDragOffset = dp2px(getContext(), dpValue);    }    public void setFinalOffsets(int value) {        setFinalOffsets(value, value, value, value);    }    //拖拽能偏移出父容器的值,取正数    public void setFinalDragOffsets(int left, int top, int right, int bottom) {        setLeftDragOffset(left);        setTopDragOffset(top);        setRightDragOffset(right);        setBottomDragOffset(bottom);    }    public void setFinalOffsets(int left, int top, int right, int bottom) {        setLeftFinalOffset(left);        setTopFinalOffset(top);        setRightFinalOffset(right);        setBottomFinalOffset(bottom);//        calLayoutOffset();    }    public void setLeftFinalOffset(int dpValue) {        this.leftFinalOffset = dp2px(getContext(), dpValue);    }    public void setRightFinalOffset(int dpValue) {        this.rightFinalOffset = dp2px(getContext(), dpValue);    }    public void setBottomFinalOffset(int dpValue) {        this.bottomFinalOffset = dp2px(getContext(), dpValue);    }    public void setTopFinalOffset(int dpValue) {        this.topFinalOffset = dp2px(getContext(), dpValue);    }    public void enableDrag(boolean value) {        dragEnable = value;    }    public void enableSide(boolean value) {        sideEnable = value;    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mWidth = getMeasuredWidth();        mHeight = getMeasuredHeight();        mChildHeight = dragView.getMeasuredHeight();        mChildWidth = dragView.getMeasuredWidth();        leftDragOffset = leftDragOffset == NONE ? mChildWidth / 2 : leftDragOffset;        rightDragOffset = rightDragOffset == NONE ? mChildWidth / 2 : rightDragOffset;        topDragOffset = topDragOffset == NONE ? mChildHeight / 2 : topDragOffset;        bottomDragOffset = bottomDragOffset == NONE ? mChildHeight / 2 : bottomDragOffset;    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        if (lastChildX == 0 && lastChildY == 0) {            calLayoutOffset();        }        dragView.layout(lastChildX, lastChildY, lastChildX + mChildWidth, lastChildY + mChildHeight);    }    public void calLayoutOffset() {        lastChildX =leftFinalOffset;        lastChildY =topFinalOffset;    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        if (getChildCount() != 1) {            throw new RuntimeException("child size must be 1");        }        dragView = getChildAt(0);        dragView.bringToFront();    }    private class MyDragCallBack extends ViewDragHelper.Callback {        @Override        public boolean tryCaptureView(@NonNull View view, int i) {            return dragView == view;        }        @Override        public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {            leftDragOffset = leftDragOffset > mChildWidth ? mChildWidth : leftDragOffset;            rightDragOffset = rightDragOffset > mChildWidth ? mChildWidth : rightDragOffset;            return clamp(left, -leftDragOffset, mWidth - mChildWidth + rightDragOffset);        }        @Override        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {            topDragOffset = topDragOffset > mChildHeight ? mChildHeight : topDragOffset;            bottomDragOffset = bottomDragOffset > mChildHeight ? mChildHeight : bottomDragOffset;            return clamp(top, -topDragOffset, mHeight - mChildHeight + bottomDragOffset);        }        @Override        public int getViewVerticalDragRange(@NonNull View child) {//            return super.getViewVerticalDragRange(child);            return mHeight;        }        @Override        public int getViewHorizontalDragRange(@NonNull View child) {//            return super.getViewHorizontalDragRange(child);            return mWidth;        }        @Override        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {            if (sideEnable) {                super.onViewReleased(releasedChild, xvel, yvel);                int finalTop = dragView.getTop() <= topFinalOffset ? topFinalOffset : dragView.getBottom() >= mHeight - bottomFinalOffset ? mHeight - dragView.getMeasuredHeight() - bottomFinalOffset : dragView.getTop();                lastChildY = finalTop;                if (Math.abs(dragView.getLeft()) <= (getMeasuredWidth() - dragView.getMeasuredWidth()) / 2) {                    lastChildX = leftFinalOffset;                    viewDragHelper.settleCapturedViewAt(lastChildX, finalTop);                } else {                    lastChildX = getMeasuredWidth() - dragView.getMeasuredWidth() - rightFinalOffset;                    viewDragHelper.settleCapturedViewAt(lastChildX,                            finalTop);                }                invalidate();            } else {                lastChildX = dragView.getLeft();                lastChildY = dragView.getTop();            }            onDrag = false;        }    }    private int clamp(int value, int min, int max) {        return Math.max(min, Math.min(max, value));    }    private Rect mRect = new Rect();    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        if (dragEnable) {            switch (ev.getAction()) {                case MotionEvent.ACTION_DOWN:                    int x = (int) ev.getX();                    int y = (int) ev.getY();                    dragView.getHitRect(mRect);                    onDrag = mRect.contains(x, y);                    break;            }            if (onDrag) return viewDragHelper.shouldInterceptTouchEvent(ev);        }        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        if (dragEnable) {            if (onDrag) {                viewDragHelper.processTouchEvent(event);                return true;            }        }        return super.onTouchEvent(event);    }    @Override    public void computeScroll() {        if (dragEnable) {            if (viewDragHelper.continueSettling(true)) {                invalidate();            }        }    }    public static int dp2px(Context context, float dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dpVal, context.getResources().getDisplayMetrics());    }}

附上demo地址 https://github.com/gouptosee/ViewDragHelperDemo

 

更多相关文章

  1. 图解Android(安卓)View的scrollTo(),scrollBy(),getScrollX(), g
  2. [android] 百度地图开发 (三).定位当前位置及getLastKnownLocati
  3. android中引用项目工程中的sqlite文件
  4. Android的PopupWindow的使用,根据点击位置显示弹窗
  5. [置顶] AndroidUI组件4- ProgressBar、SeekBar、ImageView、TabH
  6. 使用Android(安卓)RatingBar时踩过的坑
  7. 【Android】高仿大众点评中的范围选择控件之RangeSeekBar
  8. Android(安卓)特色开发,基于位置的服务
  9. Android实现贪吃蛇游戏一:游戏界面及控制

随机推荐

  1. 初级——程序如何打包成apk文件
  2. Android(安卓)图片Bitmap保存到内存卡
  3. Android图文混排ImageSpan居中,以及设置间
  4. java/android 设计模式学习笔记(6)---适配
  5. Android用GridLayout网格布局实现简单的
  6. Android软件自动更新升级(自动下载安装新
  7. 【Android开发】基本组件-复选框
  8. Android(安卓)RecyclerView 监听滑动
  9. Android(安卓)Fragments 详细使用
  10. android 学习之图像处理系统(一)