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

这样就可以实现最开始的需求了。

Android中使用自定义ViewGroup的总结

其实还有一些细节是需要处理的,下面的代码就是。


/**
* 当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

标签:自定义,viewgroup
0
投稿

猜你喜欢

  • c# 通过经纬度查询 具体的地址和区域名称

    2023-08-15 11:41:53
  • java面试题之数组中的逆序对

    2021-08-05 10:51:02
  • 基于Java实现的图的广度优先遍历算法

    2021-06-02 06:51:20
  • Java开发岗位面试被问到反射怎么办

    2023-01-07 00:09:12
  • java并发编程包JUC线程同步CyclicBarrier语法示例

    2023-01-18 07:44:58
  • Android 去掉自定义dialog的白色边框的简单方法

    2022-01-05 06:57:54
  • Android多媒体之VideoView视频播放器

    2023-11-12 14:38:35
  • C# 一个WCF简单实例

    2023-07-12 20:53:43
  • 详解MyBatis 常用写法

    2022-07-14 19:36:14
  • 关于工厂方法模式的Java实现

    2021-12-20 18:06:13
  • android选择视频文件上传到后台服务器

    2023-06-11 22:50:44
  • 基于WPF实现步骤控件的示例代码

    2021-09-12 15:16:16
  • SpringBoot动态Feign服务调用详解

    2022-05-20 13:26:23
  • Android 使用CoordinatorLayout实现滚动标题栏效果的实例

    2021-11-16 07:47:09
  • C#中使用split分割字符串的几种方法小结

    2023-06-18 17:23:21
  • Flutter 实现整个App变为灰色的方法示例

    2023-02-21 13:20:39
  • 详解SpringBoot项目整合Vue做一个完整的用户注册功能

    2022-02-13 21:46:35
  • C++语言实现线性表之链表实例

    2023-06-20 22:21:20
  • Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动

    2023-09-21 00:16:18
  • 浅析Java中Map与HashMap,Hashtable,HashSet的区别

    2022-05-31 13:46:57
  • asp之家 软件编程 m.aspxhome.com