android通用縮放滾動(dòng)容器——ZoomScrollLayout

ZoomScrollLayout是一個(gè)可以對(duì)任意子view進(jìn)行縮放怨绣、滾動(dòng)查看的控件角溃。類似于圖片查看器,支持任何類型的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;

    // 攔截滑動(dòng)事件

    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();

                    }

                    // 防抖動(dòng)

                    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);

            }

        });

        // 系統(tǒng)最小滑動(dòng)距離

        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:

                //記錄上次滑動(dòng)的位置

                mDistansX = currentX;

                mDistansY = currentY;

                //將當(dāng)前的坐標(biāo)保存為起始點(diǎn)

                mode = MODE.DRAG;

                break;

            case MotionEvent.ACTION_MOVE:

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

                    return true;

                }

                break;

            //指點(diǎn)桿保持按下减细,并且進(jìn)行位移

            //有手指抬起,將模式設(shè)為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;

    }

    /**

    * 執(zhí)行縮放操作

    */

    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一樣赢笨,只能有一個(gè)子View

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末邪财,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子质欲,更是在濱河造成了極大的恐慌树埠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘶伟,死亡現(xiàn)場離奇詭異怎憋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)九昧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門绊袋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铸鹰,你說我怎么就攤上這事癌别。” “怎么了蹋笼?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵展姐,是天一觀的道長躁垛。 經(jīng)常有香客問我,道長圾笨,這世上最難降的妖魔是什么教馆? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮擂达,結(jié)果婚禮上土铺,老公的妹妹穿的比我還像新娘。我一直安慰自己板鬓,他們只是感情好悲敷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著俭令,像睡著了一般镀迂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唤蔗,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音窟赏,去河邊找鬼妓柜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涯穷,可吹牛的內(nèi)容都是我干的棍掐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拷况,長吁一口氣:“原來是場噩夢啊……” “哼作煌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赚瘦,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤粟誓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后起意,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹰服,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年揽咕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悲酷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡亲善,死狀恐怖设易,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛹头,我是刑警寧澤顿肺,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布戏溺,位于F島的核電站,受9級(jí)特大地震影響挟冠,放射性物質(zhì)發(fā)生泄漏于购。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一知染、第九天 我趴在偏房一處隱蔽的房頂上張望肋僧。 院中可真熱鬧,春花似錦控淡、人聲如沸嫌吠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辫诅。三九已至,卻和暖如春涧狮,著一層夾襖步出監(jiān)牢的瞬間炕矮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工者冤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肤视,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓涉枫,卻偏偏與公主長得像邢滑,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355