android 自定义view,缩放放大,平移边界检测

由于项目的需要,自定义个view,控制父控件,既可以缩放放大,滑动边界检测。自己实现了通过父控件就能操作子控件的自定义view,直接引用就可以使用。

下面是我实现的一个思路,贴了部分代码,大家可以参考。具体的核心代码我都上传到了我的github上了,大家可以下载试试,感觉好的话,大家给个star

demo下载

在MainActivity调用这个方法去初始化就可以了 :

		//parentView对应的是父控件,groupView对应的是子控件
        GestureViewManager bind = GestureViewManager.bind(this, groupView, parentView);

缩放放大手势

这块我主要是继承原生的ScaleGestureDetector并实现OnScaleGestureListener的事件去处理的。

  • 1、在缩放的同时获取detector.getScaleFactor()的值,由于控件大小不停在改变,所以该值会出现误差导致比例出现异常,因为缩放平移肯定需要有一个边界,所以目前我通过外面包裹ViewGroup,通过给ViewGruop设置手势的方式解决这个问题。因为外面的ViewGroup大小是不会改变的,所以系统计算的比例不会出现问题。有一个ViewGruop也符合我们的需求。
  • 2、如果View为可点击的View(Button)或我们给它设置了点击事件,这时View是没办法再监听缩放手势的,我们需要额外处理一下,在缩放手势执行前屏蔽点击事件,在结束后再设置允许点击。
  • 3、detector.getScaleFactor()得到的缩放比例都是相对于目前view的大小进行计算的,而我们缩放view时,view的比例都是从原始大小开始的,所以我们必须记录上次缩放的比例,下次开始缩放时,从上次的比例大小开始缩放。

上面的三点是我通过查资料总结的方案,下面是我实现的代码

/**
 * Created by wdh.
 * Description :放大缩放手势的监听器
 */

public class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener/*, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener */ {

    private View targetView;
    private float scale = 1;
    private float scaleTemp = 1;

    private boolean isFullGroup = false;

    ScaleGestureListener(View targetView, ViewGroup viewGroup) {
        this.targetView = targetView;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scale = scaleTemp * detector.getScaleFactor();
        targetView.setScaleX(scale);
        targetView.setScaleY(scale);
        return false;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        scaleTemp = scale;
    }



    float getScale() {
        return scale;
    }

    public boolean isFullGroup() {
        return isFullGroup;
    }

    void setFullGroup(boolean fullGroup) {
        isFullGroup = fullGroup;
    }

    void onActionUp() {
        if (isFullGroup && scaleTemp < 1) {
            scale = 1;
            targetView.setScaleX(scale);
            targetView.setScaleY(scale);
            scaleTemp = scale;
        }
    }
}

到这里就基本实现了缩放和放大的功能,很简单。

2、平移的手势以及移动边界计算

这块我也是用的原生的控件去处理的GestureDetector,并继承SimpleOnGestureListener的监听事件

  • view大小是不能小于viewGroup的大小的,小于group会回弹到充满viewGroup(后面改成了可选设置)。
  • view小于group大小时需要居中显示。并且缩放时,如果view不在中心,需要虽然比例慢慢回到中心。
  • 放大平移时,不能移出group边界。
  • 滑动和缩放的手势会起冲突,无法同时监听。所以在控件的onTouchEvent方法中,我们需要自己处理一下,我这里是通过判断按在屏幕上的手指数量,来返回不同手势回调的。一根手指按下,则为滑动手势,两根手指,则为缩放手势。
  • 接下来就是计算边界,我们先不考虑缩放的情况,只考虑控件本身可移动的大小。能够移动的距离分别为控件距左、上、右、下的距离。也就是,如果控件向左移动,则判断控件距离左边的距离是否大于0,如果大于0,才允许向左移动。以此类推上、右和下。如下图
    在这里插入图片描述
    通过上面的需求,下面是我的主要代码处理
/**
 * Description :滑动手势的监听器
 */

public class ScrollGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

        distanceX = -distanceX;
        distanceY = -distanceY;

        if (isFullGroup || scale > 1) {
            if (viewWidthReal > groupWidth) {
                translationXOnScrollEvent(distanceX);
            }
            if (viewHeightReal > groupHeight) {
                translationYOnScrollEvent(distanceY);
            }
        } else {
            translationXOnScrollEvent(distanceX);
            translationYOnScrollEvent(distanceY);
        }

        return super.onScroll(e1, e2, distanceX, distanceY);
    }

    private void translationXOnScrollEvent(float distanceX) {
        //最大移动距离全部为正数,所以需要通过判断distanceX的正负,来判断是向左移动还是向右移动,
        // 然后通过取distanceX的绝对值来和相应移动方向的最大移动距离比较
        if ((distanceX < 0 && Math.abs(distanceXTemp + distanceX) < maxTranslationLeft)
                || (distanceX > 0 && distanceXTemp + distanceX < maxTranslationRight)) {
            distanceXTemp += distanceX;
            targetView.setTranslationX(distanceXTemp);
            //如果超出边界,就移动到最大距离,防止边界有剩余量
        } else if ((distanceX < 0 && Math.abs(distanceXTemp + distanceX) > maxTranslationLeft)) {
            distanceXTemp = -maxTranslationLeft;
            targetView.setTranslationX(-maxTranslationLeft);
        } else if ((distanceX > 0 && distanceXTemp + distanceX > maxTranslationRight)) {
            distanceXTemp = maxTranslationRight;
            targetView.setTranslationX(maxTranslationRight);
        }
    }

    private void translationYOnScrollEvent(float distanceY) {
        if ((distanceY < 0 && Math.abs(distanceYTemp + distanceY) < maxTranslationTop)
                || (distanceY > 0 && distanceYTemp + distanceY < maxTranslationBottom)) {
            distanceYTemp += distanceY;
            targetView.setTranslationY(distanceYTemp);
            //如果超出边界,就移动到最大距离,防止边界有剩余量
        } else if ((distanceY < 0 && Math.abs(distanceYTemp + distanceY) > maxTranslationTop)) {
            distanceYTemp = -maxTranslationTop;
            targetView.setTranslationY(-maxTranslationTop);
        } else if ((distanceY > 0 && distanceYTemp + distanceY > maxTranslationBottom)) {
            distanceYTemp = maxTranslationBottom;
            targetView.setTranslationY(maxTranslationBottom);
        }
    }

猜你喜欢

转载自blog.csdn.net/wang852575989/article/details/88304001
今日推荐