安卓自定义控件(四)实现自定义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无法实现我们的布局时,我们就可以 ...
随机推荐
- hdu 1018 共同交流~
Big Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total ...
- .NET企业级应用WebService上传下载文件
在建立好一个WebService后会有一个自带的 [WebMethod]//在待会写的所有方法中都要写这个,便于调试 public string HelloWorld() { return " ...
- 【Win 10 应用开发】在代码中加载文本资源
记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...
- 相对定位的div没有出现纵向滚动条
在一个相对定位的div中绝对定位很多html元素,纵向没有滚动条出现.原因:我们期望作为原点的点在窗口原点的下方,但是滚动条以窗口原点作为原点,所以没有出现滚动条.解决:在div外面再套一个div,o ...
- python学习笔记(十一)之函数
牛刀小试: 定义一个无参函数 >>> def myFirstFunc(): ... print("Hello python") ... print("h ...
- Introduction of Git, Github and Gitlab
========================================================================== Version control is a syst ...
- Problem F
Problem Description "Yakexi, this is the best age!" Dong MW works hard and get high pay, h ...
- Windows解决anaconda下双python版本安装TensorFlow
首先,就是双版本anaconda的安装: 以前安装好的是python2.7版本,而TensorFlow的安装仅支持3.5版本的.但是自己本来的2.7版本又不想遗弃.所以安装双版本的: 在anacond ...
- 学习笔记TF060:图像语音结合,看图说话
斯坦福大学人工智能实验室李飞飞教授,实现人工智能3要素:语法(syntax).语义(semantics).推理(inference).语言.视觉.通过语法(语言语法解析.视觉三维结构解析)和语义(语言 ...
- 【转载】以Java的视角来聊聊SQL注入
以Java的视角来聊聊SQL注入 原创 2017-08-08 javatiku Java面试那些事儿 在大二就接触过sql注入,之前一直在学习windows逆向技术,认为web安全以后不是自己的从业方 ...