android通用缩放滚动容器——ZoomScrollLayout

ZoomScrollLayout是一个可以对任意子view进行缩放、滚动查看的控件。类似于图片查看器,支持任何类型的view。

先看效果:


222.gif

上代码:


import android.content.Context;

import android.util.AttributeSet;

import android.view.*;

import android.widget.RelativeLayout;

import com.orhanobut.logger.Logger;

/**

* Created by wg on 2017/4/21.

*/

public class ZoomScrollLayout extends RelativeLayout implements ScaleGestureDetector.OnScaleGestureListener {

    private ScaleGestureDetector mScaleDetector;

    private GestureDetector mGestureDetector;

    private static final float MIN_ZOOM = 0.3f;

    private static final float MAX_ZOOM = 3.0f;

    private Integer mLeft, mTop, mRight, mBottom;

    private int centerX, centerY;

    private float mLastScale = 1.0f;

    private float totleScale = 1.0f;

    // childview

    private View mChildView;

    // 拦截滑动事件

    float mDistansX, mDistansY, mTouchSlop;

    private enum MODE {

        ZOOM, DRAG, NONE

    }

    private MODE mode;

    boolean touchDown;

    public ZoomScrollLayout(Context context) {

        super(context);

        init(context);

    }

    public ZoomScrollLayout(Context context, AttributeSet attrs) {

        super(context, attrs);

        init(context);

    }

    public ZoomScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);

        init(context);

    }

    public void init(Context context) {

        mScaleDetector = new ScaleGestureDetector(context, this);

        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {

            @Override

            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

                if (mode == MODE.DRAG) {

                    if (mChildView == null) {

                        mChildView = getChildAt(0);

                        centerX = getWidth() / 2;

                        centerY = getHeight() / 2;

                    }

                    if (mLeft == null) {

                        mLeft = mChildView.getLeft();

                        mTop = mChildView.getTop();

                        mRight = mChildView.getRight();

                        mBottom = mChildView.getBottom();

                    }

                    // 防抖动

                    if (touchDown) {

                        touchDown = false;

                        return true;

                    }

                    Logger.i("distanceX=" + distanceX + ";distanceY=" + distanceY);

                    Logger.i("mLeft=" + mLeft + ";mTop=" + mTop);

                    mLeft = mLeft - (int) distanceX;

                    mTop = mTop - (int) distanceY;

                    mRight = mRight - (int) distanceX;

                    mBottom = mBottom - (int) distanceY;

                    mChildView.layout(mLeft, mTop, mRight, mBottom);

                }

                return true;

            }

            @Override

            public boolean onDown(MotionEvent e) {

                touchDown = true;

                return super.onDown(e);

            }

        });

        // 系统最小滑动距离

        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

    }

    @Override

    public boolean onInterceptTouchEvent(MotionEvent e) {

        int action = e.getActionMasked();

        int currentX = (int) e.getX();

        int currentY = (int) e.getY();

        switch (action) {

            case MotionEvent.ACTION_DOWN:

                //记录上次滑动的位置

                mDistansX = currentX;

                mDistansY = currentY;

                //将当前的坐标保存为起始点

                mode = MODE.DRAG;

                break;

            case MotionEvent.ACTION_MOVE:

                if (Math.abs(mDistansX - currentX) >= mTouchSlop || Math.abs(mDistansY - currentY) >= mTouchSlop) { //父容器拦截

                    return true;

                }

                break;

            //指点杆保持按下,并且进行位移

            //有手指抬起,将模式设为NONE

            case MotionEvent.ACTION_UP:

            case MotionEvent.ACTION_POINTER_UP:

                mode = MODE.NONE;

                break;

        }

        return super.onInterceptTouchEvent(e);

    }

    @Override

    public boolean onTouchEvent(MotionEvent event) {

        mScaleDetector.onTouchEvent(event);

        mGestureDetector.onTouchEvent(event);

        return true;

    }

    @Override

    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

        Logger.i("MotionEvent.onScale");

        if (mode == MODE.ZOOM) {

            float scaleFactor = scaleGestureDetector.getScaleFactor();

            float tempScale = mLastScale * scaleFactor;

            if (tempScale <= MAX_ZOOM && tempScale >= MIN_ZOOM) {

                totleScale = tempScale;

                applyScale(totleScale);

            }

        }

        return false;

    }

    /**

    * 执行缩放操作

    */

    public void applyScale(float scale) {

        mChildView.setScaleX(scale);

        mChildView.setScaleY(scale);

    }

    @Override

    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {

        Logger.i("MotionEvent.onScaleBegin");

        mode = MODE.ZOOM;

        if (mode == MODE.ZOOM) {

            if (mChildView == null) {

                mChildView = getChildAt(0);

                centerX = getWidth() / 2;

                centerY = getHeight() / 2;

            }

            mLeft = mChildView.getLeft();

            mTop = mChildView.getTop();

            mRight = mChildView.getRight();

            mBottom = mChildView.getBottom();

        }

        return true;

    }

    @Override

    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

        mLastScale = totleScale;

    }

}

注意:ZoomScrollLayout 和ScrollView一样,只能有一个子View

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358