热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Android自定义ViewGroup之CustomGridLayout(一)

这篇文章主要为大家详细介绍了Android自定义ViewGroup之CustomGridLayout的相关资料,感兴趣的小伙伴们可以参考一下

之前写了两篇关于自定义view的文章,本篇讲讲自定义ViewGroup的实现。

我们知道ViewGroup就是View的容器类,我们经常用的LinearLayout,RelativeLayout等都是ViewGroup的子类。并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是为用于告诉容器的),我们的宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等;于是乎,ViewGroup的职能为:给childView计算出建议的宽和高和测量模式 ;决定childView的位置;为什么只是建议的宽和高,而不是直接确定呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高。

View的根据ViewGroup传入的测量值和模式,对自己宽高进行确定(onMeasure中完成),然后在onDraw中完成对自己的绘制。ViewGroup需要给View传入view的测量值和模式(onMeasure中完成),而且对于此ViewGroup的父布局,自己也需要在onMeasure中完成对自己宽和高的确定。此外,需要在onLayout中完成对其childView的位置的指定。

因为ViewGroup有很多子View,所以它的整个绘制过程相对于View会复杂一点,但是还是遵循三个步骤measure,layout,draw,我们依次说明。
本文我们来写一个类似于GridView的网格容器吧,姑且叫做CustomGridView。

自定义属性/获取属性值

<&#63;xml version="1.0" encoding="utf-8"&#63;>

 
 
 
 
 


 public CustomGridView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 if (attrs != null) {
  TypedArray a = getContext().obtainStyledAttributes(attrs,
   R.styleable.CustomGridView);
  colums = a.getInteger(R.styleable.CustomGridLayout_numColumns, 3);
  hSpace = a.getInteger(R.styleable.CustomGridLayout_hSpace, 10);
  vSpace = a.getInteger(R.styleable.CustomGridLayout_vSpace, 10);
  a.recycle();
 }
 }

 public MyGridLayout(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
 }

 public MyGridLayout(Context context) {
 this(context, null);
 }

LayoutParams

ViewGroup还有一个很重要的知识LayoutParams,LayoutParams存储了子View在加入ViewGroup中时的一些参数信息,在继承ViewGroup类时,一般也需要新建一个新的LayoutParams类,就像SDK中我们熟悉的LinearLayout.LayoutParams,RelativeLayout.LayoutParams类等一样,那么可以这样做,在你定义的ViewGroup子类中,新建一个LayoutParams类继承与ViewGroup.LayoutParams。

public static class LayoutParams extends ViewGroup.LayoutParams {
  public int left = 0;
  public int top = 0;

  public LayoutParams(Context arg0, AttributeSet arg1) {
  super(arg0, arg1);
  }

  public LayoutParams(int arg0, int arg1) {
  super(arg0, arg1);
  }

  public LayoutParams(android.view.ViewGroup.LayoutParams arg0) {
  super(arg0);
  } 
 }

那么现在新的LayoutParams类已经有了,如何让我们自定义的ViewGroup使用我们自定义的LayoutParams类来添加子View呢,ViewGroup同样提供了下面这几个方法供我们重写,我们重写返回我们自定义的LayoutParams对象即可。

 @Override
 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
 return new CustomGridLayout.LayoutParams(getContext(), attrs);
 }

 @Override
 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
 }

 @Override
 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
 return new LayoutParams(p);
 }

 @Override
 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
 return p instanceof CustomGridLayout.LayoutParams;
 }

measure
在onMeasure中需要做两件事:
 &#8226;计算childView的测量值以及模式
measureChildren(widthMeasureSpec, heightMeasureSpec);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
child.measure(WidthMeasureSpec, HeightMeasureSpec);
 &#8226;设置ViewGroup自己的宽和高
测量ViewGroup的大小,如果layout_width和layout_height是match_parent或具体的xxxdp,就很简答了,直接调用setMeasuredDimension()方法,设置ViewGroup的宽高即可,如果是wrap_content,就比较麻烦了,我们需要遍历所有的子View,然后对每个子View进行测量,然后根据子View的排列规则,计算出最终ViewGroup的大小。
注意:在自定义View第一篇讲SpecMode时,曾说到UNSPECIFIED一般都是父控件是AdapterView,通过measure方法传入的模式。在这里恰好就用到了。 

