天天看点

仿ViewPager之ViewGroup一

 用到Scroller类,滚动的速度;GestureDetector手势类,应用到了快速滑动方法。

public class MyScrollView extends ViewGroup{

	private Context ctx;

	/**
	 * 判断是否发生快速滑动
	 */
	protected boolean isFling;
	
	public MyScrollView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		this.ctx = context;
		initView();
	}

	private void initView() {
		
//		myScroller = new MyScroller(ctx);
		myScroller = new Scroller(ctx);
		detector = new GestureDetector(ctx, new OnGestureListener() {
			@Override
			public boolean onSingleTapUp(MotionEvent e) {
				return false;
			}
			
			@Override
			public void onShowPress(MotionEvent e) {
			}
			
			/**
			 * 响应手指在屏幕上的滑动事件
			 */
			@Override
			public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
					float distanceY) {
				//移动屏幕 
//				System.out.println("distanceX::"+distanceX);
				/**
				 * 移动当前view内容 移动一段距离
				 * disX	 X方向移的距离		为正是,图片向左移动,为负时,图片向右移动 
				 * disY  Y方向移动的距离
				 */
				scrollBy((int) distanceX, 0);
				
				/**
				 * 将当前视图的基准点移动到某个点  坐标点
				 * x 水平方向X坐标
				 * Y 竖直方向Y坐标
				 *  scrollTo(x,  y);
				 */
				return false;
			}
			
			@Override
			public void onLongPress(MotionEvent e) {
			}
			
			@Override
			/**
			 * 发生快速滑动时的回调
			 */
			public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
					float velocityY) {
				isFling = true;
				if(velocityX>0 && currId>0){ // 快速向右滑动
					currId--;
				}else if(velocityX<0 && currId<getChildCount()-1){ // 快速向左滑动
					currId++;
				}
				moveToDest(currId);
				return false;
			}
			
			@Override
			public boolean onDown(MotionEvent e) {
				return false;
			}
		});
		
	}

	@Override
	/**
	 * 计算 控件大小,
	 * 做为viewGroup 还有一个责任,,:计算 子view的大小
	 */
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//		int size = MeasureSpec.getSize(widthMeasureSpec);
//		int mode = MeasureSpec.getMode(widthMeasureSpec);
		for (int i = 0; i < getChildCount(); i++) {
			View v = getChildAt(i);
			v.measure(widthMeasureSpec, heightMeasureSpec);
		}
	}
	
	@Override
	/**
	 * 对子view进行布局,确定子view的位置
	 * changed  若为true ,说明布局发生了变化
	 * l\t\r\b\  是指当前viewgroup 在其父view中的位置 
	 */
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		for (int i = 0; i < getChildCount(); i++) {
			View view = getChildAt(i); // 取得下标为I的子view
			/**
			 * 父view 会根据子view的需求,和自身的情况,来综合确定子view的位置,(确定他的大小),view.getWidth();  得到view的真实的大小。
			 */
			//指定子view的位置  ,  左,上,右,下,是指在viewGround坐标系中的位置
			view.layout(0+i*getWidth(), 0, getWidth()+i*getWidth(), getHeight());	
		}
	}
	
	/**
	 * 手势识别的工具类
	 */
	private GestureDetector detector;
	
	/**
	 * 当前的ID值
	 * 显示在屏幕上的子View的下标
	 */
	private int currId = 0;
	
	/**
	 * down 事件时的x坐标
	 */
	private int firstX = 0;
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		super.onTouchEvent(event);
		detector.onTouchEvent(event);
		//添加自己的事件解析	
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			firstX = (int) event.getX();
			break;
		case MotionEvent.ACTION_MOVE:
			
			break;
		case MotionEvent.ACTION_UP:			
			if(!isFling){//  在没有发生快速滑动的时候,才执行按位置判断currid
				int nextId = 0;
				if(event.getX()-firstX>getWidth()/2){ // 手指向右滑动,超过屏幕的1/2  当前的currid - 1
					nextId = currId-1;
				}else if(firstX - event.getX()>getWidth()/2){ // 手指向左滑动,超过屏幕的1/2  当前的currid + 1
					nextId = currId+1;
				}else{
					nextId = currId;
				}
				moveToDest(nextId);
//				scrollTo(0, 0);
			}
			isFling = false;
			break;
		}
		
		return true; 
	}

	/**
	 * 计算位移的工具类
	 */
