Kategorie
Benutzerdefiniertes Layout kann in zwei Situationen unterteilt werden.
Anpassen Sie ViewGroup und erstellen Sie einige ViewGroups, die sich von LinearLayout, RelativeLayout usw. unterscheiden. Zum Beispiel: GridLayout nach API 14 hinzugefügt, CoordinatorLayout in der Design-Support-Bibliothek usw.
Passen Sie einige vorhandene Layouts an und fügen Sie einige Sonderfunktionen hinzu. Zum Beispiel: TableLayout und PercentFrameLayout in Prozent unterstützen Bibliothek usw.
Prozess
Der Prozess der benutzerdefinierten Ansicht ist: onMeasure()->onLayout()->onDraw(). Beim Anpassen von ViewGroup ist es im Allgemeinen nicht erforderlich, onDraw zu implementieren. Natürlich können besondere Anforderungen gelten, wie zum Beispiel: CoordinatorLayout.
OnMeasure und onLayout können also grundsätzlich die meisten ViewGroups ausführen, mit denen wir in Kontakt kommen. Aber es reicht nicht aus, nur zu wissen, wie man die Größe der ViewGroup in onMeasure misst und die Position der Child View in onLayout berechnet.
Zum Beispiel: Wie können Sie Eigenschaften für die untergeordnete Ansicht einer ViewGroup festlegen?
Ein Beispiel.
Schreiben Sie eine benutzerdefinierte ViewGroup und fügen Sie ein Attribut hinzu, um die Größe (Länge und Breite) der untergeordneten Ansicht im Verhältnis zur ViewGroup zu steuern.
Angenommen, es handelt sich um ein LinearLayout, dann definieren Sie zuerst ein 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); } }
Abgesehen von anderen Dingen müssen wir Attribute hinzufügen, um die Größe der untergeordneten Ansicht zu steuern. Definieren Sie dann dieses Attribut zuerst in value/attr.xml (verwenden Sie CustomLinearLayout_Layout, um es von CustomLinearLayout zu unterscheiden, der Name ist natürlich willkürlich).
<declare-styleable name="CustomLinearLayout_Layout"> <!-- 定义比例 --> <attr name="inner_percent" format="float"/> </declare-styleable>
Diese Methode wird schließlich aufgerufen, wenn ViewGroup addView() aufruft.
public void addView(View child, int index, LayoutParams params)
Dieser Parameter stellt die Konfiguration von ViewGroup.LayoutParams dar, einschließlich Breite und Höhe, LinearLayout.LayoutParams fügt Gewichtsattribute usw. hinzu. Dann sollten wir ein LayoutParams implementieren. Das war's also vorerst.
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(); } } }
Jetzt können Sie unsere Attribute in XML verwenden.
<?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>
Es ist einfach weich und hat keine Wirkung.
Wie steuern Sie also die Größe der untergeordneten Ansicht? Natürlich wird es in onMeasure gesteuert. addView führt den folgenden Code aus.
requestLayout(); invalidate(true);
In diesem Fall werden onMeasure() und onLayout() erneut durchlaufen. Nach der Implementierung der onMeasure()-Methode werde ich die Größe der untergeordneten Ansicht direkt verarbeiten. Da ich LinearLayout erbe, wird MeasureChildBeforeLayout() tatsächlich verarbeitet. Der letzte Schritt besteht darin, die Größe der untergeordneten Ansicht beim Messen von ChildBeforeLayout zu berücksichtigen.
@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); } }
Auf diese Weise können die ersten Anforderungen erreicht werden.
Tatsächlich müssen noch einige Details verarbeitet werden, der folgende Code lautet.
/** * 当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); }
Um es zusammenzufassen
Worauf Sie beim Anpassen von View und ViewGroup mehr achten müssen, sind onMeasure, onLayout und onDraw.
Trennen Sie die Eigenschaften der ViewGroup selbst und die Eigenschaften der Child View.
Sie können auf den Code im Support-Paket verweisen, und das Debuggen ist ebenfalls sehr praktisch.
Bei der Android-Entwicklung gibt es tatsächlich viele Stellen, an denen Sie Ansichten anpassen müssen, aber die meisten davon verfügen über entsprechende Open-Source-Bibliotheken. Aber wir müssen immer noch wissen, wie man eine ViewGroup anpasst.
Weitere Artikel zur Zusammenfassung der Verwendung benutzerdefinierter ViewGroup in Android finden Sie auf der chinesischen PHP-Website!