In Android werden mehrere gängige ViewGroup-Implementierungen bereitgestellt, darunter LinearLayout, Relativeayout, FrameLayout usw. Diese ViewGroups können unsere allgemeinen Entwicklungsanforderungen erfüllen, für komplexe Schnittstellenanforderungen reichen diese Layouts jedoch nicht aus. Daher gibt es in den Anwendungen, mit denen wir in Kontakt gekommen sind, zahlreiche benutzerdefinierte ViewGroups.
Um eine benutzerdefinierte ViewGroup zu implementieren, besteht der erste Schritt darin, benutzerdefinierte Attribute zu lernen. Diese benutzerdefinierten Attribute machen uns flexibler bei der Konfiguration der Layoutdatei. Benutzerdefinierte Attribute werden in einer attrs.xml-Datei im Werteverzeichnis deklariert.
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CascadeViewGroup"> <attr name="verticalspacing" format="dimension"/> <attr name="horizontalspacing" format="dimension"/> </declare-styleable> <declare-styleable name="CascadeViewGroup_LayoutParams"> <attr name="layout_paddingleft" format="dimension"/> <attr name="layout_paddinTop" format="dimension"/> </declare-styleable> </resources>
Hier deklarieren wir zwei benutzerdefinierte Eigenschaftssätze für unsere benutzerdefinierte CascadeViewGroup-Komponente, die im Tag
Bevor wir den Code schreiben, legen wir außerdem eine Standardbreite und -höhe fest, die CascadeLayout verwenden soll. Diese beiden Eigenschaften sind in dimens.xml definiert.
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="default_horizontal_spacing">10dp</dimen> <dimen name="default_vertical_spacing">10dp</dimen> </resources>
Jetzt beginnen wir mit dem Schreiben der benutzerdefinierten Komponente CascadeLayout.
package com.app.CustomViewMotion; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * Created by charles on 2015/8/13. */ public class CascadeViewGroup extends ViewGroup { //自定义布局中设置的宽度和高度 private int mHoriztonalSpacing; private int mVerticalSpacing; public CascadeViewGroup(Context context) { this(context, null); } public CascadeViewGroup(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CascadeViewGroup(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CascadeViewGroup); try { //获取设置的宽度 mHoriztonalSpacing = a.getDimensionPixelSize(R.styleable.CascadeViewGroup_horizontalspacing, this.getResources().getDimensionPixelSize(R.dimen.default_horizontal_spacing)); //获取设置的高度 mVerticalSpacing = a.getDimensionPixelSize(R.styleable.CascadeViewGroup_verticalspacing, this.getResources().getDimensionPixelSize(R.dimen.default_vertical_spacing)); } catch (Exception e) { e.printStackTrace(); } finally { a.recycle(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int count = this.getChildCount(); int width = this.getPaddingLeft(); int height = this.getPaddingTop(); for (int i = 0; i < count; i++) { final View currentView = this.getChildAt(i); this.measureChild(currentView, widthMeasureSpec, heightMeasureSpec); CascadeViewGroup.LayoutParams lp = (CascadeViewGroup.LayoutParams) currentView.getLayoutParams(); if(lp.mSettingPaddingLeft != 0){ width +=lp.mSettingPaddingLeft; } if(lp.mSettingPaddingTop != 0){ height +=lp.mSettingPaddingTop; } lp.x = width; lp.y = height; width += mHoriztonalSpacing; height += mVerticalSpacing; } width +=getChildAt(this.getChildCount() - 1).getMeasuredWidth() + this.getPaddingRight(); height += getChildAt(this.getChildCount() - 1).getMeasuredHeight() + this.getPaddingBottom(); this.setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec)); } @Override protected void onLayout(boolean b, int l, int i1, int i2, int i3) { final int count = this.getChildCount(); for (int i = 0; i < count; i++) { final View currentView = this.getChildAt(i); CascadeViewGroup.LayoutParams lp = (CascadeViewGroup.LayoutParams) currentView.getLayoutParams(); currentView.layout(lp.x, lp.y, lp.x + currentView.getMeasuredWidth(), lp.y + currentView.getMeasuredHeight()); } } public static class LayoutParams extends ViewGroup.LayoutParams { int x; int y; int mSettingPaddingLeft; int mSettingPaddingTop; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.CascadeViewGroup_LayoutParams); mSettingPaddingLeft = a.getDimensionPixelSize(R.styleable.CascadeViewGroup_LayoutParams_layout_paddingleft, 0); mSettingPaddingTop = a.getDimensionPixelSize(R.styleable.CascadeViewGroup_LayoutParams_layout_paddinTop, 0); a.recycle(); } public LayoutParams(int width, int height) { super(width, height); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } } @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 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(this.getContext(), attrs); } }
Der Code ist etwas länger, aber die Struktur ist immer noch sehr klar.
1) Der Wert des Konfigurationsattributs im Konstruktor oder in der XML-Datei. Rufen Sie die Eigenschaften, die wir im Layout festlegen, über die Methoden in TypedArray ab und speichern Sie sie in Mitgliedsvariablen.
2) Erstellen Sie eine benutzerdefinierte innere Klasse LayoutParams. Durch die Konstruktion dieser internen Klasse können wir ihre Attributwerte speichern, wenn wir unsere Unteransichten für das Layout in der Layoutphase messen.
3) GenerateLayoutParams(), GenerateDefaultParams() und andere Methoden. Geben Sie unsere benutzerdefinierten layoutParams in diesen Methoden zurück. Warum diese Methoden überschrieben werden müssen, wird durch einen Blick auf die addView()-Methode der ViewGroup-Klasse deutlich.
4) Messphase. In der Messphase messen wir unsere eigene Größe sowie die Größe der Unteransicht und speichern die Informationen der Unteransicht in LayoutParams.
5) Layoutphase. Legen Sie ihre Positionen basierend auf den Informationen jeder Unteransicht fest.
Fügen Sie abschließend die Layoutdatei hinzu.
<?xml version="1.0" encoding="utf-8"?> <!--添加自定义属性给viewGroup--> <!--新添加的命名空间的后缀必须保持和.xml中声明的包名一致--> <com.app.CustomViewMotion.CascadeViewGroup xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ts="http://schemas.android.com/apk/res/com.app.CustomViewMotion" android:layout_width="match_parent" android:layout_height="match_parent" ts:horizontalspacing="15dp" ts:verticalspacing="15dp"> <TextView android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:text="text1" android:background="#668B8B"/> <TextView android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:text="text2" android:background="#FFDAB9"/> <TextView android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:text="text3" android:background="#43CD80"/> <!--这个子view中添加自定义子view属性--> <TextView android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:text="text4" ts:layout_paddingleft="100dp" ts:layout_paddinTop="100dp" android:background="#00CED1"/> </com.app.CustomViewMotion.CascadeViewGroup>
Der erzielte Effekt ist wie folgt:
Das Obige ist der gesamte Inhalt. Ich hoffe, er kann jedem eine Referenz geben, und ich auch Ich hoffe, dass jeder die chinesische PHP-Website unterstützt.
Weitere Artikel zur Implementierungsmethode der benutzerdefinierten Android ViewGroup finden Sie auf der chinesischen PHP-Website!