自定义ViewGroup(这里以自定义一个类似ViewPager的控件为例)

首先,我们得先要明白ViewGroup是什么,职责是什么?

ViewGroup就是用来放置View控件的的容器,职责是测量每一个子view的大小,还有摆放的位置。



 * 1,写一个类继承于ViewGroup


 * 2,重载onMeasure方法,用于测量的子View的大小


 * 3,重载onLayout,用于安置子view的位置

到这里,一个ViewGroup就算自定义完成了,

下面的步骤是实现ViewGroup的功能

想要实现子类滑动的效果,就要借助Scroller这个类,这个类的使用请参考http://blog.csdn.net/qq_15949077/article/details/46940507
 * 4.然后通过Gesturector手势识别器的ontuchEvent屏幕监听器即可实现页面的滑动


 * 5,最后写一个内部接口,对外提供一个监听的set方法


看代码吧,这是一个实现类似viewPager的例子,里面的Scroller类作用很大


public class MyViewGroup extends ViewGroup {
	GestureDetector gd;//手势识别器
	Scroller scroll;
	int itemID=0;
	
	int firstX;
	int lastX;
	int dis;
	
	
	boolean isFling;
	
	itemChangelistener listener;
	
	/**
	 * 事件中断器用的变量
	 */
	int downx;
	int downy;
	int movex;
	int movey;
	int distancex;
	int distancey;
	
	public MyViewGroup(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		
		scroll=new Scroller(context);
		
		gd=new GestureDetector(context, new OnGestureListener() {
			
			@Override
			public boolean onSingleTapUp(MotionEvent e) {
				// TODO Auto-generated method stub
				return false;
			}
			
			@Override
			public void onShowPress(MotionEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
					float distanceY) {
				// TODO Auto-generated method stub
				if(itemID==0&&distanceX<0){
					
				}
				else if(itemID==getChildCount()-1&&distanceX>0){
					
				}
				else{
					scrollBy((int) distanceX, 0);
				}
				
				
				return false;
			}
			
			@Override
			public void onLongPress(MotionEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
					float velocityY) {
				// TODO Auto-generated method stub
				
				isFling=true;
		
				if(itemID==0&&velocityX>0){
					
				}
				else if(itemID==getChildCount()-1&&velocityX<0){
					
				}
				else if(velocityX>0){
					itemID--;
					
				}
				else if(velocityX<0){
					itemID++;
					
				}
				
				return false;
			}
			
			@Override
			public boolean onDown(MotionEvent e) {
				// TODO Auto-generated method stub

				return false;
			}
		});
	}
	
	//测量子view的大小
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		for(int i=0;i<this.getChildCount();i++){
			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
			
			
		}
		
	}
	
	//指定子View的位置
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		// TODO Auto-generated method stub
		for(int i=0;i<this.getChildCount();i++){
			getChildAt(i).layout(i*getWidth(), 0, i*getWidth()+getWidth(), getHeight());
		}
	}

	
	/**
	 * 事件中断器
	 * 比较上下和左右移动的距离谁大,大的就移动谁
	 * 
	 * 
	 * 如果返回值为true:则代表事件到这来被中断掉,就会执行自己的onTouchEvent,
	 * 注意,奇怪的是从move开始,而不是down,所以在down里有做处理的要注意,
	 * 而且这里有时会产生跳动bug,原因不知道,理解不了,(不是因为从move开始执行的原因,因为在down都没做处理的情况下也一样跳动)
	 * 解决这个bug的方法也很简单,在dowm的方法里将这个事件传给手势识别器就ok了
	 * 
	 * 如果返回值为false:将不会执行onTouchEvent方法,而是把事件传给子控件
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub

		boolean result=false;
		switch(ev.getAction()){
		case MotionEvent.ACTION_DOWN:
			gd.onTouchEvent(ev);
			downx=(int) ev.getX();
			downy=(int) ev.getY();
			
			break;
		case MotionEvent.ACTION_MOVE:
			
			movex=(int) ev.getX();
			movey=(int) ev.getY();
			distancex=Math.abs(movex-downx);
			distancey=Math.abs(movey-downy);
			
			if(distancex>distancey&&distancex>10){
				result= true;
			}
			else{
				result= false;
			}
//			else if(distancex<distancey&&distancey>10){
//				result= false;
//			}
			
			break;
		
		}
		return result;
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		gd.onTouchEvent(event);
		
	
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
		
			firstX=(int)event.getX();
			
			
			break;
		case MotionEvent.ACTION_MOVE:
			

			
			break;
		case MotionEvent.ACTION_UP:
		
	
		
			lastX=(int) event.getX();
			dis=lastX-firstX;
			if(!isFling){
		
				if(itemID==0&&dis>0){
					
				}
				else if(itemID==getChildCount()-1&&dis<0){
					
				}
				else if(dis>0&&dis>getWidth()/5){
					itemID--;
				}
				else if(dis<0&&Math.abs(dis)>getWidth()/5){
					itemID++;
				}
				
			}
			
			
			myScrollto(itemID);
			break;
		}
		
		
		
		return true;
	}
	
	
	
	/**
	 * 开始移动
	 * @param ItemID
	 */
	public void myScrollto(int itemID){
		if(listener!=null){
			listener.itemID(itemID);
		}
		int dis=itemID*getWidth()-getScrollX();
		
		scroll.startScroll(getScrollX(), 0, dis, 0, Math.abs(dis));
		invalidate();
		
	}
	
	
	/**
	 * 执行invalidata方法后回调
	 */
	@Override
	public void computeScroll() {
		// TODO Auto-generated method stub
		super.computeScroll();
		if(scroll.computeScrollOffset()){
			scrollTo(scroll.getCurrX(), 0);
			invalidate();
		}
		else{
			isFling=false;
		}
	}
	
	/**
	 * 对外部提供的设置指定页面的方法
	 * @param itemID
	 */
	public void setItemID(int itemID){
		this.itemID=itemID;
		myScrollto(itemID);
	}
	
	/**
	 * 对外提供一个监听页面改变后获取id的方法
	 * @param listener
	 */
	public void setonItemChangelistener(itemChangelistener listener){
		this.listener=listener;
	}
	
	/**
	 * 对外提供的滑动到哪个页面id的接口
	 * @author Administrator
	 *
	 */
	interface itemChangelistener{
		void itemID(int ItemID);
	}
	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值