Android中使用自定义ViewGroup的总结
作者:CoolEgos 发布时间:2021-12-18 04:25:39
分类
自定义Layout可以分为两种情况。
自定义ViewGroup,创造出一些不同于LinearLayout,RelativeLayout等之类的ViewGroup。比如:API 14以后增加的GridLayout、design support library中的CoordinatorLayout等等。
自定义一些已经有的Layout然后加一些特殊的功能。比如:TableLayout以及percent support library中的PercentFrameLayout等等。
流程
自定义View的流程是:onMeasure()->onLayout()->onDraw()。自定义ViewGroup的时候一般是不要去实现onDraw的,当然也可能有特殊的需求,比如:CoordinatorLayout。
所以onMeasure和onLayout基本能做大部分我们接触的ViewGroup。但是仅仅的知道在onMeasure中测量ViewGroup的大小以及在onLayout中计算Child View的位置还是不够。
比如:怎么可以给ViewGroup的Child View设置属性?
一个例子。
写一个自定义的ViewGroup,增加一个属性控制Child View的大小(长宽)占ViewGroup的比例。
假设是一个LinearLayout,那么就先定义一个CustomLinearLayout。
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
其它抛开不说,我们是需要增加属性用来控制Child View的大小。那么就先在value/attr.xml中定义那个属性(使用CustomLinearLayout_Layout来与CustomLinearLayout区分下,当然这个名字是随意的)。
<declare-styleable name="CustomLinearLayout_Layout">
<!-- 定义比例 -->
<attr name="inner_percent" format="float"/>
</declare-styleable>
ViewGroup调用addView()的时候最终都会调用到这个方法。
public void addView(View child, int index, LayoutParams params)
这个params代表的就是View的配置,ViewGroup.LayoutParams中就包含了width、height,LinearLayout.LayoutParams增加了weight属性等等。那么我们就应该实现一个LayoutParams。那么现在就是这样了。
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public static class LayoutParams extends LinearLayout.LayoutParams {
private float innerPercent;
private static final int DEFAULT_WIDTH = WRAP_CONTENT;
private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
public LayoutParams() {
super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
innerPercent = -1.0f;
}
public LayoutParams(float innerPercent) {
super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
this.innerPercent = innerPercent;
}
public LayoutParams(ViewGroup.LayoutParams p) {
super(p);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public LayoutParams(LinearLayout.LayoutParams source) {
super(source);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public LayoutParams(LayoutParams source) {
super(source);
this.innerPercent = source.innerPercent;
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
init(c, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLinearLayout_Layout);
innerPercent = a.getFloat(R.styleable.CustomLinearLayout_Layout_inner_percent, -1.0f);
a.recycle();
}
}
}
现在就可以在xml使用我们的属性了。
<?xml version="1.0" encoding="utf-8"?>
<com.egos.samples.custom_layout.CustomLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="200dp"
android:layout_height="200dp"
android:id="@+id/test_layout"
android:background="#ffff0000"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:text="Egos"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="add"
android:background="#ff00ff00"
app:inner_percent="0.8"/>
</com.egos.samples.custom_layout.CustomLinearLayout>
只是然并软,并没有作用。
那么到底是怎么去控制Child View的大小呢?当然是在onMeasure控制的。addView会执行下面的代码。
requestLayout();
invalidate(true);
这样的话就会重新的走一遍onMeasure(),onLayout()了。实现onMeasure()的方法以后直接去处理Child View的大小,因为我继承的是LinearLayout,所以其实是会处理到measureChildBeforeLayout()。最终是在measureChildBeforeLayout的时候来处理Child View的大小。
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// 在xml强制写成match_parent,然后在这里强制设置成
if (child != null && child.getLayoutParams() instanceof LayoutParams &&
((LayoutParams) child.getLayoutParams()).innerPercent != -1.0f) {
parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentWidthMeasureSpec) *
((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentWidthMeasureSpec));
parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentHeightMeasureSpec) *
((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentHeightMeasureSpec));
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
} else {
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
}
}
这样就可以实现最开始的需求了。
其实还有一些细节是需要处理的,下面的代码就是。
/**
* 当checkLayoutParams返回false的时候就会执行到这里的generateLayoutParams
*/
@Override
protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
return super.generateLayoutParams(lp);
}
/**
* 当addView的时候没有设置LayoutParams的话就会默认执行这里的generateDefaultLayoutParams
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
/**
* 写在xml中属性的时候就会执行这里的generateLayoutParams
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
总结一下
自定义View和ViewGroup需要多多注意的都是onMeasure、onLayout、onDraw。
把ViewGroup自身的属性和Child View的属性区分开。
可以多多的参考support包中的代码,调试也非常的方便。
做Android开发,自身需要自定义View的地方确实是比较的多,只是大部分都会有相应的开源库。但是我们还是应该需要熟练的知道该如何自定义一个ViewGroup。
自己是一个比较健忘的人,所以写的详细点。
完整的代码戳这里。
来源:http://blog.csdn.net/a38017032/article/details/54426616
猜你喜欢
- 声明:作者是根据 Hongyang的博客自己实践之后,根据自己的理解写的,有什么不对的地方还望指正。 先放两张效果图 一、准备由于Andro
- 前言C# 语言是在2000发布的,至今已正式发布了7个版本,每个版本都包含了许多令人兴奋的新特性和功能更新。同时,C# 每个版本的发布都与同
- Dataway介绍Dataway 是基于 DataQL 服务聚合能力,为应用提供的一个接口配置工具。使得使用者无需开发任何代码就配置一个满足
- 在很多项目中,为了安全安全考虑,需要对数据包进行加密处理,本文实例所述的即为C#加密代码,在应用开发中有很大的实用价值。说起数据包加密,其实
- 作为一个ORM框架,hibernate肯定也需要满足我们实现表与表之间进行关联的需要。hibernate在关联方法的实现很简单。下面我们先来
- Apache 和 Tomcat 都是web网络服务器,两者既有联系又有区别,在进行HTML、PHP、JSP、P
- 用C#如何生成二维码,我们可以通过现有的第三方dll直接来实现,下面列出几种不同的生成方法:1.通过QrCodeNet(Gma.QrCode
- 一、Collection集合Collection接口是单列集合类的父接口,这种集合可以将数据一个一个的存放到集合中。它有两个重要的子接口,分
- RestTemplate简介Spring RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTem
- redis redisson 集合操作相关类及接口Rlist:链表public interface RList<V> exten
- 应用开发的时候,有时我们需要将一些图片进行预览,例如:相片管理的应用。这个时候用ListView的话就显得不是太合适了,因为Li
- 1. List1.1 List 的常见方法方法描述boolean add(E e)尾插 evoid add(int index, E ele
- RecyclerView显示Item布局不一致在自定义RecyclerAdapter的时候,在重写onCreateViewHolder方法是
- 上篇文章老王买产品 我们从最原始的基本实现方法,到简单(静态)工厂,然后使用工厂方法设计模式进行改造,最后考虑产品会产生变体,我们
- 概念 基本映射是对一个实体进行映射,关联映射就是处理多个实体之间的关
- 1.XxlJob简介官方网址:https://www.xuxueli.com/xxl-jobXXL-JOB是一个分布式任务调度平台,其核心设
- C# 的类型转换有显式转型 和 隐式转型 两种方式。显式转型:有可能引发异常、精确度丢失及其他问题的转换方式。需要使用手段进行转换操作。隐式
- 本文实例讲述了C#(asp.net)多线程用法。分享给大家供大家参考,具体如下:using System;using System.Coll
- 引言目前很多系统为了解决数据读写的性能瓶颈,在系统架构设计中使用Redis实现缓存,Spring框架为了让开发人员更加方便快捷的使用Redi
- Oracle的jdbc驱动三种主要分类:1、JDBC OCI: oci是oracle call interface的缩写,此驱动类似于传统的