本来我是不准备写这篇文章的,我实在想不出有什么样奇怪的理由,会去继承ViewGroup然后自定义一个布局,大概是我的项目经验还不够吧,想了好久,想到了这样一个需求:

需求


如图:在项目中经常有一个这样的需求,一个View靠左,另一个View靠右,这样的布局方式使用频率非常高,通常使用的是RelativeLayout,但是每次设置Align属性真的好烦,现在就来实现一下这样的布局,解决这个问题。

思路

其实具体实现一个布局,还是可以分为两种:
1.继承一个具体的Layout,这样可以省去很多的代码逻辑,只需要注意自己要处理的业务即可,就比如说Listview嵌套Listview到时候,我们会考虑重写它的onMeasure方法。
2.继承自ViewGroup,然后自己设计代码逻辑。

LayoutParams

之前在介绍onMeasure的时候,我们注意到一个特殊的类MeasureSpec,那onLayout中,我们要注意什么参数呢?答案是:LayoutParams,这个单词直译是布局参数,从名字就可以看出是和布局相关的,既然说onLayout是用于确定子View位置的方法,那么自然离不开它的使用。

LayoutParams用于确定支持childView支持哪些属性,比如LinearLayout有一个LinearLayout.LayoutParams,这个LayoutParams就提供了Gravity,Weight这两个属性。而下面这段代码,用到了MarginLayoutParams它为子View提供了Margin属性。

源码:

这里使用第二种方式实现,也就是直接继承ViewGroup,因为我的需求是一个布局,它只允许有两个子View,一个在左边,一个在右边,这个不同于其它任何一个布局。

/**
* 自定义布局
* Created by ChenSS on 2016/12/3.
*/
public class MyRelativeLayout extends ViewGroup {
public MyRelativeLayout(Context context) {
this(context, null);
} public MyRelativeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
} public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int childWidth;
int childHeight;
MarginLayoutParams params; for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
childWidth = childView.getMeasuredWidth();
childHeight = childView.getMeasuredHeight();
params = (MarginLayoutParams) childView.getLayoutParams(); int left = 0, top = 0, right = 0, bottom = 0; switch (i) {
case 0:
left = params.leftMargin;
top = params.topMargin;
break;
case 1:
left = getWidth() - childWidth - params.rightMargin;
top = params.topMargin;
break;
}
right = left + childWidth;
bottom = childHeight + top;
childView.layout(left, top, right, bottom);
}
} @Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
//指定我们所需的LayoutParams
return new MarginLayoutParams(getContext(), attrs);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); // 计算出所有的childView的宽和高
measureChildren(widthMeasureSpec, heightMeasureSpec); // wrap_content时设置的宽和高
int width = 0;
int height = 0; int childCount = getChildCount();
if (childCount > 2)
throw new IllegalArgumentException("too many childView"); int childWidth;
int childHeight;
MarginLayoutParams params;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
params = (MarginLayoutParams) childView.getLayoutParams(); //宽度为子View大小总和
childWidth = childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
width += childWidth; //高度取最高的那个子View
childHeight = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
height = childHeight > height ? childHeight : height;
} /**
* 根据MeasureSpec设置MyRelativeLayout的宽高
*/
setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : width,
(heightMode == MeasureSpec.EXACTLY) ? sizeHeight : height);
}
}

参考:Android 手把手教您自定义ViewGroup

等到什么时候有个好的创意,再改成原创o(∩_∩)o~

