oschina源码分析之侧滑菜单界面之可以拖动的ScrollView

来源:互联网 发布:淘宝店如何刷信誉 编辑:程序博客网 时间:2024/05/05 16:46

先上源码:

package net.oschina.app.widget;import android.annotation.SuppressLint;import android.content.Context;import android.graphics.Rect;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.animation.TranslateAnimation;import android.widget.ScrollView;/** * 可以拖动的ScrollView * */public class CustomerScrollView extends ScrollView {    private static final String TAG = "CustomerScrollView";    private static final int size = 4;    private View inner;    private float y;    private Rect normal = new Rect();    public CustomerScrollView(Context context) {        super(context);    }    public CustomerScrollView(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    protected void onFinishInflate() {        if (getChildCount() > 0) {            inner = getChildAt(0);        }    }    @SuppressLint("ClickableViewAccessibility")    @Override    public boolean onTouchEvent(MotionEvent ev) {        if (inner == null) {            return super.onTouchEvent(ev);        } else {            commOnTouchEvent(ev);        }        return super.onTouchEvent(ev);    }    public void commOnTouchEvent(MotionEvent ev) {        int action = ev.getAction();        switch (action) {        case MotionEvent.ACTION_DOWN:            y = ev.getY();            break;        case MotionEvent.ACTION_UP:            if (isNeedAnimation()) {                // Log.v("mlguitar", "will up and animation");                animation();            }            break;        case MotionEvent.ACTION_MOVE:            final float preY = y;            float nowY = ev.getY();            /**             * size=4 表示 拖动的距离为屏幕的高度的1/4             */            int deltaY = (int) (preY - nowY) / size;            Log.i(TAG, "commOnTouchEvent: deltay="+ deltaY +"preY" + preY +"nowY" + nowY );            // 滚动            // scrollBy(0, deltaY);            y = nowY;            if (isNeedMove()) {                Log.i(TAG, "commOnTouchEvent:  normal.isEmpty:"  + normal.isEmpty());                if (normal.isEmpty()) {                    Log.i(TAG, "commOnTouchEvent: inner:" + inner.getLeft() + "," + inner.getTop() + "," + inner.getRight() + "," + inner.getBottom());                    normal.set(inner.getLeft(), inner.getTop(),                            inner.getRight(), inner.getBottom());                    return;                }                int yy = inner.getTop() - deltaY;                Log.i(TAG, "commOnTouchEvent: new layout:" + inner.getLeft() + "," + yy + "," + inner.getRight() + "," + (inner.getBottom() - deltaY));                // 移动布局                inner.layout(inner.getLeft(), yy, inner.getRight(),                        inner.getBottom() - deltaY);            }            break;        default:            break;        }    }    public void animation() {        TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(),                normal.top);        ta.setDuration(200);        Log.i(TAG, "animation start " + "(0,0," + inner.getTop() +"," + normal.top + ")");        inner.startAnimation(ta);        inner.layout(normal.left, normal.top, normal.right, normal.bottom);        normal.setEmpty();    }    public boolean isNeedAnimation() {        Log.i(TAG, "isNeedAnimation: " + !normal.isEmpty());        return !normal.isEmpty();    }    public boolean isNeedMove() {        int offset = inner.getMeasuredHeight() - getHeight();        int scrollY = getScrollY();        Log.i(TAG, "isNeedMove: " + "scrollY=" + scrollY + "offset=" + offset);        if (scrollY == 0 || scrollY == offset) {            return true;        }        return false;    }}
  • 先看isNeedMove()函数:

    1. int offset = inner.getMeasuredHeight() - getHeight();

    前者一般与后者相等,只有当view超出屏幕后前者才会比后者大,getMeasuredHeight() 等于 getHeight()加上屏幕之外没有显示的大小。

    1. int scrollY = getScrollY();

    看下官方对这个函数的介绍:The top edge of the displayed part of your view, in pixels. 返回的是view的顶端超过屏幕部分的大小。

因此,这个函数的判断条件其实就是view在屏幕最上面或者最下面这两种情况才需要移动。

  • 再看移动事件(commOnTouchEvent):
    1. 点击获取初始位置,抬起判断是否需要移动,主要看一下移动这个事件(MotionEvent.ACTION_MOVE)。
    2. 首先根据移动的Y和初始的Y得到移动的距离:int deltaY = (int) (preY - nowY) / size;
    3. 判断是否需要移动。如果需要,第一次用normal这个变量来记录原始的view范围,然后用inner.layout(inner.getLeft(), yy, inner.getRight(),
      inner.getBottom() - deltaY);来移动布局。就是根据先前得到的移动距离来改变view的上下显示范围。
    4. 最后就是退回动画:TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(),
      normal.top);就是简单的由现在的Y变化到记录的原始的Y。
0 0