用ViewGroup实现左右滚动

  1. import android.content.Context;  
  2. import android.graphics.Canvas;  
  3. import android.util.AttributeSet;  
  4. import android.util.Log;  
  5. import android.view.GestureDetector;  
  6. import android.view.GestureDetector.OnGestureListener;  
  7. import android.view.MotionEvent;  
  8. import android.view.View;  
  9. import android.view.ViewGroup;  
  10. import android.widget.Scroller;  
  11.   
  12. public class scollgroup extends ViewGroup implements OnGestureListener{  
  13.        public static final String tag = "CScrollView";  
  14.          
  15.        public static final int TOUCH_STATUS_NORMAL = 0;  
  16.        public static final int TOUCH_STATUS_FLING = 1;  
  17.        public static final int TOUCH_STATUS_SCROLLING = 2;  
  18.        public static final int TOUCH_STATUS_MOVING = 3;  
  19.          
  20.        /** 滚动一屏所花的时间*/  
  21.        private static final int SCROLL_ANIMATION_TIME = 1000;  
  22.        /** 首屏和尾屏的超屏数值*/  
  23.        private static final int SCREEN_OFFSET = 100;  
  24.          
  25.        public int mStatus = TOUCH_STATUS_NORMAL;  
  26.          
  27.        protected GestureDetector mGestureDetector;  
  28.        protected Scroller mScroller;  
  29.          
  30.        private int mCurrentPosX = 0;  
  31.        private int mLastPosX;  
  32.        private int mCurrentScreen = 0;  
  33.        private int mDownPosX = 0;  
  34.          
  35.        private IndicatorControl mIndicatorControl;  
  36.          
  37.        public scollgroup(Context context, AttributeSet attrs) {  
  38.                super(context, attrs);  
  39.                mGestureDetector = new GestureDetector(this);  
  40.                mScroller = new Scroller(context);  
  41.        }  
  42.   
  43.        public scollgroup(Context context) {  
  44.                super(context);  
  45.                mGestureDetector = new GestureDetector(this);  
  46.                mScroller = new Scroller(context);  
  47.        }  
  48.          
  49.        /** 
  50.         * 设置指示控制器 
  51.         * @param indicatorControl 指示控制器 
  52.         */  
  53.        public void setIndicatorControl(IndicatorControl indicatorControl) {  
  54.                this.mIndicatorControl = indicatorControl;  
  55.                if(mIndicatorControl != null) {  
  56.                        indicatorControl.setCount(this.getChildCount());  
  57.                }  
  58.        }  
  59.   
  60.        /** 
  61.         * 计算每个子控件的位置 
  62.         */  
  63.        @Override  
  64.        protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  65.                final int childCount = getChildCount();  
  66.                int left = 0;  
  67.                View child = null;  
  68.                for(int i = 0; i < childCount; i++) {  
  69.                        child = getChildAt(i);  
  70.                        if(child.getVisibility() == View.GONE)   
  71.                                continue;  
  72.                        final int width = child.getMeasuredWidth();  
  73.                        child.layout(left, 0, left + width, child.getMeasuredHeight());  
  74.                        left += width;  
  75.                }  
  76.        }  
  77.   
  78.        @Override  
  79.        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  80.                super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  81.                final int childCount = getChildCount();  
  82.                View child = null;  
  83.                for(int i = 0; i < childCount; i++) {  
  84.                        child = getChildAt(i);  
  85.                        child.measure(widthMeasureSpec, heightMeasureSpec);  
  86. //                     Log.d(tag, "width = " + MeasureSpec.getSize(widthMeasureSpec) + "; height = " + MeasureSpec.getSize(heightMeasureSpec));  
  87.                }  
  88.        }  
  89.   
  90.        @Override  
  91.        public boolean onInterceptTouchEvent(MotionEvent event) {  
  92.                  
  93.                switch(event.getAction()) {  
  94.                case MotionEvent.ACTION_DOWN:  
  95.                        Log.d(tag, "onInterceptTouchEvent => ACTION_DOWN");  
  96.                        return false;  
  97.                //触屏移动时,则传递事件到OnTouch事件  
  98.                case MotionEvent.ACTION_MOVE:  
  99.                        Log.d(tag, "onInterceptTouchEvent => ACTION_MOVE");  
  100.                        return true;  
  101.                }  
  102.                Log.d(tag, "onInterceptTouchEvent => " + event.getAction());  
  103.                return true;  
  104.        }  
  105.   
  106.        @Override  
  107.        public boolean onTouchEvent(MotionEvent event) {  
  108.                final int x = (int)event.getX();  
  109.                  
  110.                mGestureDetector.onTouchEvent(event);  
  111.                  
  112.                switch(event.getAction()) {  
  113.                case MotionEvent.ACTION_DOWN:  
  114.                        Log.d(tag, "onTouchEvent => ACTION_DOWN");  
  115.                          
  116.                        if(!mScroller.isFinished()) {  
  117.                                mScroller.abortAnimation();  
  118.                        }  
  119.                        mStatus = TOUCH_STATUS_MOVING;  
  120.                        mLastPosX = mCurrentPosX;  
  121.                        mDownPosX = x;  
  122.                case MotionEvent.ACTION_MOVE:  
  123.                        Log.d(tag, "onTouchEvent => ACTION_MOVE");  
  124.                          
  125.                        //计算当前滚动到的坐标  
  126.                        mCurrentPosX = mLastPosX + mDownPosX - x;  
  127.                        //屏幕第一屏和最后一屏越界不能超过10  
  128.                        mCurrentPosX = Math.max(  
  129.                                        Math.min(mCurrentPosX, (getChildCount() - 1)  
  130.                                                        * getMeasuredWidth() + SCREEN_OFFSET), -SCREEN_OFFSET);  
  131. //                     Log.d(tag, "mCurrentX = " + mCurrentPosX + "; mDownPosX = " + mDownPosX + "; x = " + x);  
  132.                        scrollTo(mCurrentPosX, 0);  
  133.                        break;  
  134.                case MotionEvent.ACTION_UP:  
  135.                        Log.d(tag, "onTouchEvent => ACTION_UP");  
  136.                          
  137.                        //计算当前滚动到的屏幕,如果已触发FLING事件,则不用计算当前屏幕,因为已在onFling事件中计算出来了  
  138.                        if(mStatus != TOUCH_STATUS_FLING)  
  139.                                mCurrentScreen = calculateScreen(mCurrentPosX);  
  140. //                     Log.d(tag, "mCurrentScreen = " + mCurrentScreen);  
  141.                        scrollTo(mCurrentScreen);  
  142.                        break;  
  143.                }  
  144.                return true;  
  145.        }  
  146.          
  147.   
  148.        /** 
  149.         * 将控件滚动到指定Subview 
  150.         * @param screenIndex 
  151.         */  
  152.        public void scrollTo(int screenIndex) {  
  153.                int targetX = screenIndex * getMeasuredWidth();  
  154. //             Log.d(tag, "getMeasuredWidth = " + getMeasuredWidth());  
  155.                  
  156.                //计算执行滚动事件所花的时间  
  157.                int time = Math.abs(mCurrentPosX - screenIndex * getMeasuredWidth())   
  158.                                * SCROLL_ANIMATION_TIME / getMeasuredWidth();  
  159.                mStatus = TOUCH_STATUS_SCROLLING;  
  160. //             Log.d(tag, "mCurrentPosX = " + mCurrentPosX + "; targetX -mCurrentPosX = " + (targetX -mCurrentPosX));  
  161.                mScroller.startScroll(mCurrentPosX, 0, targetX - mCurrentPosX , 0, time);  
  162.                computeScroll();  
  163.        }  
  164.          
  165.        @Override  
  166.        public void computeScroll() {  
  167.                if(mStatus == TOUCH_STATUS_SCROLLING && mScroller.computeScrollOffset()) {  
  168.                        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  
  169.                        mCurrentPosX = mScroller.getCurrX();  
  170.                        postInvalidate();  
  171.                } else if(mStatus == TOUCH_STATUS_SCROLLING) {  
  172.                        mStatus = TOUCH_STATUS_NORMAL;  
  173.                        mCurrentPosX = mScroller.getCurrX();  
  174.                          
  175.                        if(mIndicatorControl != null) {  
  176.                                mIndicatorControl.setSelected(calculateScreen(mCurrentPosX));  
  177.                        }  
  178.                }  
  179.        }  
  180.          
  181.        /** 
  182.         * 通过当前所在坐标计算出屏幕属于哪一屏 
  183.         * @param curPos 当前坐标 
  184.         */  
  185.        private int calculateScreen(int curPos) {  
  186.                int curScreen = curPos / getMeasuredWidth();  
  187.                if((curPos - curScreen * getMeasuredWidth()) > getMeasuredWidth() / 2) {  
  188.                        curScreen ++;  
  189.                }  
  190.                return Math.min(Math.max(0, curScreen), getChildCount() - 1);  
  191.        }  
  192.   
  193.        @Override  
  194.        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
  195.                        float velocityY) {  
  196. //             Log.i(tag, "Fling >> velocityX : " + velocityX + "; velocityY : " + velocityY);  
  197. //             Log.i(tag, "Fling >> event1 : " + e1.getX() + ", " + e1.getY() + ";  event2 : " + e2.getX() + ", " + e2.getY());  
  198.                mStatus = TOUCH_STATUS_FLING;  
  199.                if(velocityX > 0) {  
  200.                        mCurrentScreen = Math.max(mCurrentScreen - 10);  
  201.                } else {  
  202.                        mCurrentScreen = Math.min(mCurrentScreen + 1, getChildCount() - 1);  
  203.                }  
  204.                return false;  
  205.        }  
  206.          
  207.          
  208.   
  209.        public void requestInvalidate(int width) {  
  210.                mCurrentPosX = width * mCurrentScreen;  
  211.                scrollTo(mCurrentPosX, 0);  
  212.        }  
  213.   
  214.        @Override  
  215.        public boolean onDown(MotionEvent e) {  
  216.                // TODO Auto-generated method stub  
  217.                return false;  
  218.        }  
  219.   
  220.        @Override  
  221.        public void onShowPress(MotionEvent e) {  
  222.                // TODO Auto-generated method stub  
  223.                  
  224.        }  
  225.   
  226.        @Override  
  227.        public boolean onSingleTapUp(MotionEvent e) {  
  228.                // TODO Auto-generated method stub  
  229.                return false;  
  230.        }  
  231.   
  232.        @Override  
  233.        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,  
  234.                        float distanceY) {  
  235.                // TODO Auto-generated method stub  
  236.                return false;  
  237.        }  
  238.   
  239.        @Override  
  240.        public void onLongPress(MotionEvent e) {  
  241.                // TODO Auto-generated method stub  
  242.                  
  243.        }  
  244.          
  245.        /** 
  246.         * 指示器控制接口 
  247.         * @author 郑澍璋 
  248.         */  
  249.        public static interface IndicatorControl {  
  250.                  
  251.                /** 
  252.                 * 设置指示索引的数量 
  253.                 */  
  254.                public void setCount(int count);  
  255.                  
  256.                /** 
  257.                 * 获取当前所指示的索引值 
  258.                 */  
  259.                public int getCount();  
  260.                  
  261.                /** 
  262.                 * 设置当前选中哪一项 
  263.                 */  
  264.                public void setSelected(int index);  
  265.        }  
  266. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值