Android 自定义控件之ViewGroup实例(实现一个简易的Viewpager)

本文通过实例讲解如何自定义一个继承ViewGroup的控件,以实现一个简易的ViewPager。内容涵盖onMeasure和onLayout方法的实现,滑动翻页效果的处理,以及滑动冲突的解决。使用SimpleViewPager实现水平滑动翻页,同时处理竖直方向的ListView滑动。核心知识点包括View的测量、布局、事件分发以及Scroller和VelocityTracker的应用。
摘要由CSDN通过智能技术生成

一,写在前面     

       如何自定义一个继承ViewGroup的控件呢?在实现的过程中涉及哪些知识点?需要注意哪些地方呢?接下来以一个简易的ViewPager来展示继承ViewGroup的自定义控件。做出来是这样一个效果图,如下:


         完成一个这样的效果:水平方向由SimpleViewPager处理,竖直方向由ListView处理,SimpleViewPager有三个子元素->ListView。快速水平方向滑动时,可以进行翻页;慢速水平滑动时,若滑动页超过一半,则进行另一页,否则回到原页面。


        自定义控件ViewGroup,需要了解View的测量,布局,绘制流程。在前面博文Android自定义控件之测量onMeasure 中,从源码角度对测量流程进行了分析;布局流程相对比较简单,在继承ViewGroup时,在onLayout中设置子元素的布局即可;绘制流程常用于继承View的自定义控件。还需要了解Android事件分发的机制,从而去解决滑动冲突。在前面两篇博文Android事件分发机制之ViewGroup   ,Android事件处理之View$dispatchTouchEvent(ev)中对事件分发机制从源码角度进行了解析。接下里,直接看代码,并作分析。

二,实例展示之onMeasure

       首先看重写的onMeasure(w,h)方法,如下:

@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		//先测量子控件,再测量自己;
		measureChildren(widthMeasureSpec, heightMeasureSpec);
		
		//获取宽高的模式,大小
		int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
		int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
		int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
		
		//获取子view的个数
		int childCount = getChildCount();
		if (childCount == 0) {
			//如果没有子元素,则设置宽高大小为0
			setMeasuredDimension(0, 0);
			return;
		}
		
		//获取子View的宽,高
		View childAt = getChildAt(0);
		int childMeasuredWidth = childAt.getMeasuredWidth();
		int childMeasuredHeight = childAt.getMeasuredHeight();
		
		//分四种情况讨论
		if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
			//宽度设置为3个子view宽度相加,高度设置为一个子View高度
			
			setMeasuredDimension(childMeasuredWidth * 3, childMeasuredHeight);
		} else if (widthSpecMode == MeasureSpec.AT_MOST) {
			//宽度设置为3个子View宽度相加;
			//高度为exactly模式,直接取测量高度大小即可(分析ViewGroup$getChildMeasureSpec源码可知)
			
			setMeasuredDimension(childMeasuredWidth * 3, heightSpecSize);
		} else if (heightSpecMode == MeasureSpec.AT_MOST){
			//宽度为exactly模式,直接取测量的宽度值;高度为一个子View高度
			
			setMeasuredDimension(widthSpecSize, childMeasuredHeight);
		} else {
			//宽高都是exactly模式,则直接使用父view给的建议值大小
			
			setMeasuredDimension(widthSpecSize, heightSpecSize);
		}
		
	}
       查看FrameLayout$onMeasure(w,h)可知,该方法做了两件事,先测量子元素,再测量自己,测量逻辑见前面提到的博文,后面阐述会直接给结论,不再提供源码角度的详细分析。

        SimpleViewPager中有三个ListView,且这三个子元素的宽高都是一样的,于是我们调用ViewGroup$measureChildren(w,h)方法测量三个子元素,如果子元素宽高各不相同,ViewGroup还提供了ViewGroup$measureChild(View child,int w,int h),以及measureChildWithMargins(View child,int w, int widthUsed, int h, int heightUsed)方法,分别一个个测量子元素。

       那SimpleViewPager如何测量自己呢?分析FrameLayout的onMeasure(w,h)可知,测量自己需要判断specMode,然后取值。于是,判断宽高的测量模式,分为4种情况,不需要考虑UNSPECIFIED模式,那么宽高的测量模式只可能有AT_MOST和EXACTLY两种,总共4种情况。那么,分别为AT_MOST和EXACTLY两种情况时,如何设置测量大小?分析FrameLayout源码,查看View$resolveSizeAndState方法可知:在exactly时,分别取建议宽高测量大小即可;在at_most时,宽度的大小取三个子元素的宽度之和,高的大小取一个子元素的高度。最后,调用View$

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值