zoukankan      html  css  js  c++  java
  • 019、ViewGroup、Scroller与VelocityTracker

    范例,运用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文件中。

     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    mysql配置utf8_mb4
    Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by serv
    MongoDB自动删除过期数据--TTL索引
    正则小公举
    苹果手机弹起输入框将页面上的元素上移
    location的部分属性
    在ajax请求下的缓存机制
    苹果机的时间格式转换为时间搓
    $.extendGit 丢弃所有本地修改的方法
    调起微信扫一扫
  • 原文地址:https://www.cnblogs.com/zyh-blog/p/3343649.html
Copyright © 2011-2022 走看看