blog

ビューレイアウト

LayoutParamsとして翻訳すると、子Viewは、親コンテナの対応するものを使用して、それ自身の配置方法を親コンテナに伝えます。そのため、基本的には、各ViewGroupは、レイアウトルールをサ...

Jun 22, 2020 · 5 min. read
シェア

はじめに

LayoutParams

LayoutParams はレイアウトパラメータに変換され、子ビューは親コンテナの対応する LayoutParams を使用して、親コンテナに自身の配置方法を伝えます。基本的に、全てのViewGroupは対応するLayoutParamsを持ち、レイアウトルールをサポートします。

LayoutParamsViewとの接続方法

  • XMLでViewを定義します。このシステムはデフォルトでViewGroupのLayoutParamsをViewに追加します。
  • 対応するViewのインスタンス・オブジェクトをJavaコードで直接生成します。

ViewGroup をカスタマイズする場合、以下の点に注意する必要があります。

/**
* をオーバーライドする。
*/
public void addView(View child) {
 addView(child, -1);
}
/**
* コードを通してサブビューを追加し、場所を指定する。
*/
public void addView(View child, int index) {
 if (child == null) {
 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
 }
 LayoutParams params = child.getLayoutParams();
 if (params == null) {
 // デフォルトのLayoutParamsを生成するには、以下のメソッドをオーバーライドする必要がある。
 params = generateDefaultLayoutParams();
 if (params == null) {
 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
 }
 }
 addView(child, index, params);
}
/**
* コードを通してサブビューを追加し、高さと幅を指定する。
*/
public void addView(View child, int width, int height) {
 final LayoutParams params = generateDefaultLayoutParams();
 params.width = width;
 params.height = height;
 addView(child, -1, params);
}
/**
* 注:addViewでViewを追加する場合、LayoutParamsを渡さない場合は、以下のメソッドでデフォルトのLayoutParamsを生成する必要がある。
* メソッドをオーバーライドする。
*/
protected LayoutParams generateDefaultLayoutParams() {
 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
/**
* パラメータを指定する
*/
@Override
public void addView(View child, LayoutParams params) {
 addView(child, -1, params);
}
/**
* インデックスとパラメータを指定してビューを追加する。
*/
public void addView(View child, int index, LayoutParams params) {
 if (DBG) {
 System.out.println(this + " addView");
 }
 if (child == null) {
 throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
 }
 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
 // therefore, we call requestLayout() on ourselves before, so that the child's request
 // will be blocked at our level
 requestLayout();
 invalidate(true);
 addViewInner(child, index, params, false);
}
private void addViewInner(View child, int index, LayoutParams params,
 boolean preventRequestLayout) {
 ....
 if (mTransition != null) {
 mTransition.addChild(this, child);
 }
 	//入ってくるパラメータが合法かどうかをチェックする
 if (!checkLayoutParams(params)) {
 	//が合法でない場合は変換される。
 params = generateLayoutParams(params);
 }
 	//レイアウト処理の再実行を防ぐかどうか
 if (preventRequestLayout) {
 child.mLayoutParams = params;//これは、子Viewの再レイアウトを引き起こさない(onMeasure - the> onLayout -> onDraw)
 } else {
 child.setLayoutParams(params);//これにより、サブビューが再レイアウトされる(onMeasure -)。> onLayout -> onDraw)
 }
 if (index < 0) {
 index = mChildrenCount;
 }
 addInArray(child, index);
 // tell our children
 if (preventRequestLayout) {
 child.assignParent(this);
 } else {
 child.mParent = this;
 }
 .......
}
/**
*	注:LayoutParamsが準拠しているかどうかをチェックするには、このメソッドをオーバーライドしてチェックをカスタマイズする。
*/
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
 return p != null;
}
/**
* 注意:checkLayoutParamsはこのメソッドを呼び出すのに失敗するので、このメソッドをオーバーライドして完全なパラメータを追加する。
*/
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
 	return p;
} 
/**
* 注:xml経由で追加されたSubViewは、このメソッド経由でLayoutParamsを生成する。
*/
public LayoutParams generateLayoutParams(AttributeSet attrs) {
 return new LayoutParams(getContext(), attrs);
}

カスタム LayoutParams

1.カスタムプロパティの作成
<declare-styleable name="MyGroupView">
 <attr name="layout_simple_attr" format="integer"></attr>
 <attr name="android:layout_gravity" format="dimension"></attr>
</declare-styleable>
2.MarginLayoutParamsの継承
public class LayoutParams extends ViewGroup.MarginLayoutParams {
 public int simpleAttr;
 public int gravity;
 public LayoutParams(Context c, AttributeSet attrs) {
 super(c, attrs);
 //レイアウトを解析する
 TypedArray typedArray = c.obtainStyledAttributes(attrs, R.styleable.MyGroupView);
 simpleAttr = typedArray.getInteger(R.styleable.MyGroupView_layout_simple_attr, 0);
 gravity = typedArray.getInteger(R.styleable.MyGroupView_android_layout_gravity, -1);
 typedArray.recycle();//リソースを解放する
 }
 public LayoutParams(int width, int height) {
 super(width, height);
 }
 public LayoutParams(ViewGroup.MarginLayoutParams source) {
 super(source);
 }
 public LayoutParams(ViewGroup.LayoutParams source) {
 super(source);
 }
}
3.ViewGroupのLayoutParamsに関連するいくつかのメソッドを書き換えます。
// 例としてのFrameLayout
// LayoutParamsが合法かどうかをチェックする。
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
 return p instanceof LayoutParams;
}
//デフォルトのLayoutParamsを生成する
@Override
protected LayoutParams generateDefaultLayoutParams() {
 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
//入力されるlpを変換する
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
 if (sPreserveMarginParamsInLayoutParamConversion) {
 if (lp instanceof LayoutParams) {
 return new LayoutParams((LayoutParams) lp);
 } else if (lp instanceof MarginLayoutParams) {
 return new LayoutParams((MarginLayoutParams) lp);
 }
 }
 return new LayoutParams(lp);
}
// XMLによってロードされたViewは、このメソッドを通してLayoutParamsを構築する。
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
 return new FrameLayout.LayoutParams(getContext(), attrs);
}

Layout

public void layout(int l, int t, int r, int b) {
 // 最初にonMesuareを実行する必要があるかどうかを判断する。これは通常measureメソッドで設定される。
 if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
 onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
 }
 int oldL = mLeft;
 int oldT = mTop;
 int oldB = mBottom;
 int oldR = mRight;
 // 通常、layoutメソッドを呼び出す必要があるのは、ビューの位置を移動させる場合だけだ。プロパティ・アニメーションの変位アニメーションを実装する場合、layoutメソッドを直接呼び出すと、小節の計算量が減り、setFrame/setOpticalFrameが無効化処理のトリガーになる可能性がある。
 boolean changed = isLayoutModeOptical(mParent) ?
 setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
 if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
 onLayout(changed, l, t, r, b);
 .....
 }
 ....
}
Read next

ゲーム開発におけるFlutterの性能とクロスプラットフォーム開発の利点。

しかし、小規模なゲーム開発では、この2つを学ぶコストは明らかに大きすぎます。 もっと勉強したら、flutterにもFlameとSpriteWというゲームエンジンがあることがわかりました。

Jun 22, 2020 · 2 min read