Catégorie
La mise en page personnalisée peut être divisée en deux situations.
Personnalisez ViewGroup et créez des ViewGroups différents de LinearLayout, RelativeLayout, etc. Par exemple : GridLayout ajouté après l'API 14, CoordinationLayout dans la bibliothèque de support de conception, etc.
Personnalisez certaines mises en page existantes et ajoutez des fonctions spéciales. Par exemple : TableLayout et PercentFrameLayout dans la bibliothèque de support en pourcentage, etc.
Processus
Le processus de vue personnalisée est : onMeasure()->onLayout()->onDraw(). Lors de la personnalisation de ViewGroup, il n'est généralement pas nécessaire d'implémenter onDraw. Bien entendu, il peut y avoir des exigences particulières, telles que : CoordinationLayout.
Donc, onMeasure et onLayout peuvent essentiellement réaliser la plupart des ViewGroups avec lesquels nous entrons en contact. Mais il ne suffit pas de savoir comment mesurer la taille du ViewGroup dans onMeasure et calculer la position de la Child View dans onLayout.
Par exemple : Comment définir les propriétés de la vue enfant d'un ViewGroup ?
Un exemple.
Écrivez un ViewGroup personnalisé et ajoutez un attribut pour contrôler la taille (longueur et largeur) de la vue enfant proportionnellement au ViewGroup.
En supposant qu'il s'agisse d'un LinearLayout, définissez d'abord un 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); } }
Autrement dit, nous devons ajouter des attributs pour contrôler la taille de la vue enfant. Définissez ensuite d'abord cet attribut dans value/attr.xml (utilisez CustomLinearLayout_Layout pour le distinguer de CustomLinearLayout, bien sûr, le nom est arbitraire).
<declare-styleable name="CustomLinearLayout_Layout"> <!-- 定义比例 --> <attr name="inner_percent" format="float"/> </declare-styleable>
Cette méthode sera éventuellement appelée lorsque ViewGroup appelle addView().
public void addView(View child, int index, LayoutParams params)
Ce paramètre représente la configuration de ViewGroup.LayoutParams contient la largeur et la hauteur, LinearLayout.LayoutParams ajoute des attributs de poids, etc. Ensuite, nous devrions implémenter un LayoutParams. Voilà donc tout pour l'instant.
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(); } } }
Vous pouvez désormais utiliser nos attributs en 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>
C'est juste doux et n'a aucun effet.
Alors, comment contrôlez-vous la taille de Child View ? Bien sûr, cela est contrôlé dans onMeasure. addView exécutera le code suivant.
requestLayout(); invalidate(true);
Dans ce cas, onMeasure() et onLayout() seront à nouveau parcourus. Après avoir implémenté la méthode onMeasure(), je gérerai directement la taille de la vue enfant. Parce que j'hérite de LinearLayout, MeasureChildBeforeLayout() sera réellement traité. La dernière étape consiste à gérer la taille de la vue enfant lors de la mesure de ChildBeforeLayout.
@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); } }
De cette manière, les exigences initiales peuvent être atteintes.
En fait, il y a encore quelques détails qui doivent être traités, le code suivant est.
/** * 当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); }
Pour résumer
Ce à quoi vous devez prêter plus d'attention lors de la personnalisation de View et ViewGroup sont onMeasure, onLayout et onDraw.
Séparez les propriétés du ViewGroup lui-même et les propriétés de la Child View.
Vous pouvez vous référer au code dans le package de support, et le débogage est également très pratique.
Lors du développement Android, il existe en effet de nombreux endroits où vous devez personnaliser les vues, mais la plupart d'entre elles ont des bibliothèques open source correspondantes. Mais nous devons encore savoir comment personnaliser un ViewGroup.
Pour plus d'articles liés au résumé de l'utilisation de ViewGroup personnalisé dans Android, veuillez faire attention au site Web PHP chinois !