安卓自定义控件(四)实现自定义Layout
本来我是不准备写这篇文章的,我实在想不出有什么样奇怪的理由,会去继承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);
}
}
等到什么时候有个好的创意,再改成原创o(∩_∩)o~
安卓自定义控件(四)实现自定义Layout的更多相关文章
- 安卓自定义控件(三)实现自定义View
前面两篇博客,把View绘制的方法说了一下,但是,我们只在onDraw里面做文章,控件都是直接传入一个Context,还不能在布局文件里使用自定义View.这一篇博客,就不再讲绘制,在我们原先的基础上 ...
- 安卓自定义控件(一)Canvas、Paint、Shader、Xfermode
关于自定义控件,之前就写过一篇自定义控件,上图下字的Button,图片任意指定大小,但是使用效果还是让人感觉不幸福,这次索性彻彻底底地对自定义控件做一次彻彻底底的总结. 我会花4篇博客来介绍自定义控件 ...
- 安卓第四天笔记-Sqlite
安卓第四天笔记-Sqlite 1.数据库的创建运行与更新 1.1.创建一个类继承SqliteOpenHelper 1.2.创建构造方法 /** * 数据库创建类 * @author 刘楠 * * 20 ...
- 安卓自定义控件(二)BitmapShader、ShapeDrawable、Shape
第一篇博客中,我已经Canvas.Paint.Shader.Xfermode这些对象做了总结,而现在这篇文章主要介绍BitmapShader位图渲染,Xfermode如何实际应用,还有形状的绘制.不过 ...
- MATLAB地图工具箱学习总结(四)自定义投影
MATLAB地图工具箱学习总结(四)自定义投影 这是本系列的最后一篇文章,准备给大家讲讲自定义投影怎么做.在做这项作业的时候,自己也是花了不少时间,将所有地图投影源文件都看了一遍,简单分析了一下源代码 ...
- 微信公众账号开发教程(四)自定义菜单(含实例源码)——转自http://www.cnblogs.com/yank/p/3418194.html
微信公众账号开发教程(四)自定义菜单 请尊重作者版权,如需转载,请标明出处. 应大家强烈要求,将自定义菜单功能课程提前. 一.概述: 如果只有输入框,可能太简单,感觉像命令行.自定义菜单,给我们提供了 ...
- NGUI系列教程四(自定义Atlas,Font)
今天我们来看一下怎么自定义NGUIAtlas,制作属于自己风格的UI.第一部分:自定义 Atlas1 . 首先我们要准备一些图标素材,也就是我们的UI素材,将其导入到unity工程中.2. 全选我们需 ...
- Wix打包系列(四) 自定义UI
原文:Wix打包系列(四) 自定义UI 除了标准的安装界面,如果我们要在安装时需要提供一些额外的信息时,这时就需要自定义界面来显示和录入这些信息. 4.1 自定义对话框 如上一章中我们测试数据库的连 ...
- UICollectionView之自定义Layout
#import <UIKit/UIKit.h> @interface WQViewController : UIViewController - (id)initWithFrame:(CG ...
- 自定义 Layout布局 UICollectionViewLayout
from: http://www.tuicool.com/articles/vuyIriN 当我们使用系统自带的UICollectionViewFlowLayout无法实现我们的布局时,我们就可以 ...
随机推荐
- sql server 2008 18456错误
来自:http://blog.csdn.net/qishuangquan/article/details/6024767 百度搜18456错误几乎只能搜到一篇文章,并不是说结果条数,而是所有的文章都是 ...
- 【NOIP模拟】LCS及方案数(DP)
Description 对于一个序列
- 【ASP.NET MVC 学习笔记】- 05 依赖注入工具Ninject
本文参考:http://www.cnblogs.com/willick/p/3223042.html 1.Ninject是一款轻量级的DI工具,可通过VS的插件NuGet将其引用到项目中. 2.使用N ...
- spring mvc 一次请求 两次查询
1.查找页面<img> <div class="qrcode-content"> <img src="#" alt="& ...
- PHP Curl模拟登录并抓取数据
使用PHP的Curl扩展库可以模拟实现登录,并抓取一些需要用户账号登录以后才能查看的数据.具体实现的流程如下(个人总结): 1. 首先需要对相应的登录页面的html源代码进行分析,获得一些必要的信息: ...
- Linux命令用法
1.cut http://www.cnblogs.com/dong008259/archive/2011/12/09/2282679.html 2.sed http://www.cnblogs.com ...
- Leetcode题解(九)
28.Implement strStr()-------KMP算法(*) 题目 这道题目其实就是实现KMP算法,并且该算法也是比较经典的算法,需要很好的掌握: 贴上几个介绍字符串匹配的算法说明链接 h ...
- Building roads
Building roads Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...
- 浅谈Jquery中的bind(),live(),delegate(),on()绑定事件方式 [转载]
前言 因为项目中经常会有利用jquery操作dom元素的增删操作,所以会涉及到dom元素的绑定事件方式,简单的归纳一下bind,live,delegate,on的区别,以便以后查阅,也希望该文章日后能 ...
- header操作cookie
root@kl20080094:~# curl -I "http://www.xxx.com" HTTP/1.1 200 OK Server: nginx/0.8.53 Date: ...