自定义 SimpleVG
来源:互联网 发布:淘宝网店图标尺寸 编辑:程序博客网 时间:2024/06/17 17:18
一、实现一个简单的 ViewGroup
该自定义 ViewGroup 类似 LinearLayout 垂直布局的形式
布局
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <cc.catface.helloworld.view.SimpleVG android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#20f0"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="first button" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="你的信仰是什么" /> <Button android:layout_width="200dp" android:layout_height="wrap_content" android:text="second button" /> <Button android:layout_width="300dp" android:layout_height="60dp" android:text="third button" /> </cc.catface.helloworld.view.SimpleVG></LinearLayout>
onMeasure方法中遍历测得所有子控件的宽高
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); // 保存实际布局的宽高 int width = 0; int height = 0; // 轮询遍历所有子控件的宽高 for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); // 得到子控件的宽高 int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 整个布局的高为所有子控件高值的总和 height += childHeight; // 整个布局的宽为所有子空间中最宽的那个控件的宽值 width = Math.max(width, childWidth); } setMeasuredDimension((widthSpecMode == MeasureSpec.AT_MOST) ? width : widthSpecSize, (heightSpecMode == MeasureSpec.AT_MOST) ? height : heightSpecSize);}
简述一下上面代码中的最后一行setMeasuredDimension(……),实际为以下代码的简写
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(width, height);} else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(width, heightSpecSize);} else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpecSize, height);} else { setMeasuredDimension(widthSpecSize, heightSpecSize);}
也就是说,宽或高谁是 wrap_content(AT_MOST),那么就将自己测量处理的到的结果交给系统。反之,如果是 match_parent(EXACTLY)或者具体 dp/px值(EXACTLY),那么就将父容器的建议值交给系统,就是 MeasureSpec.getSize方法得到的宽或高值交给系统
onLayout方法中确定所有子控件的位置
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int top = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 该行即确定各个子控件的left,top,right,button位置坐标点,具体分析如下图 child.layout(0, top, childWidth, top + childHeight); top += childHeight; }}
上面代码
child.layout(0, top, childWidth, top + childHeight);
的分析图
而、为自定义布局中的子控件添加 margin
布局
<!-- 为增加展示效果,为各子控件添加了背景色,并添加同样效果的LinearLayout布局作为对比 --><?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"> <cc.catface.helloworld.view.SimpleVG android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#20f0"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30px" android:background="#100f" android:text="first button" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="60px" android:background="#200f" android:text="你的信仰是什么" /> <Button android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginBottom="15px" android:layout_marginLeft="20px" android:layout_marginRight="30px" android:layout_marginTop="90px" android:background="#300f" android:text="second button" /> <Button android:layout_width="300dp" android:layout_height="60dp" android:layout_marginTop="120px" android:background="#400f" android:text="third button" /> </cc.catface.helloworld.view.SimpleVG> <View android:layout_width="match_parent" android:layout_height="10px" android:background="#000" /> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#20f0" android:orientation="vertical"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30px" android:background="#100f" android:text="first button" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="60px" android:background="#200f" android:text="你的信仰是什么" /> <Button android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginBottom="15px" android:layout_marginLeft="20px" android:layout_marginRight="30px" android:layout_marginTop="90px" android:background="#300f" android:text="second button" /> <Button android:layout_width="300dp" android:layout_height="60dp" android:layout_marginTop="120px" android:background="#400f" android:text="third button" /> </LinearLayout></LinearLayout>
结果如下,margin作用并没有显示出来
原因很容易得出,因为布局中添加了margin,肯定要在父容器的测量和规划布局中得到 margin值并做对应处理啊。也就是在 onMeasure方法和 onLayout方法中获取 margin值并做对应处理,具体处理情况如下
针对 margin 重新 measure 和 layout
具体就是通过 MarginLayoutParams 获取到 margin值,然后做相应处理
onMeasure:重点是两条*号中间的处理
for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); // ******************************************************************************************// MarginLayoutParams childMarginarginLayoutParams = (MarginLayoutParams) child.getLayoutParams(); // 分别为当前子控件的宽/高 int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 分别为当前布局的中宽/高(添加对应各子控件的margin值) width = Math.max(width, childWidth + childMarginarginLayoutParams.leftMargin + childMarginarginLayoutParams.rightMargin); height += childHeight + childMarginarginLayoutParams.topMargin + childMarginarginLayoutParams.bottomMargin; // ******************************************************************************************************\\}
onLayout:重点是两条*号中间的处理
for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // *******************************************************************************************/ MarginLayoutParams childMarginarginLayoutParams = (MarginLayoutParams) child.getLayoutParams(); // 子控件的宽/高 int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 子控件的各margin值 int leftMargin = childMarginarginLayoutParams.leftMargin; int topMargin = childMarginarginLayoutParams.topMargin; int rightMargin = childMarginarginLayoutParams.rightMargin; int bottomMargin = childMarginarginLayoutParams.bottomMargin; // 先记录各个子控件的topMargin值 top += topMargin; // 左上位置坐标(leftMargin, top)很好理解 // 子控件宽/高分别就是左上坐标加上自己的测量宽高值 child.layout(leftMargin, top, leftMargin + childWidth, top + childHeight); // 当前子控件位置确定后,记录下一个子控件的top点为当前子控件高度+当前子控件的bottomMargin值 // 当记录下一个子控件的top点时,配合上面的 top += topMargin 即可得到结果 top += (childHeight + bottomMargin); // *******************************\\}
效果
结果报错:类转换异常
针对 margin 在 onMeasure方法和 onLayout方法中做处理,需要复写如下三个方法
// 为当前自定义ViewGroup获取MarginLayoutParams以对margin做相关处理@Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new MarginLayoutParams(p);}@Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs);}@Override protected LayoutParams generateDefaultLayoutParams() { return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);}
上述过程处理完成后的最终效果如下
阅读全文
1 0
- 自定义 SimpleVG
- 自定义
- 自定义
- 自定义
- 自定义
- 自定义
- 自定义TexBox,自定义ComboBox
- 自定义View自定义属性
- 自定义View自定义属性
- 自定义View自定义属性
- 自定义View自定义属性
- 自定义一个自定义对话框
- 自定义控件-自定义吐司
- 自定义checkbox,自定义button
- 自定义控件--自定义对话框
- 自定义控件----自定义属性
- 自定义控件:自定义Toast
- 自定义View自定义属性
- PBS作业提交后,输出错误MPI_Aobrt
- java使用condition进行线程通信
- iptables
- JAVA开发之 30-java的数组的12个最佳方法
- 数据中心 FailOver 后避免数据错乱的关键做法
- 自定义 SimpleVG
- 二叉查找树
- 【JS】:关于select的multiple的JS复选使用
- LeetCode 226. Invert Binary Tree 递归、指针交换 思考链表的交换
- eclipse中报的错:An internal error occurred during: "reload maven project". java.lang.NullPointerExceptio
- fedora 装zapproxy小问题
- 【转】将QT开发的界面程序封装成DLL,在VC中成功调用
- MySQL数据库高并发优化配置
- 用R语言做网页爬虫和文本分析1