https://developer.android.com/reference/android/view/ViewGroup
ViewGroup์ (์๋ ๋ผ๊ณ ๋ถ๋ฅด๋) ๋ค๋ฅธ ๋ทฐ๋ค์ ํฌํจํ ์ ์๋ ํน๋ณํ ๋ทฐ์ ๋๋ค. ์ด ๋ทฐ ๊ทธ๋ฃน์ ๋ ์ด์์๊ณผ ๋ทฐ ์ปจํ ์ด๋๋ค์ ๊ธฐ๋ณธ ํด๋์ค์ ๋๋ค. ์ด ํด๋์ค๋ ๋ํ ๋ ์ด์์ ๋งค๊ฐ๋ณ์๋ค์ ์ํ ๊ธฐ๋ณธ ํด๋์ค์ธ ViewGroup.LayoutParams ํด๋์ค๋ฅผ ์ ์ํฉ๋๋ค.
๋ ์ด์์ ์์ฑ๊ณผ ๊ด๋ จํด LayoutParams๋ ํ์ธํ์ธ์.
์ฌ๊ธฐ ์ผ์ชฝ๊ณผ ์ค๋ฅธ์ชฝ ์ฌ๋ฐฑ ์์ ์๋ ๋ค์ ์์(stack) ์ ์๋ ๊ฐ๋จํ FrameLayout์ ๊ตฌํํ ์ปค์คํ ViewGroup์ ์์ฑ๋ ๊ตฌํ์ด ์์ต๋๋ค.
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews;
/**
* ์ปค์คํ
๋ ์ด์์ ๋ฉ๋์ ๋ฅผ ์์ฑํ ์์
๋๋ค. ๊ฝค ์์ ํ ๊ธฐ๋ฅ์ ๋ ์ด์์ ๋งค๋์ ๋ก
* ๋น๊ต์ ์ข
ํฉ์ ์ด๊ณ , ๋ค๋ฅธ ๋ชจ๋ ๋ ์ด์์๋ค์ ๊ฒฝ์ฐ๋ ์ฒ๋ฆฌํฉ๋๋ค.
* ๋ ํน์ ํ ๊ฒฝ์ฐ์ ๋จ์ํ์ํฌ ์ ์์ต๋๋ค.
*/
@RemoteViews.RemoteView
public class CustomLayout extends ViewGroup {
/** ์ผ์ชฝ ์ฌ๋ฐฑ์ ์๋
๋ค์ด ์ฌ์ฉํ ๊ณต๊ฐ์ ์์น
private int mLeftWidth;
/** ์ค๋ฅธ์ชฝ ์ฌ๋ฐฑ์ ์๋
๋ค์ด ์ฌ์ฉํ ๊ณต๊ฐ์ ์์น
private int mRightWidth;
/** ์๋
๋ค์ gravity์ ๋ฐ๋ผ ์๋
ํ๋ ์์ ๊ณ์ฐํ๋๋ฐ ์ฐ์
๋๋ค.
private fianl Rect mTmpContainerRect = new Rect();
private final Rect mTmpChildRect = new Rect();
public CustomLayout(Context context) {
super(context);
}
public CustomLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* ์คํฌ๋กคํ์ง ์๋ ๋ ์ด์์ ๋งค๋์ ๋ ์ด๊ฒ์ด ํ์ํฉ๋๋ค.
*/
@Override
publci boolean shouldDelayChildPressedState() {
return false;
}
/**
* ๋ชจ๋ ์๋
๋ค์ด ์ค์ค๋ก ์ธก์ ํ๊ณ
* ์๋
๋ค์ด ๊ธฐ๋ฐ์ผ๋ก ํ๋ ๋ ์ด์์์ ์ธก์ ์ ๊ณ์ฐํ๋๋ก ์์ฒญํฉ๋๋ค.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int HeightMeasureSpec) {
int count = getChildCount();
// ๋ฐฐ์น๋ ๊ณณ์ ๋ทฐ์ ์ผ์ชฝ๊ณผ ์ค๋ฅธ์ชฝ์์ ์ฌ์ฉํ๋ ๊ณต๊ฐ์ ์ถ์ ํฉ๋๋ค.
// ๋ฉค๋ฒ ๋ณ์๋ค์ด ํ์ํ๊ณ , ๋์ค์ ๋ ์ด์์์ ์ํด ์ธ ์ ์์ต๋๋ค.
mLeftWidth = 0;
mRightWidth = 0;
// ์ธก์ ์ ์ด ๊ฐ๋ค์ ๊ฒฐ๊ตญ์๋ ๊ณ์ฐํ ๊ฒ๋๋ค.
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
// ์๋
๋ค์ ์ธก์ ํ๊ณ , ์๋
์ ํฌ๊ธฐ๋ก๋ถํฐ dimension๋ค์ ๊ณ์ฐํ๋ ๊ฒ์
// ๋ชจ๋ ์๋
๋ค์๊ฒ ๋ฐ๋ณตํฉ๋๋ค.
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
// ์๋
๋ฅผ ์ธก์ ํฉ๋๋ค.
measureChildWithMargins(child, widthMeasureSpect, 0, heightMeasureSpct, 0);
// layout params์ ๊ธฐ๋ฐ์ผ๋ก ํฌ๊ธฐ ์ ๋ณด๋ฅผ ์
๋ฐ์ดํธ ํฉ๋๋ค.
// ์ผ์ชฝ์ด๋ ์ค๋ฅธ์ชฝ์ ๋ฐฐ์นํ๋๋ก ์์ฒญ๋ ์๋
๋ค์ ์ด ์ฌ๋ฐฑ์ผ๋ก ๊ฐ๋๋ค.
final LaoutParmas lp = (LayoutParams) child.getLayoutParmas();
if (lp.position == LayoutParams.POSITION_LEFT) {
mLeftWidth += Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
} else if (pl.position == LayoutParams.POSITION_RIGHT) {
mRightWidth += Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
} else {
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + pl.rightMargin);
}
maxHeight = Max.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}
// ์ด ๋๋น๋ ๋ชจ๋ ์์ชฝ ์๋
๋ค์ ์ฌ๋ฐฑ๋ค์ ๋ํ ๊ฒ์ ์ต๋๊ฐ์
๋๋ค.
maxWidth += mLeftWidth + mRightWidth;
// ์ต์ ๋์ด์ ๋๋น๋ฅผ ๋น๊ตํฉ๋๋ค.
maxHeight = Max.max(maxHeight, getSuggetedMininumHeight());
maxWidth = Max.max(maxWidth, getSuggestedMininumWidth());
// ์ต์ข
์์น๋ฅผ ๊ธฐ๋กํฉ๋๋ค.
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasrueSpect, childState),
resolveSizeAndState(maxHeight, heightMeasureSpect,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
/**
* ์ด ๋ ์ด์์ ์์ ๋ชจ๋ ์๋
๋ค์ ๋ฐฐ์นํฉ๋๋ค.
*/
@Override
protectd void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
// ๋ฐฐ์น ์์
์ ํ๋๋ฐ ๋จผ ์ผ์ชฝ๊ณผ ์ค๋ฅธ์ชฝ์ ๊ฐ์ฅ์๋ฆฌ์
๋๋ค.
int leftPos = getPaddingLeft();
int rightPos = right - left - getPaddingRight();
// ๊ฐ์ฅ์๋ฆฌ์ ์ค๊ฐ ์ง์ญ ์์ชฝ์
๋๋ค.
final int middleLeft = leftPos + mLeftWidth;
final int middleRight = rightPos - mRightWidth;
// ๋ฐฐ์น ์์
์ ํ๋๋ฐ ์ต์๋จ๊ณผ ์ตํ๋จ ๊ฐ์ฅ์๋ฆฌ์
๋๋ค.
final int parentTop = getPaddingTop();
final int parentBottom = bottom - top - getPaddingBottom();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
foma; :aupitParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
// ์๋
๋ฅผ ๋ฐฐ์นํ๋ ํ๋ ์์ ๊ณ์ฐํฉ๋๋ค.
if (lp.position == LayoutParams.POSITION_LEFT) {
mTmpContainerRect.left = leftPos + lp.leftMargin;
mTmpContainerRect.right = leftPos + width + lp.rightMargin;
leftPos = mTmpContainerRect.right;
} else if (lp.position == LaoutParams.POSITION_RIGHT) {
mTmpContainerRect.right = rightPos - lp.rightMargin;
mTmpContanerRect.left = rightPos - width - lp.leftMargin;
rightPos = mTmpContainerRect.left;
} else {
mTmpContainerRect.left = middleLeft + lp.leftMargin;
mTmpContainerRect.right = middleRight + lp.rightMargin;
}
mTmpContainerRect.top = parentTop + lp.topMargin;
mTmpContainerRect.bottom = parentBottom - lp.bottomMargin;
// ์๋
์ gravity์ ํฌ๊ธฐ๋ฅผ ์ฌ์ฉํด ์ปจํ
์ด๋ ์์ ์ต์ข
ํ๋ ์์
// ๊ฒฐ์ ํฉ๋๋ค.
Gravity.apply(lp.gravity, width, height, mTmpContainerRect, mTmpChildRect);
// ์๋
๋ฅผ ๋ฐฐ์นํฉ๋๋ค.
child.layout(mTmpChildRect.left, mTmpChildRect.top,
mTmpChildRect.right, mTmpChildREct.bottom);
}
}
}
// -----------------------------------------------------------------
// ๊ตฌํ์ ๋๋จธ์ง๋ ์์ ๋ณ ์ปค์คํ
๋ ์ด์์ ๋งค๊ฐ๋ณ์๋ค์ ์ํ ๊ฒ์
๋๋ค.
// ์ด๊ฒ๋ค์ด ํ์์๋ค๋ฉด (์๋ฅผ ๋ค์ด ์์๋ค์ ์์น๋ฅผ ๊ณ ์ ์ํค๋ ๋ ์ด์์ ๋งค๋์ ๋ฅผ
// ์์ฑํ๋ ๊ฒฝ์ฐ) ๋ค ๋์ด๊ฐ๋ ๋ฉ๋๋ค.
@Override
public LayoutParmas generateLayotuParams(AttributeSet attrs) {
return new CustomLayout.LayoutParmas(getContext(), attrs);
}
@Override
protected LayoutParams generatelaoutParams(AttributeSet attrs) {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT0;
}
@Override
protected LayoutParams generateDefaultLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParam(p)
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams
}
/**
* ์ปค์คํ
์๋
๋ณ ๋ ์ด์์ ์ ๋ณด
*/
public static class LayoutParams extends MarginLayoutParmas {
/**
* gravity๋ ์ด ๋ ์ด์์ ๋งค๊ฐ๋ณ์๋ค๊ณผ ๊ด๋ จ๋ ๋ทฐ์ ์ ์ฉ๋๋ค.
*/
public int gravity = Gravity.TOP | Gravity.START;
public static int POSITION_MIDDLE = 0;
public static int POSITION_LEFT = 1;
public static int POSITION_RIGHT = 2;
public int position = POSITION_MIDDLE;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
// ์ธํ๋ ์ด์
๋์ layout XML์์ ๋ ์ด์์ ๋งค๊ฐ๋ณ์ ๊ฐ๋ค์ ๊ฐ์ ธ์ต๋๋ค.
// XML์์ ๋ ์ด์์ ํ์๊ฐ ๋ฐ๋๋ ๊ฒ์ ์ ๊ฒฝ์ฐ์ง ์๋๋ค๋ฉด ํ์ํ์ง ์์ต๋๋ค.
TypedArray e = c.obtainStyledAttributes(attrs, R.styleable.CustomLaoutLP);
gravity = a.getInt(R.styleable.CustomLayoutLP_android_layout_gravity, gravity);
position = a.getInt(R.styleable.CustomLaoutLP_layout_position, position);
a.recycle();
}
public LayoutParams(int width, int height) {
super(width, height)
}
public LayoutParmas(ViewGroup.LayoutParmas source) {
super(source);
}
}
}
๋ง์ฝ ์์์ฒ๋ผ XML ๋ ์ด์์ ์์ฑ๋ค์ ๊ตฌํํ๋ ์ค์ด๋ผ๋ฉด, ์ด๊ฒ์ res/values/attrs.xml์ ๋ค์ด๊ฐ ํด๋นํ๋ ์ ์์ ๋๋ค.
<declare-styleable name="CustomLayoutLP">
<attr name="android:layout_gravity" />
<attr name="layout_position">
<enum name="middle" value="0" />
<enum name="left" value="1" />
<enum name="right" value="2" />
</attr>
</declare-styleable>
์ต์ข ์ผ๋ก ์ด ๋ ์ด์์ ๋งค๋์ (๊ตฌํํ ๋ทฐ๊ทธ๋ฃน)๋ XML ๋ ์ด์์์์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉ๋ ์ ์์ต๋๋ค.
<com.example.android.apis.view.CustomLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- ์ฒซ ๋ฒ์งธ ๋ทฐ๋ฅผ ์ผ์ชฝ์ ๋์ต๋๋ค. -->
<TextView
android:background="@drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="left"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="l1"/>
<!-- ๋ ๋ฒ์งธ ๋ทฐ๋ฅผ ์ผ์ชฝ์ ์์ต๋๋ค. -->
<TextView
android:background="@drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="left"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="l2"/>
<!-- ๋ ๋ทฐ๋ฅผ ์ค๋ฅธ์ชฝ์ ๋์ต๋๋ค. -->
<TextView
android:background="@drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="right"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="r1"/>
<!-- ์๋์ผ๋ก ๋ทฐ๋ค์ ๊ฐ์ด๋ฐ๋ก ๊ฐ๋๋ค. ; fill vertical gravity๋ฅผ ์ฌ์ฉํฉ๋๋ค. -->
<TextView
android:background="@drawable/green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="fill-vert"/>
<!-- ์๋์ผ๋ก ๋ทฐ๋ค์ ๊ฐ์ด๋ฐ๋ก ๊ฐ๋๋ค.; fill horizontal gravity๋ฅผ ์ฌ์ฉํฉ๋๋ค. -->
<TextView
android:background="@drawable/green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|fill_horizontal"
android:text="fill-horiz"/>
<!-- ์๋์ผ๋ก ๋ทฐ๋ค์ ๊ฐ์ด๋ฐ๋ก ๊ฐ๋๋ค.; top-left gravity๋ฅผ ์ฌ์ฉํฉ๋๋ค. -->
<TextView
android:background="@drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|left"
android:text="top-left"/>
<!-- ์๋์ผ๋ก ๋ทฐ๋ค์ ๊ฐ์ด๋ฐ๋ก ๊ฐ๋๋ค.; center gravity๋ฅผ ์ฌ์ฉํฉ๋๋ค. -->
<TextView
android:background="@drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="center"/>
<!-- ์๋์ผ๋ก ๋ทฐ๋ค์ ๊ฐ์ด๋ฐ๋ก ๊ฐ๋๋ค.; bottom-right gravity๋ฅผ ์ฌ์ฉํฉ๋๋ค. -->
<TextView
android:background="@drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:text="bottom-right"/>
</com.example.android.apis.view.CustomLayout>