안드로이드 커스텀 뷰를 만드는 방법에 대해 알아보자. 위와 같이 3개의 별을 만들고 "눌러서 갱신" 버튼을 누르면 다음 별이 선택되 노란색 별로 바뀌는 것을 구현해 볼 것이다. 


1. 커스텀 뷰의 레이아웃을 결정한다.

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" >
<ImageView
android:id="@+id/star1"
android:src="@drawable/star"
android:layout_margin="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/star2"
android:src="@drawable/star_empty"
android:layout_margin="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/star3"
android:src="@drawable/star_empty"
android:layout_margin="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</merge>

커스텀 뷰의 레이아웃은 위와 같다. @drawable/star 은 노란색으로 칠해진 별 이미지이고 @drawable/star_empty 는 테두리만 있는 별 이미지다. 위 코드에서 루트 태그가 LinearLayout이 아니라 merge태그를 사용했다. 커스텀 뷰가 LinearLayout을 상속할 예정이므로 LinearLayout의 중첩을 피하기 위해서 merge 태그를 사용했다. 


2. 레이아웃 XML로 설정할 수 있는 항목을 attrs.xml에 작성한다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
<attr name="selected" format="integer" />
</declare-styleable>
</resources>

커스텀 뷰의 xml로 설정을 변경할 수 있게 attrs.xml에 위와 같이 작성했다. xml 또는 AttributeSet 클래스로 interger 타입의 selected 변수를 접근할 수 있다. 이에 대해서 차차 알아볼 예정이다.


3. 커스텀 뷰의 클래스를 작성한다. 

public class MyCustomView extends LinearLayout {
private ImageView mStar1;
private ImageView mStar2;
private ImageView mStar3;
private int mSelected = 0;

public MyCustomView(Context context) {
super(context);
initializeViews(context, null);
}


public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
initializeViews(context, attrs);
}

public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeViews(context, attrs);
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initializeViews(context, attrs);
}

/**
* 레이아웃 초기화
*
* @param context
*/
private void initializeViews(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.three_stars_indicator, this);
if (attrs != null) {
//attrs.xml에 정의한 스타일을 가져온다
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView);
mSelected = a.getInteger(0, 0);
a.recycle(); // 이용이 끝났으면 recycle() 호출
}
}

@Override // inflate가 완료되는 시점에 호출된다.
protected void onFinishInflate() {
super.onFinishInflate();
mStar1 = (ImageView) findViewById(R.id.star1);
mStar2 = (ImageView) findViewById(R.id.star2);
mStar3 = (ImageView) findViewById(R.id.star3);
// 처음에만 xml로부터의 지정을 반영시키고자 두 번째 인수인 force true로 한다
setSelected(mSelected, true);
}

/**
* 지정된 번호로 선택한다
*
* @param select 지정할 번호(0이 가장 왼쪽)
*/
public void setSelected(int select) {
setSelected(select, false);
}

private void setSelected(int select, boolean force) {
if (force || mSelected != select) {
if (2 > mSelected && mSelected < 0) {
return;
}
mSelected = select;
if (mSelected == 0) {
mStar1.setImageResource(R.drawable.star);
mStar2.setImageResource(R.drawable.star_empty);
mStar3.setImageResource(R.drawable.star_empty);
} else if (mSelected == 1) {
mStar1.setImageResource(R.drawable.star_empty);
mStar2.setImageResource(R.drawable.star);
mStar3.setImageResource(R.drawable.star_empty);
} else if (mSelected == 2) {
mStar1.setImageResource(R.drawable.star_empty);
mStar2.setImageResource(R.drawable.star_empty);
mStar3.setImageResource(R.drawable.star);
}
}
}

public int getSelected() {
return mSelected;
}
}


4. 메인 앱의 레이아웃에 삽입한다. 

<com.advanced_android.compositecustomviewsample.MyCustomView
android:id="@+id/indicator"
app:selected="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

app 이름 공간에 접근하기 위해서는 xmlns:app="http://schemas.android.com/apk/res-auto"를 넣어야 된다. 그러면 이 이름공간을 통해 자동으로 attrs.xml에서 정의한 내용에 접근할 수 있다.


5. 메인 액티비티에서 커스텀 뷰를 컨트롤 한다.

final MyCustomView indicator = (MyCustomView) findViewById(R.id.indicator);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int selected = indicator.getSelected();
if (selected == 2) {
selected = 0;
} else {
selected++;
}
Log.d("MainActivity", "selected=" + selected);
indicator.setSelected(selected);
}
});


+ Recent posts