티스토리 뷰
기존 뷰를 조합한 커스텀 뷰 만들기는 4단계로 진행
1. 커스텀 뷰의 레이아웃을 결정한다.
2. 레이아웃 XML로 설정할 수 있는 항목을 attrs.xml에 기재한다
3. 커스텀 뷰 클래스를 만든다
4. 메인 앱의 레이아웃에 삽입해서 확인한다.
* 버튼을 누를때 마다 변경된다
1 - 커스텀 뷰의 레이아웃을 결정한다
위에 이미지를 보면 이미지뷰를 옆으로 나열할 것이므로 LinearLayout으로
에워 쌉니다.
layout/four_starts_indicator.xml
<?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">
<ImageView
android:id="@+id/iv_start1"
android:src="@drawable/star"
android:layout_margin="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/iv_start2"
android:src="@drawable/star_empty"
android:layout_margin="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/iv_start3"
android:src="@drawable/star_empty"
android:layout_margin="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/iv_start4"
android:src="@drawable/star_empty"
android:layout_margin="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</merge>
여기서 주의할 점은 맨 처음 루트 태그가 LinearLayout이 아니라 merge 태그라는
점입니다. 이유는 커스텀 뷰가 LinearLayut을 상속한 클래스이므로 LinearLayout의
불필요한 중첩을 피하기 위해서 입니다.
2. 레이아웃 XML로 설정을 변경할 수 있게 준비합니다.
XML로 몇 번째 별이 선택됐는지 설정할 수 있게 selected 속성을 추가 합니다
format은 형 정보로, 수치인 integer가 됩니다.
단, 커스텀 뷰가 하나가 아닌 몇가지가 있을경우 아마 공통적인 속성이 생기기
마련인데 그때 마다 각각 선언하면 오류가 발생되니깐 공통적인 속성을 위로
빼서 선언하고 호출해서 사용 하면 됩니다.
values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="selected" format="integer" />
<declare-styleable name="MyCustomView">
<attr name="selected"/>
</declare-styleable>
<declare-styleable name="MyCustomView2">
<attr name="selected"/>
</declare-styleable>
</resources>
상단에 속성 <attr name="selected" format="integer" /> 속성은
MyCustomView , MyCustomView2에서 둘다 사용하기 때문에 상단에
선언 이후 가져와서 쓰게 처리 합니다
3 - 커스텀 뷰 클래스를 만든다
커스텀 뷰를 만들 때는 View를 상속할 필요가 있습니다.
1번에서 설명한것 처럼 LinearLayout을 상속합니다.
여기서 염두에 둘 것은 3가지 입니다.
1. 레이아웃 XML 2.스타일 반경 3. 외부 클래스로서, 액티비티로 조작할 후 있게 공개
메서드를 구현합니다
레이아웃을 전개하는 데는 LayoutInflater.inflate()를 이용합니다
ex) LayoutInflater.inflate(@LayoutRes int resource, @Nullable ViewGroup root)
MyCustomView2
public class MyCustomView2 extends LinearLayout {
private ImageView ivStart1, ivStart2, ivStart3, ivStart4;
private int mSelected = 0; // 선택된 번호
public MyCustomView2(Context context) {
super(context);
initializeViews(context, null);
}
public MyCustomView2(Context context, AttributeSet attrs) {
super(context, attrs);
initializeViews(context, attrs);
}
public MyCustomView2(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeViews(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyCustomView2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initializeViews(context, attrs);
}
// 객체 초기화 될때 호출 된다.
private void initializeViews(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.four_starts_indicator, this);
if (attrs != null) {
//attrs.xml에 정의한 스타일을 가져온다 즉 (attrs.xml에서 발생된 selected 속성이
// 발생되어 private void setSelected(int select, boolean force)를 호출하게 된다.
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView2);
mSelected = a.getInteger(0, 0);
a.recycle(); // 이용이 끝났으면 recycle() 호출
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
ivStart1 = (ImageView) findViewById(R.id.iv_start1);
ivStart2 = (ImageView) findViewById(R.id.iv_start2);
ivStart3 = (ImageView) findViewById(R.id.iv_start3);
ivStart4 = (ImageView) findViewById(R.id.iv_start4);
// 처음에는 XML의 지정을 반영하고자 0번째 인수인 force를 true로 한다
setSelected(mSelected, true);
}
/**
* 지정된 번호로 선택한다
*
* @param select 지정할 번호(0이 가장 왼쪽)
*/
public void setSelected(int select) {
setSelected(select, false);
}
public int getSelected() {
return mSelected;
}
private void setSelected(int select, boolean force) {
if (force || mSelected != select) {
if (3 > mSelected && mSelected < 0) {
return;
}
mSelected = select;
if (mSelected == 0) {
ivStart1.setImageResource(R.drawable.star);
ivStart2.setImageResource(R.drawable.star_empty);
ivStart3.setImageResource(R.drawable.star_empty);
ivStart4.setImageResource(R.drawable.star_empty);
} else if (mSelected == 1) {
ivStart1.setImageResource(R.drawable.star_empty);
ivStart2.setImageResource(R.drawable.star);
ivStart3.setImageResource(R.drawable.star_empty);
ivStart4.setImageResource(R.drawable.star_empty);
} else if (mSelected == 2) {
ivStart1.setImageResource(R.drawable.star_empty);
ivStart2.setImageResource(R.drawable.star_empty);
ivStart3.setImageResource(R.drawable.star);
ivStart4.setImageResource(R.drawable.star_empty);
} else if (mSelected == 3) {
ivStart1.setImageResource(R.drawable.star_empty);
ivStart2.setImageResource(R.drawable.star_empty);
ivStart3.setImageResource(R.drawable.star_empty);
ivStart4.setImageResource(R.drawable.star);
}
}
}
}
4. 메인 앱의 레이아웃에 삽입해서 확인한다
<패키지명.클래스명> 태그를 삽입한다.
여기에서는 attrs.mxl에서 정의한 몇 번째 별을 선택 하는지 설정할 수 있게 하는
selected 속성도 사용 됩니다.
app:selected가 되고 이름공간이 부여돼 있는데 attrs.xml에서 지정한 정의를 이용하기 위해서 xmlns:app="http://schemas.android.com/apk/res-auto"가 됩니다.
이 이름공간을 이용하면 자동으로 attrs.xml에서 정의한 내용을 연결할 수 있습니다.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.advanced_android.compositecustomviewsample.MainActivity">
<com.advanced_android.compositecustomviewsample.MyCustomView2
android:id="@+id/indicator2"
app:selected="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</com.advanced_android.compositecustomviewsample.MyCustomView2>
<Button
android:id="@+id/button"
android:layout_below="@+id/indicator2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="눌러서 갱신" />
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MyCustomView2 indicator2 = (MyCustomView2) findViewById(R.id.indicator2);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 현재 선택된 별을 가져온다
int selected = indicator2.getSelected();
if (selected == 3) {
selected = 0; // 맨 오른쪽에 있을때는 처음으로 돌아간다.
} else {
selected++; // 1개씩 오른쪽으로 이동한다
}
Log.d("MainActivity", "selected=" + selected);
// 다음 선택할 메소드를 실행
indicator2.setSelected(selected); // 선택 상태를 변경한다.
}
});
}
}
다음으로 버튼을 눌렸을 때 선택한 별을 바꾸는 부분을 보면 처음에 findViewById()를
호출해 커스텀 뷰의 인스턴스를 가져 옵니다. 인스턴스를 가져올 수 있으면 나머지는 커스텀 뷰에 정의된 public 메서드를 호출할 수 있습니다.
'android' 카테고리의 다른 글
6. BroadcastReceiver로 브로드캐스트 이벤트를 수신하자 (0) | 2018.05.20 |
---|---|
5. UI를 갖지 않는 프래그먼트를 이용하자 (0) | 2018.05.20 |
4.중첩 프래그먼트를 이용하자 (0) | 2018.05.09 |
3. 프래그먼트를 동적으로 추가 및 삭제하기 (0) | 2018.05.03 |
2.프래그먼트를 이해하자 (0) | 2018.05.03 |