Android中使用自定义ViewGroup的总结

分类

自定义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。

自己是一个比较健忘的人,所以写的详细点。

完整的代码戳这里。

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

时间: 2024-11-05 19:30:49

Android中使用自定义ViewGroup的总结的相关文章

Android 中TabLayout自定义选择背景滑块的实例代码_Android

 TabLayout是Android 的Material Design包中的一个控件,可以和V4包中的ViewPager搭配产生一个联动的效果.这里我自定义了一个滑块能够跟随TabLayout进行滑动选择的SliderLayout.效果见下图(白色方框): 下面是SliderLayout的源码: import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawab

Android 中TabLayout自定义选择背景滑块的实例代码

TabLayout是Android 的Material Design包中的一个控件,可以和V4包中的ViewPager搭配产生一个联动的效果.这里我自定义了一个滑块能够跟随TabLayout进行滑动选择的SliderLayout.效果见下图(白色方框): 下面是SliderLayout的源码: import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawabl

Android中DialogFragment自定义背景与宽高的方法

介绍 DialogFragment在android 3.0时被引入.是一种特殊的Fragment,用于在Activity的内容之上展示一个模态的对话框.典型的用于:展示警告框,输入框,确认框等等. 在DialogFragment产生之前,我们创建对话框:一般采用AlertDialog和Dialog.注:官方不推荐直接使用Dialog创建对话框. 本文主要给大家介绍了关于Android中DialogFragment自定义背景与宽高的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的

解析Android中使用自定义字体的实现方法_Android

1.Android系统默认支持三种字体,分别为:"sans", "serif", "monospace 2.在Android中可以引入其他字体 . 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?><TableLayout xmlns:Android="http://schemas.android.com/apk/res/android&qu

深入理解Android中View和ViewGroup

深入理解Android中View 这回我们是深入到View内部,去研究View,去了解View的工作,抛弃其他因素,以便为以后能灵活的使用自定义空间打下一定的基础.希望有志同道合的朋友一起来探讨,深入Android内部,深入理解Android. 一.View是什么? View是什么了,每个人都有自己的理解.在Android的官方文档中是这样描述的:这个类表示了用户界面的基本构建模块.一个View占用了屏幕上的一个矩形区域并且负责界面绘制和事件处理.View是用来构建用户界面组件(Button,T

解析Android中使用自定义字体的实现方法

1.Android系统默认支持三种字体,分别为:"sans", "serif", "monospace 2.在Android中可以引入其他字体 .复制代码 代码如下:<?xml version="1.0" encoding="utf-8"?><TableLayout xmlns:Android="http://schemas.android.com/apk/res/android"

Android中制作自定义dialog对话框的实例分享_Android

自定义dialog基础版很多时候,我们在使用android sdk提供的alerdialog的时候,会因为你的系统的不同而产生不同的效果,就好比如你刷的是MIUI的系统,弹出框都会在顶部显示!这里简单的介绍自定义弹出框的应用. 首先创建布局文件dialog: 代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.andr

Android中PopupWindow自定义坐标实现

Android中PopupWindow位置的确定一般通过showAsDropDown函数来实现,该函数有两个重载函数,分别定义如下: [java] view plaincopyprint? public void showAsDropDown(View anchor) {       showAsDropDown(anchor, 0, 0);   }      public void showAsDropDown(View anchor, int xoff, int yoff) {       

Android中制作自定义dialog对话框的实例分享

自定义dialog基础版 很多时候,我们在使用android sdk提供的alerdialog的时候,会因为你的系统的不同而产生不同的效果,就好比如你刷的是MIUI的系统,弹出框都会在顶部显示!这里简单的介绍自定义弹出框的应用. 首先创建布局文件dialog: 代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.and