Android知识梳理之自定义ViewGroup
来源:互联网 发布:入门单反推荐2017知乎 编辑:程序博客网 时间:2024/03/29 18:45
在我们进行android开发的时候虽然官方提供了形形色色的控件,但是有的时候根据不同业务需求我们找不到官方的控件支持,那么这个时候就需要我们自己去定义控件来适应不同的需求了.本篇将和大家一起探讨自定义ViewGrop 的相关知识.
转载请注明出处: http://blog.csdn.net/unreliable_narrator?viewmode=contents
首先我们先来看看官方文档是如何进行描述的:
翻译过来的大体意思就是:一个ViewGroup是一种特殊的视图可以包含其他视图(称为孩子)的视图组基类的布局和视图的容器。这个类也定义了viewgroup.layoutparams类作为基类的布局参数。也就是说ViewGroup实际上就是存放一些控件的容器,比如官方自带的一些Linerlayout,RelativeLayout等等.我们先来讲讲ViewGroup中两个重要的方法:onLayout和onMeasure,onLayout是必须重写实现的.
AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
setMeasureDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
//可作为模板代码!
private int measureWidth(int measureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode == MeasureSpec.EXACTLY){//精确值模式,指定具体数值
result = specSize;
}else{
result = 200;//先设置一个默认大小
//最大值模式,layout_width 或 layout_height 为 wrap_content 时,控件大小随控件的内容变化而变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。
if(specMode == MeasureSpec.AT_MOST){
result = Math.min(result, specSize);//取出我们指定的大小和 specSize 中最小的一个来作为最后的测量值
}
//MeasureSpec.UNSPECIFIED 不指定其大小,View 想多大就多大
}
return result;
}
onLayout()方法:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int childLeft = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
final int childWidth = childView.getMeasuredWidth();
childView.layout(childLeft, 0, childLeft + childWidth,
childView.getMeasuredHeight());
childLeft += childWidth;
}
}
}
}
public class MyView extends ViewGroup { public MyView(Context context) { this(context, null); } public MyView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //调用该方法预先对子控件进行测量 measureChildren(widthMeasureSpec, heightMeasureSpec); //设置控件的宽高 setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } /** * 返回控件的宽 * * @param widthMeasureSpec * @return */ private int measureWidth(int widthMeasureSpec) { int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); int result = 0; //判断是否是包裹内容的模式 if (specMode == MeasureSpec.AT_MOST) { int size = 0; //将所有的子控件的宽度进行叠加 for (int x = 0; x < getChildCount(); x++) { View child = getChildAt(x); int measuredWidth = child.getMeasuredWidth(); size += measuredWidth; } result = size; } else { result = specSize; } return result; } /** * 返回控件的高 * * @param heightMeasureSpec * @return */ private int measureHeight(int heightMeasureSpec) { int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int result = 0; //判断是否是包裹内容 if (heightMode == MeasureSpec.AT_MOST) { for (int x = 0; x < getChildCount(); x++) { View child = getChildAt(x); int measuredHeight = child.getMeasuredHeight(); //取子控件最大的高度 int min = Math.max(result, measuredHeight); result = min; } } else { result = heightSize; } return result; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); int left = 0; //左边的距离 View child; //遍历布局子元素 for (int i = 0; i < childCount; i++) { child = getChildAt(i); int width = child.getMeasuredWidth(); child.layout(left, 0, left + width, child.getMeasuredHeight()); left += width; } }}布局文件:
<com.dapeng.viewgropdemo.MyView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/black"> <View android:layout_width="50dip" android:layout_height="50dip" android:background="@android:color/holo_green_dark"></View> <View android:layout_width="100dip" android:layout_height="100dip" android:background="@android:color/holo_red_light"></View> <View android:layout_width="200dip" android:layout_height="200dip" android:background="@android:color/holo_orange_light"></View> </com.dapeng.viewgropdemo.MyView>
接着讲一个很容易出错的问题:getMeasuredWidth()与getWidth()的区别。他们的值大部分时间都是相同的,但意义确是根本不一样的,我们就来简单分析一下。
区别主要体现在下面两点:
(1)首先getMeasureWidth()方法在measure()过程结束后就可以获取到了,而getWidth()方法要在layout()过程结束后才能获取到。
(2) getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的,而getWidth()方法中的值则是通过layout(left,top,right,bottom)方法设置的。
参数 1. AttributeSet attrs xml解析inflate时生成和容器类型匹配的布局LayoutParams
2. ViewGroup.LayoutParams p 传入viewGroupLayoutParams 然后生成和容器类型匹配的布局LayoutParams
总结:
这个方法主要是用于被子View调用。
生成和此容器类型相匹配的布局参数类。
@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs){return new MarginLayoutParams(getContext(), attrs);} @Overrideprotected LayoutParams generateDefaultLayoutParams(){return new MarginLayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);} @Overrideprotected LayoutParams generateLayoutParams(LayoutParams p){return new MarginLayoutParams(p);}
cParams = (MarginLayoutParams) childView.getLayoutParams();int bottomMargin = cParams.bottomMargin;int topMargin = cParams.topMargin;int leftMargin = cParams.leftMargin;int rightMargin = cParams.rightMargin;
LayoutParams相当于一个Layout的信息包,它封装了Layout的位置、高、宽等信息。假设在屏幕上一块区域是由一个Layout占领的,如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去。
可以这样去形容LayoutParams,在象棋的棋盘上,每个棋子都占据一个位置,也就是每个棋子都有一个位置的信息,如这个棋子在4行4列,这里的“4行4列”就是棋子的LayoutParams。
1,一个确定的值;
2,FILL_PARENT,即填满(和父容器一样大小);
3,WRAP_CONTENT,即包裹住组件就好。
/**
* 使用自定义LayoutParams必须重写下面的四个方法
*/
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
}
- Android知识梳理之自定义ViewGroup
- Android知识梳理之自定义View
- android之自定义ViewGroup
- Android之自定义ViewGroup
- Android之自定义ViewGroup
- Android之自定义ViewGroup
- Android知识梳理之Intent
- Android 学习之--自定义ViewGroup
- Android学习之自定义ViewGroup
- Android 自定义view的知识梳理。
- 自定义ViewGroup知识总结
- 自定义View知识梳理
- Android知识梳理之Service整理
- Android知识梳理之BroadcastReceiver整理
- Android知识梳理之事件传递机制
- Android知识梳理之屏幕适配全攻略
- Android知识梳理之屏幕适配全攻略
- Android知识梳理之屏幕适配全攻略
- Activity的四种launchMode
- 发短信
- c++使用rapidxml读取cocos2dx的配置文件
- FJNU/大二下/javascript/事件处理
- C# 显示接口和隐式接口
- Android知识梳理之自定义ViewGroup
- csharp 反编译
- 目标跟踪 详细博文
- 框架的理解
- Android开发:集成dropbox网盘功能
- AJAX POST&跨域 解决方案 - CORS
- 图像处理 源代码
- Memcached 集群的高可用(HA)架构
- HashMap相关学习