博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

019、ViewGroup、Scroller与VelocityTracker

Posted on 2013-09-27 21:48  mz_zyh  阅读(465)  评论(0编辑  收藏  举报
范例,运用ViewGroup、Scroller、VelocityTracker所创建的MyViewGroup类。ViewGroup与Scroller类为建置View Layout与移动View的相关类,VelocityTracker则是用以追踪User在触控屏幕时的滑动速度。
关于MyViewGroup类:
有两个构造方法:1、在程序里配置之用;2、在Layout里配置ViewGroup时指派属性只用。
onInterceptTouchEvent()方法可以拦截触摸事件
MyViewGroup类的代码如下:
public class MyViewGroup extends ViewGroup {
    private Scroller mScroller;
    private int mScaledTouchSlop = 0;
    private int mCurrentLayoutFlag = 0;// 当前显示页的标识
    private int mScrollingX = 0;
    private static final int FLING_VELOCITY = 1000;// 滑动时的比较数值
    private static final int REST_STATE = 1;
    private static final int SCROLLING_STATE = 2;
    private static final String TAG = "MyViewGroup";
    private int mTouchState = REST_STATE;// 触摸状态标识
    private float mLastMovingX;// 记录按下时X的坐标
    private VelocityTracker mVelocityTracker;
    public MyViewGroup(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        mScroller = new Scroller(context);
        // 在User触控滑动前预测移动位移
        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        // 配置ViewGroup的宽为WRAP_CONTENT,高为MATCH_PARENT
        MyViewGroup.this.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
    }
    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        mScroller = new Scroller(context);
        // 在用户触控之前预测移动位移
        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        // 配置ViewGroup的宽为WRAP_CONTENT,高为MATCH_PARENT
        MyViewGroup.this.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
                R.styleable.SlideStyledAttributes);
        mCurrentLayoutFlag = mTypedArray.getInteger(
                R.styleable.SlideStyledAttributes_view_screen, 0);
    }
    /**
     * 复写onMeasure方法,并判断所在Layout Flag
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("error mode.");
        }
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("error mode.");
        }
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
        scrollTo(mCurrentLayoutFlag * width, 0);
    }
    /**
     * 继承在ViewGroup必须重写的onLayout()方法
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
        int childLeft = 0;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                int childWidth = child.getMeasuredWidth();
                child.layout(childLeft, 0, childLeft + childWidth,
                        child.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }
    /**
     * 复写computeScroll()方法告诉View已更新
     */
    @Override
    public void computeScroll() {
        // TODO Auto-generated method stub
        if (mScroller.computeScrollOffset()) {
            // 取得目前Scroller的X offset
            mScrollingX = mScroller.getCurrX();
            // 移动至scroll移动的position
            scrollTo(mScrollingX, 0);
            // 调用invalidate()方法处理来自non-UI thread的移动请求。
            postInvalidate();
        }
    }
    /**
     * 触摸拦截,每次触摸都会先调用该方法,返回true,则不在进行往下传,这后面的触摸处理事件不能被激发
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 实现onInterceptTouchEvent方法拦截User手指触控屏幕移动事件
        if ((ev.getAction() == MotionEvent.ACTION_MOVE)
                && (mTouchState != REST_STATE)) {
            return true;
        }
        switch (ev.getAction()) {
        // 按住触控屏幕事件开始
        case MotionEvent.ACTION_DOWN:
            // 记录按下的X坐标
            mLastMovingX = ev.getX();
            mTouchState = mScroller.isFinished() ? REST_STATE : SCROLLING_STATE;
            break;
        // 按住触控屏幕其移动事件
        case MotionEvent.ACTION_MOVE:
            // 判断ACTION_MOVE事件间的移动X坐标间距
            int intShiftX = (int) Math.abs(ev.getX() - mLastMovingX);
            if (intShiftX > mScaledTouchSlop) {
                mTouchState = SCROLLING_STATE;
            }
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            // 手指离开屏幕
            mTouchState = REST_STATE;
            break;
        }
        return mTouchState != REST_STATE;
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mVelocityTracker == null) {
            // 实现flinging事件,通过obtain()取得新的tracking实例
            mVelocityTracker = VelocityTracker.obtain();
        }
        // 将User触控的MotionEvent加入Tracker
        mVelocityTracker.addMovement(event);
        // 判断user的onTouchEvent屏幕触控事件
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 判断滑动事件是否完成,并停止滑动动画
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            }
            // 当手指按下屏幕触发事件时,记录X的坐标
            mLastMovingX = event.getX();
            break;
        case MotionEvent.ACTION_UP:
            // 当手指离开屏幕时,记录下mVelocityTracker的记录,并取得X轴滑动速度
            VelocityTracker velocityTracker = mVelocityTracker;
            velocityTracker.computeCurrentVelocity(1000);
            float xVelocity = velocityTracker.getXVelocity();
            // 当X轴滑动速度大于1000,且mCurrentLayoutFlag>0
            if (xVelocity > FLING_VELOCITY && mCurrentLayoutFlag > 0) {
                // 向左移动画面
                snapToScreen(mCurrentLayoutFlag - 1);
            } else if (xVelocity < -FLING_VELOCITY
                    && mCurrentLayoutFlag < getChildCount() - 1) {
                // 向右移动画面
                snapToScreen(mCurrentLayoutFlag + 1);
            } else {
                snapToDestination();
            }
            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
            mTouchState = REST_STATE;
            break;
        case MotionEvent.ACTION_CANCEL:
            mTouchState = REST_STATE;
            break;
        }
        mScrollingX = MyViewGroup.this.getScrollX();
        return true;
    }
    /**
     * 当不需要滑动时,会调用该方法
     */
    private void snapToDestination() {
        int screenWidth = getWidth();
        int whichScreen = (mScrollingX + (screenWidth / 2)) / screenWidth;
        snapToScreen(whichScreen);
    }
    /**
     * 切换界面时调用的方法
     * 
     * @param whichScreen
     *            需要移至的目标界面的flag
     */
    private void snapToScreen(int whichScreen) {
        // TODO Auto-generated method stub
        mCurrentLayoutFlag = whichScreen;
        int newX = whichScreen * getWidth();
        int delta = newX - mScrollingX;
        mScroller.startScroll(mScrollingX, 0, delta, 0, Math.abs(delta) * 2);
        // 静止重绘View的画面
        invalidate();
    }
}

 

在使用这个自定义的控件时,布局文件里面的定义如下:

 

    <com.example.ex_4_33_myviewgroup.MyViewGroup
        xmlns:app="http://schemas.android.com/apk/res/com.example.ex_4_33_myviewgroup"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:view_screen="1" >
        <include layout="@layout/layout_left" />
        <include layout="@layout/layout_center" />
        <include layout="@layout/layout_right" />
    </com.example.ex_4_33_myviewgroup.MyViewGroup>

 

 

其中,用于标示显示哪一页的样式,定义如下
    <declare-styleable name="SlideStyledAttributes">
        <attr name="view_screen" format="integer" />
    </declare-styleable>

 

定义在styles.xml文件中。