@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);

 //UNSPECIFIED一般都是父控件是AdapterView,通过measure方法传入的模式
 final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(sizeWidth, MeasureSpec.UNSPECIFIED);
 final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(sizeHeight, MeasureSpec.UNSPECIFIED);
 measureChildren(childWidthMeasureSpec, childHeightMeasureSpec);

 int childCount = this.getChildCount();
 int line = childCount % colums == 0 &#63; childCount / colums : (childCount + colums) / colums;

 //宽布局为wrap_content时,childWidth取childView宽的最大值,否则动态计算
 if (widthMode == MeasureSpec.AT_MOST) {
  for (int i = 0; i 

layout
最核心的就是调用layout方法,根据我们measure阶段获得的LayoutParams中的left和top字段,也很好对每个子View进行位置排列。

@Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
 int childCount = this.getChildCount();
 for (int i = 0; i 

draw
ViewGroup在draw阶段,其实就是按照子类的排列顺序,调用子类的onDraw方法,因为我们只是View的容器,本身一般不需要draw额外的修饰,所以往往在onDraw方法里面,只需要调用ViewGroup的onDraw默认实现方法即可。不需要重写。

最后,在自定义ViewGroup中定义GridAdatper接口,以便在外部可以为ViewGroup设置适配器。

 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 

并且在自定义ViewGroup中定义OnItemClickListener接口,以便在外部可以获取到childView的点击事件。

public interface OnItemClickListener {
 void onItemClick(View v, int index);
 }

 public void setOnItemClickListener(final OnItemClickListener listener) {
 if (this.adapter == null)
  return;
 for (int i = 0; i 

使用自定义的CustomViewGroup

布局文件:

<&#63;xml version="1.0" encoding="utf-8"&#63;>


 


grid_item:

<&#63;xml version="1.0" encoding="utf-8"&#63;>

 


 Java文件:

protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 grid = (CustomGridLayout) findViewById(R.id.gridview);
 grid.setGridAdapter(new GridAdatper() {
  @Override
  public View getView(int index) {
  View view = getLayoutInflater().inflate(R.layout.grid_item, null);
  ImageView iv = (ImageView) view.findViewById(R.id.iv);
  iv.setImageResource(srcs[index]);
  return view;
  }

  @Override
  public int getCount() {
  return srcs.length;
  }
 });
 grid.setOnItemClickListener(new OnItemClickListener() {
  @Override
  public void onItemClick(View v, int index) {
  Toast.makeText(MainActivity.this, "item="+index, Toast.LENGTH_SHORT).show();
  }
 });
 }
}

运行后效果图如下:

这里写图片描述

改变一下布局:



这里写图片描述

再改变



这里写图片描述

再变



这里写图片描述

Demo下载地址:http://xiazai.jb51.net/201609/yuanma/CustomGridLayout(jb51.net).rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 深入解析 Android 选择器与形状绘制技术
    本文深入探讨了 Android 中选择器(Selector)与形状绘制(Shape Drawing)技术的应用与实现。重点分析了 `Selector` 的 `item` 元素,其中包括 `android:drawable` 属性的使用方法及其在不同状态下的表现。此外,还详细介绍了如何通过 XML 定义复杂的形状和渐变效果,以提升 UI 设计的灵活性和美观性。 ... [详细]
  • 在基于.NET框架的分层架构实践中,为了实现各层之间的松散耦合,本文详细探讨了依赖注入(DI)和控制反转(IoC)容器的设计与实现。通过合理的依赖管理和对象创建,确保了各层之间的单向调用关系,从而提高了系统的可维护性和扩展性。此外,文章还介绍了几种常见的IoC容器实现方式及其应用场景,为开发者提供了实用的参考。 ... [详细]
  • ASP11:深入解析与应用展望本文详细探讨了 ASP11 中的 `AppRelativeTemplateSourceDirectory` 属性,该属性用于获取或设置包含控件的 Page 或 UserControl 对象的应用程序相对虚拟目录。此外,文章还介绍了 1.0 版本中的 Binding 机制,分析了其在实际开发中的应用和优化方法,为开发者提供了全面的技术指导。 ... [详细]
  • 使用CardView实现圆角和圆形效果:边角与半径的精准控制 ... [详细]
  • 本文介绍了Android动画的基本概念及其主要类型。Android动画主要包括三种形式:视图动画(也称为补间动画或Tween动画),主要通过改变视图的属性来实现动态效果;帧动画,通过顺序播放一系列预定义的图像来模拟动画效果;以及属性动画,通过对对象的属性进行平滑过渡来创建更加复杂的动画效果。每种类型的动画都有其独特的应用场景和实现方式,开发者可以根据具体需求选择合适的动画类型。 ... [详细]
  • 通过自定义 `TextView`,实现了在用户点击或焦点变化时动态调整字体颜色的效果。该方法利用了 `ColorStateList` 和 `Selector` 资源文件,确保了界面交互的流畅性和视觉效果的提升。具体实现中,通过重写 `onTouchEvent` 和 `onFocusChanged` 方法,精确控制了颜色变化的时机和状态。此外,还对性能进行了优化,确保在高频率操作下依然保持高效响应。 ... [详细]
  • 解决基于XML配置的MyBatis在Spring整合中出现“无效绑定语句(未找到):com.music.dao.MusicDao.findAll”问题的方法
    在将Spring与MyBatis进行整合时,作者遇到了“无效绑定语句(未找到):com.music.dao.MusicDao.findAll”的问题。该问题主要出现在使用XML文件配置DAO层的情况下,而注解方式配置则未出现类似问题。作者详细分析了两个配置文件之间的差异,并最终找到了解决方案。本文将详细介绍问题的原因及解决方法,帮助读者避免类似问题的发生。 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • Android 图像色彩处理技术详解
    本文详细探讨了 Android 平台上的图像色彩处理技术,重点介绍了如何通过模仿美图秀秀的交互方式,利用 SeekBar 实现对图片颜色的精细调整。文章展示了具体的布局设计和代码实现,帮助开发者更好地理解和应用图像处理技术。 ... [详细]
  • Spring框架入门指南:专为新手打造的详细学习笔记
    Spring框架是Java Web开发中广泛应用的轻量级应用框架,以其卓越的功能和出色的性能赢得了广大开发者的青睐。本文为初学者提供了详尽的学习指南,涵盖基础概念、核心组件及实际应用案例,帮助新手快速掌握Spring框架的核心技术与实践技巧。 ... [详细]
  • 本文探讨了在Android应用中实现动态滚动文本显示控件的优化方法。通过详细分析焦点管理机制,特别是通过设置返回值为`true`来确保焦点不会被其他控件抢占,从而提升滚动文本的流畅性和用户体验。具体实现中,对`MarqueeText.java`进行了代码层面的优化,增强了控件的稳定性和兼容性。 ... [详细]
  • 本文探讨了提升项目效能与质量的综合优化策略。通过系统分析项目管理流程,结合先进的技术手段和管理方法,提出了多项具体措施,旨在提高项目的执行效率和最终交付质量。这些策略包括但不限于优化资源配置、加强团队协作、引入自动化工具以及实施持续改进机制,为项目成功提供了坚实的保障。 ... [详细]
  • 本研究提出了一种方法,用于判断两个数组中的元素是否相同,而不考虑其顺序。该方法通过检查数组中每个元素的出现次数来实现。具体实现如下:首先验证输入参数是否为数组,然后对两个数组进行排序并逐个比较元素。若所有元素均相等,则返回 `true`,否则返回 `false`。此方法适用于需要忽略顺序的数组比较场景。 ... [详细]
  • 题目链接:http://poj.org/problem?id=3083。题目描述:给定一个迷宫,其中 'S' 表示起点,'E' 表示终点,'#' 表示墙壁,'.' 表示可通行的道路。起点和终点均位于迷宫的边界上,并且保证存在唯一路径。任务是求从起点 'S' 到终点 'E' 的最短路径步数,且优先考虑向左转弯。通过深度优先搜索(DFS)和广度优先搜索(BFS)算法进行路径探索,分析两种方法的优劣及适用场景。 ... [详细]
  • 在Linux环境下编译安装Heartbeat时,常遇到依赖库缺失的问题。为确保顺利安装,建议预先通过yum安装必要的开发库,如glib2-devel、libtool-ltdl-devel、net-snmp-devel、bzip2-devel和ncurses-devel等。这些库是编译过程中不可或缺的组件,能够有效避免编译错误,确保Heartbeat的稳定运行。 ... [详细]
author-avatar
fjfzfisher
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有