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); }}