ViewDragHelper自定义view保存view的位置

ViewDragHelper根据拖拽状态改变子view的背景这篇博客的需求相似,不过这里不是去修改view的背景,而是去保存view最后一次显示在界面上时的位置,以备下次进来时可以找到原来的位置。

先来说说现象吧:在不处理view的位置之前,你可能会遇到这样的情况,当view从前台跑到后台,再从后台跑到前台时,view的位置不再是这个activity显示的最终位置,而是view显示的初始位置,换句话说,view的位置已经跳回到最初的位置去了,于是,产品就出了需要保存view的位置的个需求。

保存位置最简单的方法就是调用view的layout(int l, int t, int r, int b)方法,这个方法必要的参数是view所在的四个顶点的位置,其中必不可少的是l,t两个参数,那么,这里我们就来保存l,t吧。思路很简单,在自定义view的onLayout(boolean changed, int left, int top, int right, int bottom)方法里面去重新layout view的位置就OK了。

在重新layout view的位置之前,我们需要去初始化view的位置,也就是view的最初位置,然后在执行onViewReleased(View releasedChild, float xvel, float yvel)的时候,去保存当前view的最终位置,最后在onLayout(boolean changed, int left, int top, int right, int bottom)中去重新设置view位置就OK了。

话不多说,直接上代码:

package com.baimi.wallet.ui.view;

import android.content.Context;
import android.graphics.Point;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

import com.baimi.wallet.R;

/**
 * Author: Morse
 * Time: 2016/12/12 09:34
 * Company:bmsh
 * Deprecated:可移动的view
 */
public class DragView extends FrameLayout {

    private ViewDragHelper mDragger;
    private ViewDragHelper.Callback callback;
    private View dragView;
    //是否移动Y轴
    private boolean isVerticalMove;
    private Point mAutoBackOriginPos = new Point();
    private boolean isLeft;
    private boolean isFirst = true;
    private int mLeft, mTop;

    public DragView(Context context) {
        this(context, null);
    }

    public DragView(Context context, AttributeSet attrs) {
        super(context, attrs);
        callback = new DraggerCallBack();
        //第二个参数就是滑动灵敏度的意思 可以随意设置
        mDragger = ViewDragHelper.create(this, 1.0f, callback);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        dragView = findViewById(R.id.tv_cash_back);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //决定是否拦截当前事件
        return mDragger.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //处理事件
        mDragger.processTouchEvent(event);
        return true;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (isFirst) {
            //初始化默认位置,只需要在第一次执行这个方法的时候去初始化
            mLeft = dragView.getLeft();
            mTop = dragView.getTop();
            isFirst = false;
        }
        //设置view的位置
        dragView.layout(mLeft, mTop, mLeft + dragView.getMeasuredWidth(), mTop + dragView.getMeasuredHeight());
    }


    /**
     * 滚动
     */
    @Override
    public void computeScroll() {
        if (mDragger.continueSettling(true)) {
            invalidate();
        }
    }

    public void showDragView() {
        if (View.VISIBLE != dragView.getVisibility()) {
            dragView.setVisibility(VISIBLE);
        }
    }

    public void hideDragView() {
        if (GONE != dragView.getVisibility()) {
            dragView.setVisibility(GONE);
        }
    }

    public void setVerticalMove(boolean verticalMove) {
        isVerticalMove = verticalMove;
    }

    class DraggerCallBack extends ViewDragHelper.Callback {

        //这个地方实际上函数返回值为true就代表可以滑动 为false 则不能滑动
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return true;
        }

        //这个地方实际上left就代表 你将要移动到的位置的坐标。返回值就是最终确定的移动的位置。
        // 我们要让view滑动的范围在我们的layout之内
        //实际上就是判断如果这个坐标在layout之内 那我们就返回这个坐标值。
        //如果这个坐标在layout的边界处 那我们就只能返回边界的坐标给他。不能让他超出这个范围
        //除此之外就是如果你的layout设置了padding的话,也可以让子view的活动范围在padding之内的.
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //取得左边界的坐标
            final int leftBound = getPaddingLeft();
            //取得右边界的坐标
            final int rightBound = getWidth() - child.getWidth() - leftBound;
            //这个地方的含义就是 如果left的值 在leftbound和rightBound之间 那么就返回left
            //如果left的值 比 leftbound还要小 那么就说明 超过了左边界 那我们只能返回给他左边界的值
            //如果left的值 比rightbound还要大 那么就说明 超过了右边界,那我们只能返回给他右边界的值
            return Math.min(Math.max(left, leftBound), rightBound);
        }

        //纵向的注释就不写了 自己体会
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            final int topBound = getPaddingTop();
            final int bottomBound = getHeight() - child.getHeight() - topBound;
            return Math.min(Math.max(top, topBound), bottomBound);
        }

        //需要子view的点击事件时添加,并且在xml文件中设置click属性为true
        @Override
        public int getViewHorizontalDragRange(View child) {
            return getMeasuredWidth() - child.getMeasuredWidth();
        }

        //需要子view的点击事件时添加,并且在xml文件中设置click属性为true
        @Override
        public int getViewVerticalDragRange(View child) {
            return getMeasuredHeight() - child.getMeasuredHeight();
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            //手指释放时可以自动回去,实现回弹需要配合computeScroll方法的配合才能生效
            //如果移动的位置大于屏幕的一半,那么就回到右侧,否则移动到左侧
            if (releasedChild == dragView) {
                isLeft = dragView.getLeft() + dragView.getWidth() / 2 - getMeasuredWidth() / 2 < 0;
                //记住释放之后view的最终位置
                mLeft = isLeft ? 0 : mAutoBackOriginPos.x;
                mTop = isVerticalMove ? dragView.getTop() : mAutoBackOriginPos.y;
                mDragger.settleCapturedViewAt(mLeft, mTop);
                dragView.setBackgroundResource(isLeft ? R.drawable.icon_cash_back_left : R.drawable.icon_cash_back);
                invalidate();
            }
        }
    }

}

希望博客对各位有所帮助,欢迎常来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值