今回は ViewGroup を直接継承し実装してみました。
子要素の配置を行うためいに ViewGroup#onLayout をオーバーライドします。
ここで View#getWidth の戻り値は 0 となってしまうため、 View#getMeasuredWidthを使用します。
ですが、素の状態では View#getMeasuredWidth の戻り値も 0 となり、コントロールのサイズを取得できません。
そこでまず ViewGroup#onMeasure をオーバーライドし子要素に対してView#measureを呼び出します。
子要素の配置を行うためいに ViewGroup#onLayout をオーバーライドします。
ここで View#getWidth の戻り値は 0 となってしまうため、 View#getMeasuredWidthを使用します。
ですが、素の状態では View#getMeasuredWidth の戻り値も 0 となり、コントロールのサイズを取得できません。
そこでまず ViewGroup#onMeasure をオーバーライドし子要素に対してView#measureを呼び出します。
以下の呪文を記述
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = View.resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = View.resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); this.setMeasuredDimension(width, height); int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); int childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { getChildAt(i).measure(childWidthSpec, childHeightSpec); } }
これを書けば、View#getMeasuredWidthで値が取得できるようになります。
ちゃんと理解してはいない。
ちゃんと理解してはいない。
あとは前回 View#onWindowFocusChangedのなかに書いてた事をView#onLayoutに記述してやればOK。
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = this.getChildCount(); // 行の高さ int rowHeight = 0; // 現在の行の幅 int currentRowWidth = 0; int rowMaxWidth = this.getWidth(); int top = 0; for (int i = 0; i < count; i++) { View child = this.getChildAt(i); if (child.getVisibility() != View.GONE) { int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); if (rowMaxWidth < currentRowWidth + childWidth) { // はみ出すので演算をやり直す。 // 次行の位置を設定 top += rowHeight < childHeight ? childHeight : rowHeight; // 現在行の高さを初期化 rowHeight = 0; // 現在行の幅を初期化 currentRowWidth = 0; } // コントロールを適切な位置に配置 child.layout( currentRowWidth, top, currentRowWidth + childWidth, top + childHeight); // 行の高さを更新する必要があるか rowHeight = rowHeight < childHeight ? childHeight : rowHeight; // 現在のコントロール幅を合計値に加算する currentRowWidth += childWidth; } } }
良い感じ良い感じ。
実際にはMarginなんかが設定されるハズ。
なの以下の クラス をInner Classとして作成。
なの以下の クラス をInner Classとして作成。
public static class LayoutParams extends MarginLayoutParams { public LayoutParams(Context context, AttributeSet attr) { super(context, attr); } public LayoutParams(int width, int height) { super(width, height); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } public LayoutParams(MarginLayoutParams source) { super(source); } }
MarginLayoutParams を継承しただけ。今後を考えてInner Classとした。
このクラスを定義しただけで、(WrapLayout.LayoutParams)child.getLayoutParams() なんてした日には楽しい結末(ClassCastException)が待っているので、 以下の Method を Override
このクラスを定義しただけで、(WrapLayout.LayoutParams)child.getLayoutParams() なんてした日には楽しい結末(ClassCastException)が待っているので、 以下の Method を Override
・ ViewGroup#generateLayoutParams
・ ViewGroup#generateDefaultLayoutParams
・ ViewGroup#checkLayoutParams
・ ViewGroup#generateLayoutParams
・ ViewGroup#generateDefaultLayoutParams
・ ViewGroup#checkLayoutParams
・ ViewGroup#generateLayoutParams
@Override public WrapLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { return new WrapLayout.LayoutParams(getContext(), attrs); } @Override protected WrapLayout.LayoutParams generateDefaultLayoutParams() { return new WrapLayout.LayoutParams(WrapLayout.LayoutParams.WRAP_CONTENT, WrapLayout.LayoutParams.WRAP_CONTENT); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof WrapLayout.LayoutParams; } @Override protected WrapLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new WrapLayout.LayoutParams(p); }
設定された margin を使って配置を行うように ViewGroup#onLayout に修正を加える。
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = this.getChildCount(); // 行の高さ int rowHeight = 0; // 現在の行の幅 int currentRowWidth = 0; int rowMaxWidth = this.getWidth(); int top = 0; for (int i = 0; i < count; i++) { View child = this.getChildAt(i); if (child.getVisibility() != View.GONE) { WrapLayout.LayoutParams lp = (WrapLayout.LayoutParams)child.getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); int childTotaWidth = childWidth + lp.rightMargin + lp.leftMargin; int childTotalHeight = childHeight + lp.topMargin + lp.bottomMargin; if (rowMaxWidth < currentRowWidth + childTotaWidth) { // はみ出すので演算をやり直す。 // 高さを設定 top += rowHeight < childTotalHeight ? childTotalHeight : rowHeight; // 現在行の高さを初期化 rowHeight = 0; // 現在行の幅を初期化 currentRowWidth = 0; } child.layout( currentRowWidth + lp.leftMargin, top + lp.topMargin, currentRowWidth + childWidth + lp.rightMargin, top + childHeight + lp.bottomMargin); rowHeight = rowHeight < childTotalHeight ? childTotalHeight : rowHeight; currentRowWidth += childTotaWidth; } } }
これでMarginが有効になったWrapLayoutの出来上がり。
ソースコード全文(import等は割愛)
ソースコード全文(import等は割愛)
public class WrapLayout extends ViewGroup { //================================================================== // constructor //================================================================== public WrapLayout(Context context) { super(context); } public WrapLayout(Context context, AttributeSet attrs) { super(context, attrs); } public WrapLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } //================================================================== // public methods //================================================================== @Override public WrapLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { return new WrapLayout.LayoutParams(getContext(), attrs); } //================================================================== // protected methods //================================================================== @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = View.resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = View.resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); this.setMeasuredDimension(width, height); int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); int childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { getChildAt(i).measure(childWidthSpec, childHeightSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = this.getChildCount(); // 行の高さ int rowHeight = 0; // 現在の行の幅 int currentRowWidth = 0; int rowMaxWidth = this.getWidth(); int top = 0; for (int i = 0; i < count; i++) { View child = this.getChildAt(i); if (child.getVisibility() != View.GONE) { WrapLayout.LayoutParams lp = (WrapLayout.LayoutParams)child.getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); int childTotaWidth = childWidth + lp.rightMargin + lp.leftMargin; int childTotalHeight = childHeight + lp.topMargin + lp.bottomMargin; if (rowMaxWidth < currentRowWidth + childTotaWidth) { // はみ出すので演算をやり直す。 // 高さを設定 top += rowHeight < childTotalHeight ? childTotalHeight : rowHeight; // 現在行の高さを初期化 rowHeight = 0; // 現在行の幅を初期化 currentRowWidth = 0; } child.layout( currentRowWidth + lp.leftMargin, top + lp.topMargin, currentRowWidth + childWidth + lp.rightMargin, top + childHeight + lp.bottomMargin); rowHeight = rowHeight < childTotalHeight ? childTotalHeight : rowHeight; currentRowWidth += childTotaWidth; } } } @Override protected WrapLayout.LayoutParams generateDefaultLayoutParams() { return new WrapLayout.LayoutParams(WrapLayout.LayoutParams.WRAP_CONTENT, WrapLayout.LayoutParams.WRAP_CONTENT); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof WrapLayout.LayoutParams; } @Override protected WrapLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new WrapLayout.LayoutParams(p); } //================================================================== // inner class //================================================================== public static class LayoutParams extends MarginLayoutParams { public LayoutParams(Context context, AttributeSet attr) { super(context, attr); } public LayoutParams(int width, int height) { super(width, height); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } public LayoutParams(MarginLayoutParams source) { super(source); } } }
0 件のコメント:
コメントを投稿