安卓自定义控件(四)实现自定义Layout的更多相关文章

  1. 安卓自定义控件(三)实现自定义View

    前面两篇博客,把View绘制的方法说了一下,但是,我们只在onDraw里面做文章,控件都是直接传入一个Context,还不能在布局文件里使用自定义View.这一篇博客,就不再讲绘制,在我们原先的基础上 ...

  2. 安卓自定义控件(一)Canvas、Paint、Shader、Xfermode

    关于自定义控件,之前就写过一篇自定义控件,上图下字的Button,图片任意指定大小,但是使用效果还是让人感觉不幸福,这次索性彻彻底底地对自定义控件做一次彻彻底底的总结. 我会花4篇博客来介绍自定义控件 ...

  3. 安卓第四天笔记-Sqlite

    安卓第四天笔记-Sqlite 1.数据库的创建运行与更新 1.1.创建一个类继承SqliteOpenHelper 1.2.创建构造方法 /** * 数据库创建类 * @author 刘楠 * * 20 ...

  4. 安卓自定义控件(二)BitmapShader、ShapeDrawable、Shape

    第一篇博客中,我已经Canvas.Paint.Shader.Xfermode这些对象做了总结,而现在这篇文章主要介绍BitmapShader位图渲染,Xfermode如何实际应用,还有形状的绘制.不过 ...

  5. MATLAB地图工具箱学习总结(四)自定义投影

    MATLAB地图工具箱学习总结(四)自定义投影 这是本系列的最后一篇文章,准备给大家讲讲自定义投影怎么做.在做这项作业的时候,自己也是花了不少时间,将所有地图投影源文件都看了一遍,简单分析了一下源代码 ...

  6. 微信公众账号开发教程(四)自定义菜单(含实例源码)——转自http://www.cnblogs.com/yank/p/3418194.html

    微信公众账号开发教程(四)自定义菜单 请尊重作者版权,如需转载,请标明出处. 应大家强烈要求,将自定义菜单功能课程提前. 一.概述: 如果只有输入框,可能太简单,感觉像命令行.自定义菜单,给我们提供了 ...

  7. NGUI系列教程四(自定义Atlas,Font)

    今天我们来看一下怎么自定义NGUIAtlas,制作属于自己风格的UI.第一部分:自定义 Atlas1 . 首先我们要准备一些图标素材,也就是我们的UI素材,将其导入到unity工程中.2. 全选我们需 ...

  8. Wix打包系列(四) 自定义UI

    原文:Wix打包系列(四) 自定义UI 除了标准的安装界面,如果我们要在安装时需要提供一些额外的信息时,这时就需要自定义界面来显示和录入这些信息. 4.1  自定义对话框 如上一章中我们测试数据库的连 ...

  9. UICollectionView之自定义Layout

    #import <UIKit/UIKit.h> @interface WQViewController : UIViewController - (id)initWithFrame:(CG ...

  10. 自定义 Layout布局 UICollectionViewLayout

    from:   http://www.tuicool.com/articles/vuyIriN 当我们使用系统自带的UICollectionViewFlowLayout无法实现我们的布局时,我们就可以 ...

随机推荐

  1. sql server 2008 18456错误

    来自:http://blog.csdn.net/qishuangquan/article/details/6024767 百度搜18456错误几乎只能搜到一篇文章,并不是说结果条数,而是所有的文章都是 ...

  2. 【NOIP模拟】LCS及方案数(DP)

    Description 对于一个序列

  3. 【ASP.NET MVC 学习笔记】- 05 依赖注入工具Ninject

    本文参考:http://www.cnblogs.com/willick/p/3223042.html 1.Ninject是一款轻量级的DI工具,可通过VS的插件NuGet将其引用到项目中. 2.使用N ...

  4. spring mvc 一次请求 两次查询

    1.查找页面<img> <div class="qrcode-content"> <img src="#" alt="& ...

  5. PHP Curl模拟登录并抓取数据

    使用PHP的Curl扩展库可以模拟实现登录,并抓取一些需要用户账号登录以后才能查看的数据.具体实现的流程如下(个人总结): 1. 首先需要对相应的登录页面的html源代码进行分析,获得一些必要的信息: ...

  6. Linux命令用法

    1.cut http://www.cnblogs.com/dong008259/archive/2011/12/09/2282679.html 2.sed http://www.cnblogs.com ...

  7. Leetcode题解(九)

    28.Implement strStr()-------KMP算法(*) 题目 这道题目其实就是实现KMP算法,并且该算法也是比较经典的算法,需要很好的掌握: 贴上几个介绍字符串匹配的算法说明链接 h ...

  8. Building roads

    Building roads Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...

  9. 浅谈Jquery中的bind(),live(),delegate(),on()绑定事件方式 [转载]

    前言 因为项目中经常会有利用jquery操作dom元素的增删操作,所以会涉及到dom元素的绑定事件方式,简单的归纳一下bind,live,delegate,on的区别,以便以后查阅,也希望该文章日后能 ...

  10. header操作cookie

    root@kl20080094:~# curl -I "http://www.xxx.com" HTTP/1.1 200 OK Server: nginx/0.8.53 Date: ...