Building a Custom Preference
안드로이드 프레임워크는 여러가지 모양으로 세팅 화면을 꾸밀 수 있도록 다양한 Preference subclass들을 제공합니다. 하지만 어떤 경우 이런 built-in solution 이 아닌 다른 모양으로 꾸며야 될 때가 있습니다. 예를 들어 number picker 나 date picker 등이 있습니다. 이런 경우 custom preference를 만들 수 있는데요. pPreference 클래스나 다른 subclass 를 extend 해서 사용하면 됩니다.
Preference class를 extend 할 때 주의해야 할 점 몇가지가 있습니다.
사용자가 세팅을 클릭했을 때 보여 질 UI를 정합니다.
적당한 때에 세팅 값들을 저장합니다.
Preference를 view에 값들이 나올 때 Initialize 해 줍니다.
시스템에 의해 request 될 때는 default 값을 줍니다.
Preference 가 자신의 UI (dialog 같은)를 제공할 때 lifecycle 변화를 컨트롤 하기 위해 state를 저장하고 restore 합니다. (예를 들어 사용자가 스크린을 rotate 할 때)
아래 섹션들에서 이른 일들을 하려면 어떻게 해야 하는지를 설명 할 겁니다.
Specifying the user interface
만약 Preference class 를 direct로 extend 한다면 사용자가 해당 아이템을 선택 했을 때 어떤 action이 일어나도록 하기 위해 onClick() 메소드를 implemet 해야 합니다. 대부분의 custom settings는 DialogPreference를 extend 합니다. dialog를 보여야 하니까요. 그러면 일의 진행을 좀 더 간단하게 처리할 수 있죠. DialogPreference를 extend 할 때 그 클래스에서 layout을 생성하는 동안 setDialogLayoutResource()를 호출해야 합니다.
예를 들어 여기 custom DialogPreference 를 위한 constructor 가 있습니다. layout을 정의하고 positive, negative 버튼을 정의 합니다.
public class NumberPickerPreference extends DialogPreference {
public NumberPickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDialogLayoutResource(R.layout.numberpicker_dialog);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
setDialogIcon(null);
}
...
}
Saving the setting's value
세팅에 대한 value들은 Preference class의 persist*() 메소드를 호출하면 어느때든지 저장할 수 있습니다. 예를 들어 값이 Integer 일 경우는 persistInt()를 Boolean 일 경우는 persistBoolean()을 호출하면 됩니다.
Note: 각각의 Preference 는 한가지의 data type으로 정의 됩니다. 그리고 거기에 해당하는 persist*() 메소드를 사용하여야 합니다.
persist를 선택하면 세팅은 extend 한 Preference class에 따라 진행 됩니다. DialogPreference 를 extend 했다면 사용자가 OK 버튼을 눌러서 이 창이 닫힐 때 해당 값을 persist 해야 합니다.
DialogPreference 가 닫힐 때, 시스템은 onDialogClosed() 메소드를 호출합니다. 이 메소드는 boolean argument를 가지고 있습니다. 사용자가 선택한 것이 OK 인지 Cancel 인지를 구분하기 위한 것이죠.
OK 를 누르면 해당 값을 persist 합니다.
@Override
protected void onDialogClosed(boolean positiveResult) {
// When the user selects "OK", persist the new value
if (positiveResult) {
persistInt(mNewValue);
}
}
이 예제에서는 mNewValue가 class member 입니다. 세팅의 현재 선택 된 값을 가지고 있는 변수죠. persistInt를 호출해서 이 값을 SharedPreference file 에 저장합니다.
(automatically using the key that's specified in the XML file for this Preference).
Initializing the current value
시스템이 화면에 여러분의 Preference를 추가 할 때 onSetInitialValue() 메소드가 호출됩니다. 이 메소드는 이 세팅이 persisted value 인지 여부를 notify 하게 됩니다. 만약 persisted value가 없으면 default value를 제공합니다.
onSetInitialValue() method는 boolean, restorePersistedValue 를 pass 합니다. 세팅에 값이 persisted 됐는지 여부를 알려주죠. true 이면 해당 Preference class의 getPersisted*() 메소드를 호출해서 persisted 된 값을 retrieve 해야 합니다. Integer value 인 경우는 getPersistedInt() 가 되겠죠. 최신의 값을 화면에 표시하려면 제 때애 UI를 update 해 줘야 합니다.
만약 restorePersistedValue가 false 이면 두번째 argument로 전달 된 default value를 사용해야 합니다.
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
// Restore existing state
mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
} else {
// Set default state from the XML attribute
mCurrentValue = (Integer) defaultValue;
persistInt(mCurrentValue);
}
}
각각의 getPersisted*() 메소드는 persist 값이 없거나 key 가 존재하지 않을 경우 사용될 default 값을 받게 됩니다. 위 예제에서 local constant는 getPersistedInt()가 persisted 값을 return 하지 못학 경우 사용될 default 값을 지정해 놓았습니다.
Caution : getPersisted*() 메소드에서 defaultValue를 default 값으로 사용할 수 없습니다. 왜냐하면 restorePersistedValue 가 true 이면 이 갑은 항상 null 일 것이기 때문입니다.
Providing a default value
당신의 Preference class 의 instance 가 default value를 가리키고 있다면 (with the android:defaultValue attribute) 시스템은 값을 가져오기 위해 해당 object를 instantiate할 때 onGetDefaultValue() 를 호출할 것입니다. 이 메스드를 반드시 implement 해서 시스템이 이 default value를 SharedPreferences 파일에 저장할 수 있도록 해야 합니다.
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInteger(index, DEFAULT_VALUE);
}
이 method argument들은 여러분이 필요로 하는 모든 것들을 제공 합니다. : the array of attributes and the index position of the android:defaultValue, 여러분이 반드시 retrieve 해야 할 것들이죠. 이 메소드를 attribute에서 디폴트 값을 추출하기 위해 반드시 implement 해야 하는 이유는 해당 값이 undefined 됐을 경우 attribute에 대해 local default 값을 specify 해야만 하기 때문입니다.
Saving and restoring the Preference's state
layout 의 View 처럼, 여러분의 Preference subclass는 액티비티나 fragment 가 restart 되는 경우 그 상태를 save 하거나 restoring 해야 하기 때문입니다. (사용자가 화면을 rotate 시켰을 경우). Preference class의 상태를 정확하게 저장하거나 restore 하기 위해서는 lifecycle callback 메소드인 onSaveInstanceState() and onRestoreInstanceState()를 호출 해야만 합니다.
Preference의 state는 Parcelable interface 를 implement 한 object 에 의해 정의 됩니다. 안드로이드 프레임워크는 여러분의 state object를 정의하기 위한 starting point로서 이런 object를 제공합니다.
어떻게 이 Preference class 가 그 상태를 저장하는지 정의하기 위해 Preference.BaseSavedState class 를 extend 해야 합니다. 그리고 몇개의 메소드들을 override 하고 CREATOR 객체를 정의합니다.
대부분의 앱들은, 아래와 같이 implementation을 사용할 수 있습니다. Integer 가 아닌 경우 아래 에제에서 몇 줄만 바꿔서 사용하시면 됩니다.
private static class SavedState extends BaseSavedState {
// Member that holds the setting's value
// Change this data type to match the type saved by your Preference
int value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
// Get the current preference's value
value = source.readInt(); // Change this to read the appropriate data type
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Write the preference's value
dest.writeInt(value); // Change this to write the appropriate data type
}
// Standard creator object using an instance of this class
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
위 Preference.BaseSavedState implementation을 여러분 앱에 추가 했다면 (usually as a subclass of your Preference subclass), 여러분은 Preference subclass에 대해 onSaveInstanceState() and onRestoreInstanceState()를 implement 합니다.
For example:
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
// Check whether this Preference is persistent (continually saved)
if (isPersistent()) {
// No need to save instance state since it's persistent,
// use superclass state
return superState;
}
// Create instance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
// Set the state's value with the class member that holds current
// setting value
myState.value = mNewValue;
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
// Check whether we saved the state in onSaveInstanceState
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save the state, so call superclass
super.onRestoreInstanceState(state);
return;
}
// Cast state to custom BaseSavedState and pass to superclass
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
// Set this Preference's widget to reflect the restored state
mNumberPicker.setValue(myState.value);
}
'WEB_APP > Android' 카테고리의 다른 글
[Android] Optional SQLite Tutorial (0) | 2016.10.23 |
---|---|
Udacity 강좌 - Lesson 4 - Activity (0) | 2016.10.23 |
[Android] Settings - 2 - (0) | 2016.10.10 |
Udacity 강좌 - Lesson 3 실습 03 - Implicit Intent - (0) | 2016.10.09 |
Udacity 강좌 - Lesson 3 실습 02 - Settings 구현하기 - (1) | 2016.10.03 |
[Android] Settings - 1 - (0) | 2016.09.26 |
Udacity 강좌 - Lesson 3 실습 01 - 다른 Activity로 화면 전환하기 - (0) | 2016.09.21 |
Android Toast 정리 (0) | 2016.09.07 |
Udacity 강좌 - Lesson 2 소스 실행 순서 따라가기 (0) | 2016.09.06 |
Udacity 강좌 - Lesson 2 실습 05 (2) | 2016.08.29 |