//	private MyScroller myScroller;
	private Scroller myScroller;
	
	
	/**
	 * 移动到指定的屏幕上
	 * @param nextId	屏幕 的下标
	 */
	public  void moveToDest(int nextId) {
		/*
		 * 对 nextId 进行判断 ,确保 是在合理的范围  
		 * 即  nextId >=0  && next <=getChildCount()-1
		 */
		//确保 currId>=0
		currId = (nextId>=0)?nextId:0;
		//确保 currId<=getChildCount()-1
		currId = (nextId<=getChildCount()-1)?nextId:(getChildCount()-1);		
		//瞬间移动
//		scrollTo(currId*getWidth(), 0);
		
		//触发listener事件
		if(pageChangedListener!=null){
			pageChangedListener.moveToDest(currId);
		}
		
		int distance = currId*getWidth() - getScrollX(); // 最终的位置 - 现在的位置  = 要移动的距离
		
//		myScroller.startScroll(getScrollX(),0,distance,0);
		
		//设置运行的时间
		myScroller.startScroll(getScrollX(),0,distance,0,Math.abs(distance));
		/*
		 * 刷新当前view   onDraw()方法 的执行
		 */
		invalidate();
	}

	/**
	 * invalidate();  会导致  computeScroll()这个方法的执行
	 */
	@Override
	public void computeScroll() {
		//动画没有结束
		if(myScroller.computeScrollOffset()){
			int newX = (int) myScroller.getCurrX();
			System.out.println("newX::"+newX);
			scrollTo(newX, 0);
			invalidate();
		};
	}

	
	public MyPageChangedListener getPageChangedListener() {
		return pageChangedListener;
	}

	public void setPageChangedListener(MyPageChangedListener pageChangedListener) {
		this.pageChangedListener = pageChangedListener;
	}

	private MyPageChangedListener pageChangedListener;
	/**
	 * 页面改时时的监听接口
	 * @author leo
	 *
	 */
	public interface MyPageChangedListener{
		void moveToDest(int currid);
	}
	
}
           
/**
 * 计算位移距离的工具类
 * @author leo
 *
 */
public class MyScroller {

	
	private int startX;
	private int startY;
	private int distanceX;
	private int distanceY;
	
	/**
	 * 开始执行动画的时间
	 */
	private long startTime;
	/**
	 * 判断是否正在执行动画
	 * true 是还在运行
	 * false  已经停止
	 */
	private boolean isFinish;

	public MyScroller(Context ctx){
		
	}

	/**
	 * 开移移动
	 * @param startX	开始时的X坐标
	 * @param startY	开始时的Y坐标
	 * @param disX		X方向 要移动的距离
	 * @param disY		Y方向 要移动的距离
	 */
	public void startScroll(int startX, int startY, int disX, int disY) {
		this.startX = startX;
		this.startY = startY;
		this.distanceX = disX;
		this.distanceY = disY;
		this.startTime = SystemClock.uptimeMillis();
		
		this.isFinish = false;
	}

	/**
	 * 默认运行的时间
	 * 毫秒值
	 */
	private int duration = 500;
	/**
	 * 当前的X值
	 */
	private long currX;
	
	/**
	 * 当前的Y值
	 */
	private long currY;
	
	/**
	 * 计算一下当前的运行状况
	 * 返回值:
	 * true  还在运行
	 * false 运行结束
	 */
	public boolean computeScrollOffset() {

		if (isFinish) {
			return false;
		}

		// 获得所用的时间
		long passTime = SystemClock.uptimeMillis() - startTime;

		// 如果时间还在允许的范围内
		if (passTime < duration) {
			
			// 当前的位置  =  开始的位置  +  移动的距离(距离 = 速度*时间)
			currX = startX + distanceX * passTime / duration;
			currY = startY + distanceY * passTime / duration;

		} else {
			currX = startX + distanceX;
			currY = startY + distanceY;
			isFinish = true;
		}

		return true;
	}

	public long getCurrX() {
		return currX;
	}

	public void setCurrX(long currX) {
		this.currX = currX;
	}
	
	
}
           
public class MainActivity extends Activity {

	private MyScrollView msv;
	
	//图片资源ID 数组
	private int[] ids = new int[]{R.drawable.a1,R.drawable.a2,R.drawable.a3,R.drawable.a4,R.drawable.a5,R.drawable.a6};
	
	private RadioGroup radioGroup;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		msv =(MyScrollView) findViewById(R.id.myscroll_view);
		radioGroup = (RadioGroup) findViewById(R.id.radioGroup);
		
	
		
		for (int i = 0; i < ids.length; i++) {
			ImageView image = new ImageView(this);
			image.setBackgroundResource(ids[i]);
			msv.addView(image);
			
			
		}
		
		msv.setPageChangedListener(new MyPageChangedListener() {
			
			@Override
			public void moveToDest(int currid) {
				((RadioButton)radioGroup.getChildAt(currid)).setChecked(true);
			}
		});
		
		radioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			
			@Override
			public void onCheckedChanged(RadioGroup group, int checkedId) {
				msv.moveToDest(checkedId);
				
			}
		});
		
		//给自定义viewGroup添加测试的布局
		View temp = getLayoutInflater().inflate(R.layout.temp, null);
		msv.addView(temp, 2);
		
		for (int i = 0; i < msv.getChildCount(); i++) {
			//添加radioButton
			RadioButton rbtn = new RadioButton(this);
			rbtn.setId(i);
			
			radioGroup.addView(rbtn);
			if(i == 0){
				rbtn.setChecked(true);
			}
		}
		
	}
}