本来我是不准备写这篇文章的,我实在想不出有什么样奇怪的理由,会去继承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,一个在左边,一个在右边,这个不同于其它任何一个布局。

  1. /**
  2. * 自定义布局
  3. * Created by ChenSS on 2016/12/3.
  4. */
  5. public class MyRelativeLayout extends ViewGroup {
  6. public MyRelativeLayout(Context context) {
  7. this(context, null);
  8. }
  9. public MyRelativeLayout(Context context, AttributeSet attrs) {
  10. this(context, attrs, 0);
  11. }
  12. public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  13. this(context, attrs, defStyleAttr, 0);
  14. }
  15. public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  16. super(context, attrs, defStyleAttr, defStyleRes);
  17. }
  18. @Override
  19. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  20. int childCount = getChildCount();
  21. int childWidth;
  22. int childHeight;
  23. MarginLayoutParams params;
  24. for (int i = 0; i < childCount; i++) {
  25. View childView = getChildAt(i);
  26. childWidth = childView.getMeasuredWidth();
  27. childHeight = childView.getMeasuredHeight();
  28. params = (MarginLayoutParams) childView.getLayoutParams();
  29. int left = 0, top = 0, right = 0, bottom = 0;
  30. switch (i) {
  31. case 0:
  32. left = params.leftMargin;
  33. top = params.topMargin;
  34. break;
  35. case 1:
  36. left = getWidth() - childWidth - params.rightMargin;
  37. top = params.topMargin;
  38. break;
  39. }
  40. right = left + childWidth;
  41. bottom = childHeight + top;
  42. childView.layout(left, top, right, bottom);
  43. }
  44. }
  45. @Override
  46. public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
  47. //指定我们所需的LayoutParams
  48. return new MarginLayoutParams(getContext(), attrs);
  49. }
  50. @Override
  51. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  52. int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  53. int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  54. int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
  55. int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
  56. // 计算出所有的childView的宽和高
  57. measureChildren(widthMeasureSpec, heightMeasureSpec);
  58. // wrap_content时设置的宽和高
  59. int width = 0;
  60. int height = 0;
  61. int childCount = getChildCount();
  62. if (childCount > 2)
  63. throw new IllegalArgumentException("too many childView");
  64. int childWidth;
  65. int childHeight;
  66. MarginLayoutParams params;
  67. for (int i = 0; i < childCount; i++) {
  68. View childView = getChildAt(i);
  69. params = (MarginLayoutParams) childView.getLayoutParams();
  70. //宽度为子View大小总和
  71. childWidth = childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
  72. width += childWidth;
  73. //高度取最高的那个子View
  74. childHeight = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
  75. height = childHeight > height ? childHeight : height;
  76. }
  77. /**
  78. * 根据MeasureSpec设置MyRelativeLayout的宽高
  79. */
  80. setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : width,
  81. (heightMode == MeasureSpec.EXACTLY) ? sizeHeight : height);
  82. }
  83. }

参考: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. 转:用STL中的vector动态开辟二维数组

    用STL中的vector动态开辟二维数组 源代码:#include <iostream>#include <vector>using namespace std;int mai ...

  2. CentOS 6.5 + Nginx 1.8.0 + PHP 5.6(with PHP-FPM) 负载均衡源码安装

    CentOS 6.5 + Nginx 1.8.0 + PHP 5.6(with PHP-FPM) 负载均衡源码安装 http://www.cnblogs.com/ppoo24/p/4918288.ht ...

  3. ViewPager使用记录2——展示动态数据

    ViewPager是v4支持库中的一个控件,相信几乎所有接触Android开发的人都对它不陌生.之所以还要在这里翻旧账,是因为我在最近的项目中有多个需求用到了它,觉得自己对它的认识不够深刻.我计划从最 ...

  4. Spark 基本概念

    Application:用户编写的 Spark 应用程序,包含驱动程序(Driver),和分布在集群中多个节点上运行的 Executor 代码,在执行过程中由一个或多个作业组成 Driver(驱动程序 ...

  5. 长话短说 之 js的原型和闭包

    原型链:undefined, number, string, boolean 属于简单的值类型,函数.数组.对象.null.new obj()都是引用类型.检测值类型用typeof x 即可,检测引用 ...

  6. 关于CSS 的position定位问题

    对于初学者来说,css的position定位问题是比较常见的.之前搞不清楚postion定位是怎么回事,排版一直歪歪斜斜的,老是排不好 css的定位一般来说,分为四种: position:static ...

  7. MongoDB查询分析

    MongoDB 查询分析可以确保我们建立的索引是否有效,是查询语句性能分析的重要工具.MongoDB 查询分析常用函数有:explain() 和 hint(). 1. explain(): 提供查询信 ...

  8. LeetCode 11. Container With Most Water (装最多水的容器)

    Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai).  ...

  9. 微软Tech Summit 2017,等你来打Call

    2017年10月31至11月3日,由微软举办的Tech Summit 2017技术暨生态大会将在北京盛大举办,要在北京连开四天.今年的技术大会看头十足,不仅有大咖级人物带来十二大主题课程,更有三天四场 ...

  10. 解决Python2.7的UnicodeEncodeError:'ascii' codec can't encode characters in position 0-78: ordinal not in range(128)异常错误

    解决Python2.7的UnicodeEncodeError: 'ascii' codec can't encode异常错误 大家都知道,在使用python进行网络爬虫时,最头疼的就是转码问题,下面是 ...