用ViewGroup实现左右滚动

来源:互联网 发布:vr 畸变算法 编辑:程序博客网 时间:2024/05/16 14:38
import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.GestureDetector.OnGestureListener;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.Scroller;public class scollgroup extends ViewGroup implements OnGestureListener{   public static final String tag = "CScrollView";              public static final int TOUCH_STATUS_NORMAL = 0;       public static final int TOUCH_STATUS_FLING = 1;       public static final int TOUCH_STATUS_SCROLLING = 2;       public static final int TOUCH_STATUS_MOVING = 3;              /** 滚动一屏所花的时间*/       private static final int SCROLL_ANIMATION_TIME = 1000;       /** 首屏和尾屏的超屏数值*/       private static final int SCREEN_OFFSET = 100;              public int mStatus = TOUCH_STATUS_NORMAL;              protected GestureDetector mGestureDetector;       protected Scroller mScroller;              private int mCurrentPosX = 0;       private int mLastPosX;       private int mCurrentScreen = 0;       private int mDownPosX = 0;              private IndicatorControl mIndicatorControl;              public scollgroup(Context context, AttributeSet attrs) {               super(context, attrs);               mGestureDetector = new GestureDetector(this);               mScroller = new Scroller(context);       }       public scollgroup(Context context) {               super(context);               mGestureDetector = new GestureDetector(this);               mScroller = new Scroller(context);       }              /**        * 设置指示控制器        * @param indicatorControl 指示控制器        */       public void setIndicatorControl(IndicatorControl indicatorControl) {               this.mIndicatorControl = indicatorControl;               if(mIndicatorControl != null) {                       indicatorControl.setCount(this.getChildCount());               }       }       /**        * 计算每个子控件的位置        */       @Override       protected void onLayout(boolean changed, int l, int t, int r, int b) {               final int childCount = getChildCount();               int left = 0;               View child = null;               for(int i = 0; i < childCount; i++) {                       child = getChildAt(i);                       if(child.getVisibility() == View.GONE)                                continue;                       final int width = child.getMeasuredWidth();                       child.layout(left, 0, left + width, child.getMeasuredHeight());                       left += width;               }       }       @Override       protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {               super.onMeasure(widthMeasureSpec, heightMeasureSpec);               final int childCount = getChildCount();               View child = null;               for(int i = 0; i < childCount; i++) {                       child = getChildAt(i);                       child.measure(widthMeasureSpec, heightMeasureSpec);//                     Log.d(tag, "width = " + MeasureSpec.getSize(widthMeasureSpec) + "; height = " + MeasureSpec.getSize(heightMeasureSpec));               }       }       @Override       public boolean onInterceptTouchEvent(MotionEvent event) {                              switch(event.getAction()) {               case MotionEvent.ACTION_DOWN:                       Log.d(tag, "onInterceptTouchEvent => ACTION_DOWN");                       return false;               //触屏移动时,则传递事件到OnTouch事件               case MotionEvent.ACTION_MOVE:                       Log.d(tag, "onInterceptTouchEvent => ACTION_MOVE");                       return true;               }               Log.d(tag, "onInterceptTouchEvent => " + event.getAction());               return true;       }       @Override       public boolean onTouchEvent(MotionEvent event) {               final int x = (int)event.getX();                              mGestureDetector.onTouchEvent(event);                              switch(event.getAction()) {               case MotionEvent.ACTION_DOWN:                       Log.d(tag, "onTouchEvent => ACTION_DOWN");                                              if(!mScroller.isFinished()) {                               mScroller.abortAnimation();                       }                       mStatus = TOUCH_STATUS_MOVING;                       mLastPosX = mCurrentPosX;                       mDownPosX = x;               case MotionEvent.ACTION_MOVE:                       Log.d(tag, "onTouchEvent => ACTION_MOVE");                                              //计算当前滚动到的坐标                       mCurrentPosX = mLastPosX + mDownPosX - x;                       //屏幕第一屏和最后一屏越界不能超过10                       mCurrentPosX = Math.max(                                       Math.min(mCurrentPosX, (getChildCount() - 1)                                                       * getMeasuredWidth() + SCREEN_OFFSET), -SCREEN_OFFSET);//                     Log.d(tag, "mCurrentX = " + mCurrentPosX + "; mDownPosX = " + mDownPosX + "; x = " + x);                       scrollTo(mCurrentPosX, 0);                       break;               case MotionEvent.ACTION_UP:                       Log.d(tag, "onTouchEvent => ACTION_UP");                                              //计算当前滚动到的屏幕,如果已触发FLING事件,则不用计算当前屏幕,因为已在onFling事件中计算出来了                       if(mStatus != TOUCH_STATUS_FLING)                               mCurrentScreen = calculateScreen(mCurrentPosX);//                     Log.d(tag, "mCurrentScreen = " + mCurrentScreen);                       scrollTo(mCurrentScreen);                       break;               }               return true;       }              /**        * 将控件滚动到指定Subview        * @param screenIndex        */       public void scrollTo(int screenIndex) {               int targetX = screenIndex * getMeasuredWidth();//             Log.d(tag, "getMeasuredWidth = " + getMeasuredWidth());                              //计算执行滚动事件所花的时间               int time = Math.abs(mCurrentPosX - screenIndex * getMeasuredWidth())                                * SCROLL_ANIMATION_TIME / getMeasuredWidth();               mStatus = TOUCH_STATUS_SCROLLING;//             Log.d(tag, "mCurrentPosX = " + mCurrentPosX + "; targetX -mCurrentPosX = " + (targetX -mCurrentPosX));               mScroller.startScroll(mCurrentPosX, 0, targetX - mCurrentPosX , 0, time);               computeScroll();       }              @Override       public void computeScroll() {               if(mStatus == TOUCH_STATUS_SCROLLING && mScroller.computeScrollOffset()) {                       scrollTo(mScroller.getCurrX(), mScroller.getCurrY());                       mCurrentPosX = mScroller.getCurrX();                       postInvalidate();               } else if(mStatus == TOUCH_STATUS_SCROLLING) {                       mStatus = TOUCH_STATUS_NORMAL;                       mCurrentPosX = mScroller.getCurrX();                                              if(mIndicatorControl != null) {                               mIndicatorControl.setSelected(calculateScreen(mCurrentPosX));                       }               }       }              /**        * 通过当前所在坐标计算出屏幕属于哪一屏        * @param curPos 当前坐标        */       private int calculateScreen(int curPos) {               int curScreen = curPos / getMeasuredWidth();               if((curPos - curScreen * getMeasuredWidth()) > getMeasuredWidth() / 2) {                       curScreen ++;               }               return Math.min(Math.max(0, curScreen), getChildCount() - 1);       }       @Override       public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,                       float velocityY) {//             Log.i(tag, "Fling >> velocityX : " + velocityX + "; velocityY : " + velocityY);//             Log.i(tag, "Fling >> event1 : " + e1.getX() + ", " + e1.getY() + ";  event2 : " + e2.getX() + ", " + e2.getY());               mStatus = TOUCH_STATUS_FLING;               if(velocityX > 0) {                       mCurrentScreen = Math.max(mCurrentScreen - 1, 0);               } else {                       mCurrentScreen = Math.min(mCurrentScreen + 1, getChildCount() - 1);               }               return false;       }                     public void requestInvalidate(int width) {               mCurrentPosX = width * mCurrentScreen;               scrollTo(mCurrentPosX, 0);       }       @Override       public boolean onDown(MotionEvent e) {               // TODO Auto-generated method stub               return false;       }       @Override       public void onShowPress(MotionEvent e) {               // TODO Auto-generated method stub                      }       @Override       public boolean onSingleTapUp(MotionEvent e) {               // TODO Auto-generated method stub               return false;       }       @Override       public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,                       float distanceY) {               // TODO Auto-generated method stub               return false;       }       @Override       public void onLongPress(MotionEvent e) {               // TODO Auto-generated method stub                      }              /**        * 指示器控制接口        * @author 郑澍璋        */       public static interface IndicatorControl {                              /**                * 设置指示索引的数量                */               public void setCount(int count);                              /**                * 获取当前所指示的索引值                */               public int getCount();                              /**                * 设置当前选中哪一项                */               public void setSelected(int index);       }}