android自定义viewgroup实现等分格子布局
先上效果图:
实现这样的效果:
一般的思路就是,直接写布局文件,用LinearLayout 嵌套多层子LinearLayout,然后根据权重layout_weight可以达到上面的效果
还有就是利用gridview了,但是这里的需求就是不能上下滑动,使用gridview的时候还要计算布局的高度,否则内容超出下滑;
开始我是用的第一种,直接在布局文件实现了,但是后来发现代码太多太恶心哦,所以我继承viewGroup,重写两个关键的方法:onLayout(),onMeasure()
我的大致思路:
1.计算当前视图宽度和高度,然后根据边距,算出每个布局的item需要分配的多少宽度和高度:
2.支持adapter的方式,动态添加每一项,还可以设置每一项点击事件
好了,直接上关键代码:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
mMaxChildWidth = 0;
mMaxChildHeight = 0; int modeW = 0, modeH = 0;
if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED)
modeW = MeasureSpec.UNSPECIFIED;
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED)
modeH = MeasureSpec.UNSPECIFIED; final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), modeW);
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(heightMeasureSpec), modeH); count = getChildCount();
if (count == 0) {
super.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
return;
}
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
} child.measure(childWidthMeasureSpec, childHeightMeasureSpec); mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
mMaxChildHeight = Math.max(mMaxChildHeight,
child.getMeasuredHeight());
}
setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec),
resolveSize(mMaxChildHeight, heightMeasureSpec));
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
int height = b - t;// 布局区域高度
int width = r - l;// 布局区域宽度
int rows = count % colums == 0 ? count / colums : count / colums + 1;// 行数
if (count == 0)
return;
int gridW = (width - margin * (colums - 1)) / colums;// 格子宽度
int gridH = (height - margin * rows) / rows;// 格子高度 int left = 0;
int top = margin; for (int i = 0; i < rows; i++) {// 遍历行
for (int j = 0; j < colums; j++) {// 遍历每一行的元素
View child = this.getChildAt(i * colums + j);
if (child == null)
return;
left = j * gridW + j * margin;
// 如果当前布局宽度和测量宽度不一样,就直接用当前布局的宽度重新测量
if (gridW != child.getMeasuredWidth()
|| gridH != child.getMeasuredHeight()) {
child.measure(makeMeasureSpec(gridW, EXACTLY),
makeMeasureSpec(gridH, EXACTLY));
}
child.layout(left, top, left + gridW, top + gridH);
// System.out
// .println("--top--" + top + ",bottom=" + (top + gridH)); }
top += gridH + margin;
}
}
要实现adapter也很简单,自定义一个接口,下面给出完整的代码
package com.allen.view; import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup; import com.allen.mygridlayout.R; /**
* @author allen
* @email jaylong1302@163.com
* @date 2013-11-26 下午1:19:35
* @company 富媒科技
* @version 1.0
* @description 格子布局(类似4.0中的gridlayout)
*/
public class MyGridLayout extends ViewGroup {
private final String TAG = "MyGridLayout"; int margin = 2;// 每个格子的水平和垂直间隔
int colums = 2;
private int mMaxChildWidth = 0;
private int mMaxChildHeight = 0;
int count = 0; GridAdatper adapter; public MyGridLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.MyGridLayout);
colums = a.getInteger(R.styleable.MyGridLayout_numColumns, 2);
margin = (int) a.getInteger(R.styleable.MyGridLayout_itemMargin, 2);
}
} public MyGridLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public MyGridLayout(Context context) {
this(context, null);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
mMaxChildWidth = 0;
mMaxChildHeight = 0; int modeW = 0, modeH = 0;
if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED)
modeW = MeasureSpec.UNSPECIFIED;
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED)
modeH = MeasureSpec.UNSPECIFIED; final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), modeW);
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(heightMeasureSpec), modeH); count = getChildCount();
if (count == 0) {
super.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
return;
}
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
} child.measure(childWidthMeasureSpec, childHeightMeasureSpec); mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
mMaxChildHeight = Math.max(mMaxChildHeight,
child.getMeasuredHeight());
}
setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec),
resolveSize(mMaxChildHeight, heightMeasureSpec));
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
int height = b - t;// 布局区域高度
int width = r - l;// 布局区域宽度
int rows = count % colums == 0 ? count / colums : count / colums + 1;// 行数
if (count == 0)
return;
int gridW = (width - margin * (colums - 1)) / colums;// 格子宽度
int gridH = (height - margin * rows) / rows;// 格子高度 int left = 0;
int top = margin; for (int i = 0; i < rows; i++) {// 遍历行
for (int j = 0; j < colums; j++) {// 遍历每一行的元素
View child = this.getChildAt(i * colums + j);
if (child == null)
return;
left = j * gridW + j * margin;
// 如果当前布局宽度和测量宽度不一样,就直接用当前布局的宽度重新测量
if (gridW != child.getMeasuredWidth()
|| gridH != child.getMeasuredHeight()) {
child.measure(makeMeasureSpec(gridW, EXACTLY),
makeMeasureSpec(gridH, EXACTLY));
}
child.layout(left, top, left + gridW, top + gridH);
// System.out
// .println("--top--" + top + ",bottom=" + (top + gridH)); }
top += gridH + margin;
}
} public interface GridAdatper {
View getView(int index); int getCount();
} /** 设置适配器 */
public void setGridAdapter(GridAdatper adapter) {
this.adapter = adapter;
// 动态添加视图
int size = adapter.getCount();
for (int i = 0; i < size; i++) {
addView(adapter.getView(i));
}
} public interface OnItemClickListener {
void onItemClick(View v, int index);
} public void setOnItemClickListener(final OnItemClickListener click) {
if (this.adapter == null)
return;
for (int i = 0; i < adapter.getCount(); i++) {
final int index = i;
View view = getChildAt(i);
view.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
click.onItemClick(v, index);
}
});
}
} }
这里我给出完整的demo下载:click me!!!
android自定义viewgroup实现等分格子布局的更多相关文章
- android自定义viewgroup之我也玩瀑布流
先看效果图吧, 继上一篇<android自定义viewgroup实现等分格子布局>中实现的布局效果,这里稍微有些区别,每个格子的高度不规则,就是传说的瀑布流布局,一般实现这种效果,要么用第 ...
- Android自定义ViewGroup(四、打造自己的布局容器)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51500304 本文出自:[openXu的博客] 目录: 简单实现水平排列效果 自定义Layo ...
- Android自定义ViewGroup
视图分类就两类,View和ViewGroup.ViewGroup是View的子类,ViewGroup可以包含所有的View(包括ViewGroup),View只能自我描绘,不能包含其他View. 然而 ...
- Android自定义ViewGroup,实现自动换行
学习<Android开发艺术探索>中自定义ViewGroup章节 自定义ViewGroup总结的知识点 一.自定义ViewGroup中,onMeasure理解 onMeasure(int ...
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu
示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这里我简单说明一下用自定义ViewGroup来实现. 实现方法:我们自定义一个ViewGroup实现左右滑动, ...
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu[转]
http://blog.csdn.net/jj120522/article/details/8095852 示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这 ...
- android自定义viewgroup初步之一----抽屉菜单
转载请注明出处 http://blog.csdn.net/wingichoy/article/details/47832151 几天前在慕课网上看到鸿洋老师的 自定义卫星菜单,感觉很有意思,于是看完视 ...
- Android 自定义ViewGroup手把手教你实现ArcMenu
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37567907 逛eoe发现这样的UI效果,感觉很不错,后来知道github上有这 ...
- Android -- 自定义ViewGroup实现FlowLayout效果
1,在开发的时候,常在我们的需求中会有这种效果,添加一个商品的一些热门标签,效果图如下: 2,从上面效果可以看得出来,这是一个自定义的ViewGroup,然后实现换行效果,让我们一起来实现一下 自定义 ...
随机推荐
- Jpeg2000 简介
http://www.baike.com/wiki/Jpeg2000 总结Jpeg2000的六个方面: ⑴ JPEG2000可以方便地实现渐进式传输,这是JPEG2000的重要特征之一.看到这种 ...
- libevent (一) socket属性设置与初始化操作
socket属性设置与初始化操作 libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,内部使用select.epoll.kqueue等系统调用管理事件机制.著名 ...
- C语言宏高级用法 [总结]
1.前言 今天看代码时候,遇到一些宏,之前没有见过,感觉挺新鲜.如是上网google一下,顺便总结一下,方便以后学习和运用.C语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简 ...
- JS结构
当前页面的JS结构如下: <script> /* 这是立即执行的方法 */ (function () { /* 这是初始化表格 */ var init = fun ...
- JfreeChart使用(转载)
http://www.cnblogs.com/xingyun/ http://www.huosen.net/archives/156.html(此篇除了struts2外,还介绍了servlet下Jfr ...
- JavaScript事件详解-zepto的事件实现
zepto的event 可以结合上一篇JavaScript事件详解-原生事件基础(一)综合考虑源码暂且不表,github里还有中文网站都能下到最新版的zepto.整个event模块不长,274行,我们 ...
- paip 自定义输入法多多输入法词库的备份导出以及导入
paip 自定义输入法词库的备份导出以及导入 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/ ...
- paip.java 以及JavaScript (js) 的关系以及区别
paip.java 以及JavaScript (js) 的关系以及区别 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http:// ...
- Windows Error Code(windows错误代码详解)
0 操作成功完成. 1 功能错误. 2 系统找不到指定的文件. 3 系统找不到指定的路径. 4 系统无法打开文件. 5 拒绝访问. 6 句柄无效. 7 存储控制块被损坏. 8 存储空间不足,无法处理此 ...
- Linux内核--异常和中断的区别
相信大家都知道非常著名的两个名词:异常和中断,不过,你真的理解这两个名词在说什么吗?它们之间有什么区别呢? 1.中断 大家都知道,当我们在敲击键盘的同时就会产生中断 ...