分類
カスタム レイアウトは 2 つの状況に分類できます。
ViewGroup をカスタマイズし、LinearLayout、RelativeLayout などとは異なるいくつかの ViewGroup を作成します。例: API 14 以降に追加された GridLayout、デザイン サポート ライブラリの CoordinatorLayout など。
既存のレイアウトをカスタマイズし、特別な機能を追加します。例: パーセントサポートライブラリの TableLayout と PercentFrameLayout など。
プロセス
カスタムビューのプロセスは、onMeasure()->onLayout()->onDraw()です。 ViewGroup をカスタマイズする場合、通常は onDraw を実装する必要はありません。もちろん、CoordinatorLayout などの特別な要件が必要になる場合があります。
つまり、onMeasure と onLayout は基本的に、私たちが接触するほとんどの ViewGroups を実行できます。ただし、onMeasure で ViewGroup のサイズを測定し、onLayout で子ビューの位置を計算する方法を知るだけでは十分ではありません。
例: ViewGroup の子ビューのプロパティを設定するにはどうすればよいですか?
一例です。
カスタム ViewGroup を作成し、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); } }
その他のことはさておき、子ビューのサイズを制御するために属性を追加する必要があります。次に、まず 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 には幅と高さが含まれ、LinearLayout.LayoutParams には重み属性などが追加されます。次に、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()メソッドを実装した後は、LinearLayoutを継承しているので、実際にはmeasureChildBeforeLayout()が処理されます。最後のステップは、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); } }
このようにして、最初のニーズを達成できます。
実際には、処理する必要がある詳細がまだいくつかあります。次のコードです。
/** * 当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 自体のプロパティと子ビューのプロパティを区別します。
サポートパッケージ内のコードを参照でき、デバッグも非常に便利です。
Android 開発を行う場合、ビューをカスタマイズする必要がある場所は確かにたくさんありますが、そのほとんどには対応するオープンソース ライブラリがあります。ただし、ViewGroup をカスタマイズする方法を理解する必要があります。
Android でのカスタム ViewGroup の使用の概要に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。