Android自定义View之布局(layout)缩放

需求:手指滑动布局放大缩小移动,其子控件也跟着放大缩小。
系统自带有放大缩小的控件:ZoomControls.控制一张图片的放大缩小很简单。但这不满足我们的需求。今天我们来完成对布局的放大缩小还有移动。先看效果图:
Android自定义View之布局(layout)缩放_第1张图片
Android自定义View之布局(layout)缩放_第2张图片

1,首先准备工作:导入jar包,并add.
这里写图片描述

2,自定义PowerFullLayout继承FrameLayout ,注释都有 :

package com.skyworth.publicdisplay.view;

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

import com.nineoldandroids.view.ViewHelper;

/**
 * ${DESC}
 * author: gyq
 * create at 2017/2/17 11:17
 */
public class PowerFullLayout extends FrameLayout {
    // 屏幕宽高
    private int screenHeight;
    private int screenWidth;
    private ViewDragHelper mDragHelper;
    private long lastMultiTouchTime;// 记录多点触控缩放后的时间
    private int originalWidth;// view宽度
    private int originalHeight;// view高度
    private ScaleGestureDetector mScaleGestureDetector = null;
    // private View view;
    private int downX;// 手指按下的x坐标值
    private int downY;// 手指按下的y坐标值
    private int left;// view的左坐标值
    private int top;// view的上坐标值
    private int right;// view的右坐标值
    private int bottom;// view的下坐标值
    private int newHeight;
    private int newWidth;

    public  boolean isScale = false;
    private float scale;
    private float preScale = 1;// 默认前一次缩放比例为1

    public PowerFullLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public PowerFullLayout(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

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

    private void init(Context context) {
        mDragHelper = ViewDragHelper.create(this, callback);
        mScaleGestureDetector = new ScaleGestureDetector(context,
                new ScaleGestureListener());

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        screenWidth = getMeasuredWidth();
        screenHeight = getMeasuredHeight();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        super.onInterceptTouchEvent(ev);

        return isScale;
    }


    private boolean needToHandle=true;
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int pointerCount = event.getPointerCount(); // 获得多少点
        if (pointerCount > 1) {// 多点触控,
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    needToHandle=true;
                    break;
                case MotionEvent.ACTION_MOVE:

                    break;
                case MotionEvent.ACTION_POINTER_2_UP://第二个手指抬起的时候
                    needToHandle=true;
                    break;

                default:
                    break;
            }
            return mScaleGestureDetector.onTouchEvent(event);//让mScaleGestureDetector处理触摸事件
        } else {
            long currentTimeMillis = System.currentTimeMillis();
            if (currentTimeMillis - lastMultiTouchTime > 200&&needToHandle) {
//                  多点触控全部手指抬起后要等待200毫秒才能执行单指触控的操作,避免多点触控后出现颤抖的情况
                try {
                    mDragHelper.processTouchEvent(event);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return true;
            }
//            }
        }
        return false;
    }

    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        /**
         * 用于判断是否捕获当前child的触摸事件
         *
         * @param child
         *            当前触摸的子view
         * @param pointerId
         * @return true就捕获并解析;false不捕获
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            if (preScale > 1){
                return true;
            }
            return false;
        }

        /**
         * 控制水平方向上的位置
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {

            if (left < (screenWidth - screenWidth * preScale) / 2)
                left = (int) (screenWidth - screenWidth * preScale) / 2;// 限制mainView可向左移动到的位置
            if (left > (screenWidth * preScale - screenWidth) / 2)
                left = (int) (screenWidth * preScale - screenWidth) / 2;// 限制mainView可向右移动到的位置
            return left;
        }

        public int clampViewPositionVertical(View child, int top, int dy) {

            if (top < (screenHeight - screenHeight * preScale) / 2) {
                top = (int) (screenHeight - screenHeight * preScale) / 2;// 限制mainView可向上移动到的位置
            }
            if (top > (screenHeight * preScale - screenHeight) / 2) {
                top = (int) (screenHeight * preScale - screenHeight) / 2;// 限制mainView可向上移动到的位置
            }
            return top;
        }

    };

    public class ScaleGestureListener implements
            ScaleGestureDetector.OnScaleGestureListener {

        @Override
        public boolean onScale(ScaleGestureDetector detector) {

            float previousSpan = detector.getPreviousSpan();// 前一次双指间距
            float currentSpan = detector.getCurrentSpan();// 本次双指间距
            if (currentSpan < previousSpan) {
                // 缩小
                scale = preScale - (previousSpan - currentSpan) / 1000;
            } else {
                // 放大
                scale = preScale + (currentSpan - previousSpan) / 1000;
            }
            // 缩放view
            if (scale > 0.5) {
                ViewHelper.setScaleX(PowerFullLayout.this, scale);// x方向上缩放
                ViewHelper.setScaleY(PowerFullLayout.this, scale);// y方向上缩放
            }
            return false;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            // 一定要返回true才会进入onScale()这个函数
            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            preScale = scale;// 记录本次缩放比例
            lastMultiTouchTime = System.currentTimeMillis();// 记录双指缩放后的时间
        }
    }
}

声明了一个变量:isScale,使用时要定义值。判断布局是否拦截此事件。

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        super.onInterceptTouchEvent(ev);

        return isScale;
    }

3,使用:项目中我使用了类似开关的一个imageview的点击事件,来判断是否拦截事件。看代码:

windowLyt = (PowerFullLayout) findViewById(R.id.view_content);
mScale = (ImageView)findViewById(R.id.iv_scale);
        mScale.setImageResource(R.drawable.scale_normal);
        mScale.setSelected(!mScale.isSelected());


...
//imageview的点击事件
case R.id.iv_scale :
                if (!mScale.isSelected()) {
                    windowLyt.isScale = false;
                    mScale.setImageResource(R.drawable.scale_normal);

                } else if (mScale.isSelected()) {
                    windowLyt.isScale = true;
                    mScale.setImageResource(R.drawable.scale_press);
                }
                mScale.setSelected(!mScale.isSelected());
                break;
...

4,布局文件:

"match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1"
                    android:orientation="vertical">


                <com.skyworth.publicdisplay.view.PowerFullLayout
                    android:id="@+id/view_content"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:paddingBottom="1px"
                    android:paddingLeft="2px"
                    android:paddingTop="1px" />
                

写博客的初心,其实就是记录一些在项目中使用到的一些技术,总结这些技术,方便今后阅读。

jar包下载地址:
http://download.csdn.net/download/duoduo_11011/9758507

你可能感兴趣的:(自定义View)