之前發過一篇有關於自定義preference 在ActivityGroup 的包容下出現UI不能更新的問題,當時還以為是Android 的一個BUG 現在想想真可笑 。其實是自己對機制的理解不夠深刻,看來以後要多看看原始碼才行。
本篇講述內容大致為如何自定義preference 開始到與ActivityGroup 互用下UI更新的解決方法。
首先從擴充套件preference開始:
類檔案必須繼承自Preference並實現建構函式,這裡我一般實現兩個建構函式分別如下(類名為:test):
this(context, null);
// TODO Auto-generated constructor stub
}
public test(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
這裡第二個建構函式第二個引數為可以使用attrs 為我們自定義的preference 新增擴充套件的註冊屬性,比如我們如果希望為擴充套件的preference 新增一個陣列引用,就可使用如下程式碼:
if (resouceId > 0) {
mEntries = getContext().getResources().getTextArray(resouceId);
}
這裡的mEntries 是頭部宣告的一個陣列,我們可以在xml檔案通過 Entries=陣列索引得到一個陣列。在這裡不深入為大家示範了。
我們擴充套件preference 有時想讓其UI更豐富更好看,這裡我們可以通過引用一個layout 檔案為其指定UI,可以通過實現如下兩個回撥函式:
protected View onCreateView(ViewGroup parent) {
// TODO Auto-generated method stub
return LayoutInflater.from(getContext()).inflate(
R.layout.preference_screen, parent, false);
}
此回撥函式與onBindView 一一對應,並優先執行於onBindView ,當建立完後將得到的VIEW返回出去給onBindView處理,如下程式碼:
protected void onBindView(View view) {
// TODO Auto-generated method stub
super.onBindView(view);
canlendar = Calendar.getInstance();
layout = (RelativeLayout) view.findViewById(R.id.area);
title = (TextView) view.findViewById(R.id.title);
summary = (TextView) view.findViewById(R.id.summary);
layout.setOnClickListener(this);
title.setText(getTitle());
summary.setText(getPersistedString(canlendar.get(Calendar.YEAR) + “/“
+ (canlendar.get(Calendar.MONTH) + 1) + “/“
+ canlendar.get(Calendar.DAY_OF_MONTH)));
}
Tip:onBindView 不是必須的,可以將onBindView 裡的處理程式碼在onCreateView 回撥函式一併完成然後返回給onBindView ,具體怎麼寫看自己的程式碼風格吧。我個人比較喜歡這種寫法,比較明瞭。
下面我們來了解一下我擴充套件preference 比較常用到的幾個方法:
- compareTo(Preference another)
與另外一個preference比較,如果相等則返回0,不相等則返回小於0的數字。 - getContext()
獲取上下文 - getEditor()
得到一個SharePrefence 的Editor 物件 - getIntent()
獲取Intetn - getKey()
獲取當前我們在XML為其註冊的KEY - getLayoutResource()
得到當前layout 的來源 - getOnPreferenceChangeListener()
值改變的監聽事件 - getPreferenceManager()
獲得一個preference管理 - getSharedPreferences()
通過獲得管理獲取當前的sharePreferences - getSummary()
獲得當前我們在XML為其註冊的備註為summary 的值。Tip:在OnBindView 或者onCreateView 找到VIEW的時候如果存在summary 的View 物件必須為其設定summary - getTitle()
如上,不過這裡是獲取標題 - callChangeListener(Object newValue)
如果你希望你擴充套件的Preference 可以支援當數值改變時候可以呼叫OnPreferenceChangeListener此監聽方法,則必須呼叫此方法,檢視該方法原始碼為:
protected boolean callChangeListener(Object newValue) {
return mOnChangeListener == null ? true : mOnChangeListener.onPreferenceChange(this, newValue);
}
原始碼簡單不做過多介紹,只是實現一個介面。
- getPersistedBoolean(boolean defaultReturnValue)
獲得一個儲存後的布林值,檢視一下原始碼:protected boolean getPersistedBoolean(boolean defaultReturnValue) {
if (!shouldPersist()) {
return defaultReturnValue;
}
return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
} - getPersistedFloat(float defaultReturnValue)
如上,這裡獲取一個Float 的值 - getPersistedInt(int defaultReturnValue)
如上,獲取一個int 的值 - getPersistedLong(long defaultReturnValue)
如上,獲取一個Long 型數值 - getPersistedString(String defaultReturnValue)
如上,獲取一個String 數值 - persistBoolean(boolean value)
將一個布林值儲存在sharepreference中,檢視一下原始碼:protected boolean persistBoolean(boolean value) {
if (shouldPersist()) {
if (value == getPersistedBoolean(!value)) {
// It`s already there, so the same as persisting
return true;
}
SharedPreferences.Editor editor = mPreferenceManager.getEditor();
editor.putBoolean(mKey, value);
tryCommit(editor);
return true;
}
return false;
}都是sharePreference 的知識,這裡不做過多介紹。其他的跟上面的都 一樣,略過。
通過如上的一些設定,一個基本的擴充套件preference 就己經完成,下面來講講如果在ActivityGroup 裡面讓擴充套件的preference可以更新UI。之前 農民伯伯 探討過,他建議我使用onContentChanged()方法,可以使UI更新 ,試了一下發現有些許問題,不過非常感謝農民伯伯。這個方法是全域性重新整理,則全部UI都重新整理一次,但是這樣不是很合理,我想了一下,那既然此方法可以更新UI那麼一定可以行得通,我檢視一下原始碼,下面把原始碼貼出來:
public void onContentChanged() {
super.onContentChanged();
postBindPreferences();
}
/**
* Posts a message to bind the preferences to the list view.
* <p>
* Binding late is preferred as any custom preference types created in
* {@link #onCreate(Bundle)} are able to have their views recycled.
*/
private void postBindPreferences() {
if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
}
private void bindPreferences() {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
preferenceScreen.bind(getListView());
}
}
private static final int MSG_BIND_PREFERENCES = 0;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_BIND_PREFERENCES:
bindPreferences();
break;
}
}
};
原來,這裡它是另開一條執行緒來更新UI,然後當值發生變化時為其傳送訊息,在訊息佇列裡面處理UI,只不過它這裡繼承了listActivity 更新了一整個listView ,那麼我們就將它提取出來,只更新我們想要的UI則可。OK,思路出來了,下面將我擴充套件的一個preference 的原始碼提供出來:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import com.yaomei.
set.R; public class PreferenceScreenExt extends PreferenceGroup implementsOnItemClickListener, DialogInterface.OnDismissListener {
private Dialog dialog;
private TextView title, summary;
private RelativeLayout area;
private ListView listView;
List<Preference> list;
private List<HashMap<String, String>> listStr;
private CharSequence[] mEntries;
private String mValue;
private SimpleAdapter simple;
private static final int MSG_BIND_PREFERENCES = 0;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_BIND_PREFERENCES:
setValue(mValue);
break;
}
}
};
public PreferenceScreenExt(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.preferenceScreenStyle);
// TODO Auto-generated constructor stub
}
public PreferenceScreenExt(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, android.R.attr.preferenceScreenStyle);
// TODO Auto-generated constructor stub
int resouceId = attrs.getAttributeResourceValue(null, “Entries“, 0);
if (resouceId > 0) {
mEntries = getContext().getResources().getTextArray(resouceId);
}
}
@Override
// TODO Auto-generated method stub
area = (RelativeLayout) view.findViewById(R.id.area);
title = (TextView) view.findViewById(R.id.title);
summary = (TextView) view.findViewById(R.id.summary);
title.setText(getTitle());
summary.setText(getPersistedString(getSummary().toString()));
area.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
showDialog();
}
});
}
@Override
// TODO Auto-generated method stu
View view = LayoutInflater.from(getContext()).inflate(
R.layout.preference_screen, parent, false);
return view;
}
public void bindView(ListView listview) {
int length = mEntries.length;
int i = 0;
listStr = new ArrayList<HashMap<String, String>>();
for (i = 0; i < length; i++) {
HashMap<String, String> map = new HashMap<String, String>();
map.put(“keyname“, mEntries[i].toString());
listStr.add(map);
}
simple = new SimpleAdapter(getContext(), listStr, R.layout.dialog_view,
new String[] { “keyname“ }, new int[] { R.id.text });
listview.setAdapter(simple);
listview.setOnItemClickListener(this);
}
public void showDialog() {
listView = new ListView(getContext());
bindView(listView);
dialog = new Dialog(getContext(), android.R.style.Theme_NoTitleBar);
dialog.setContentView(listView);
dialog.setOnDismissListener(this);
dialog.show();
}
@Override
long id) {
// TODO Auto-generated method stub
mValue = listStr.get(position).get(“keyname“).toString();
persistString(mValue);
callChangeListener(mValue);
dialog.dismiss();
}
@Override
// TODO Auto-generated method stub
}
private OnPreferenceChangeListener temp;
public interface OnPreferenceChangeListener {
public boolean onPreferenceChange(Preference preference, Object newValue);
}
public void setOnPreferenceChangeListener(
OnPreferenceChangeListener preference) {
this.temp = preference;
}
public void setValue(String value) {
summary.setText(value);
}
public boolean callChangeListener(Object newValue) {
return temp == null ? true : temp.onPreferenceChange(this, newValue);
}
public void postBindPreferences() {
if (mHandler.hasMessages(MSG_BIND_PREFERENCES))
return;
mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
}
}
然後在preferenceActivity 介面在回撥函式:onPreferenceChange呼叫postBindPreferences即可更新。
Tip:這裡的onPreferenceChange 呼叫postBindPreferences 不是必須的,你同樣可以在內部裡面實現,通過執行某一操作傳送訊息也可。
好了,在這裡我要感謝那幾位朋友對我的幫助,提出了很多寶貴的意見。謝謝。