之前发过一篇有关于自定义preference 在ActivityGroup 的包容下出现UI不能更新的问题,当时还以为是Android 的一个BUG 现在想想真可笑 。其实是自己对机制的理解不够深刻,看来以后要多看看源码才行。
本篇讲述内容大致为如何自定义preference 开始到与ActivityGroup 互用下UI更新的解决方法。
首先从扩展preference开始:
类文件必须继承自Preference并实现构造函数,这里我一般实现两个构造函数分别如下(类名为:test):
publictest(Context context) {this(context,null);//TODO Auto-generated constructor stub}publictest(Context context, AttributeSet attrs) {
super(context, attrs);//TODO Auto-generated constructor stub}
这里第二个构造函数第二个参数为可以使用attrs 为我们自定义的preference 添加扩展的注册属性,比如我们如果希望为扩展的preference 添加一个数组引用,就可使用如下代码:
intresouceId=attrs.getAttributeResourceValue(null,"Entries",0);if(resouceId>0) {
mEntries=getContext().getResources().getTextArray(resouceId);
}
这里的mEntries 是头部声明的一个数组,我们可以在xml文件通过 Entries=数组索引得到一个数组。在这里不深入为大家示范了。
我们扩展preference 有时想让其UI更丰富更好看,这里我们可以通过引用一个layout 文件为其指定UI,可以通过实现如下两个回调函数:
@OverrideprotectedView onCreateView(ViewGroup parent) {//TODO Auto-generated method stubreturnLayoutInflater.from(getContext()).inflate(
R.layout.preference_screen, parent,false);
}
此回调函数与onBindView 一一对应,并优先执行于onBindView ,当创建完后将得到的VIEW返回出去给onBindView处理,如下代码:
@OverrideprotectedvoidonBindView(View view) {//TODO Auto-generated method stubsuper.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 比较常用到的几个方法:
Preference another)
与另外一个preference比较,如果相等则返回0,不相等则返回小于0的数字。
Object newValue)
如果你希望你扩展的Preference 可以支持当数值改变时候可以调用OnPreferenceChangeListener此监听方法,则必须调用此方法,查看该方法源码为:
protectedboolean callChangeListener(Object newValue) {returnmOnChangeListener==null?true: mOnChangeListener.onPreferenceChange(this, newValue);
}
源码简单不做过多介绍,只是实现一个接口。
protectedboolean getPersistedBoolean(boolean defaultReturnValue) {if(!shouldPersist()) {returndefaultReturnValue;
}returnmPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
}如果你有接触过sharePreference 相信一眼就能看出这里它为我们做了什么。
如上,获取一个String 数值
protectedboolean persistBoolean(boolean value) {if(shouldPersist()) {if(value==getPersistedBoolean(!value)) {//It's already there, so the same as persistingreturntrue;
}
SharedPreferences.Editor editor=mPreferenceManager.getEditor();
editor.putBoolean(mKey, value);
tryCommit(editor);returntrue;
}returnfalse;
}
都是sharePreference 的知识,这里不做过多介绍。其他的跟上面的都 一样,略过。
通过如上的一些设置,一个基本的扩展preference 就己经完成,下面来讲讲如果在ActivityGroup 里面让扩展的preference可以更新UI。之前 农民伯伯 探讨过,他建议我使用onContentChanged()方法,可以使UI更新 ,试了一下发现有些许问题,不过非常感谢农民伯伯。这个方法是全局刷新,则全部UI都刷新一次,但是这样不是很合理,我想了一下,那既然此方法可以更新UI那么一定可以行得通,我查看一下源码,下面把源码贴出来:
@OverridepublicvoidonContentChanged() {
super.onContentChanged();
postBindPreferences();
}/**
* Posts a message to bind the preferences to the list view.
*
* Binding late is preferred as any custom preference types created in
* {@link #onCreate(Bundle)} are able to have their views recycled.*/privatevoidpostBindPreferences() {if(mHandler.hasMessages(MSG_BIND_PREFERENCES))return;
mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
}privatevoidbindPreferences() {
final PreferenceScreen preferenceScreen=getPreferenceScreen();if(preferenceScreen!=null) {
preferenceScreen.bind(getListView());
}
}
privatestaticfinalintMSG_BIND_PREFERENCES=0;privateHandler mHandler=newHandler() {
@OverridepublicvoidhandleMessage(Message msg) {switch(msg.what) {caseMSG_BIND_PREFERENCES:
bindPreferences();break;
}
}
};
原来,这里它是另开一条线程来更新UI,然后当值发生变化时为其发送消息,在消息队列里面处理UI,只不过它这里继承了listActivity 更新了一整个listView ,那么我们就将它提取出来,只更新我们想要的UI则可。OK,思路出来了,下面将我扩展的一个preference 的源码提供出来:
package com.yaomei.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;publicclassPreferenceScreenExt extends PreferenceGroup implements
OnItemClickListener, DialogInterface.OnDismissListener {privateDialog dialog;privateTextView title, summary;privateRelativeLayout area;privateListView listView;
Listlist;privateList>listStr;privateCharSequence[] mEntries;privateString mValue;privateSimpleAdapter simple;privatestaticfinalintMSG_BIND_PREFERENCES=0;privateHandler mHandler=newHandler() {
@OverridepublicvoidhandleMessage(Message msg) {switch(msg.what) {caseMSG_BIND_PREFERENCES:
setValue(mValue);break;
}
}
};publicPreferenceScreenExt(Context context, AttributeSet attrs) {this(context, attrs, android.R.attr.preferenceScreenStyle);//TODO Auto-generated constructor stub}publicPreferenceScreenExt(Context context, AttributeSet attrs,intdefStyle) {
super(context, attrs, android.R.attr.preferenceScreenStyle);//TODO Auto-generated constructor stubintresouceId=attrs.getAttributeResourceValue(null,"Entries",0);if(resouceId>0) {
mEntries=getContext().getResources().getTextArray(resouceId);
}
}
@OverrideprotectedvoidonBindView(View view) {//TODO Auto-generated method stubarea=(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(newOnClickListener() {
@OverridepublicvoidonClick(View v) {//TODO Auto-generated method stubshowDialog();
}
});
}
@OverrideprotectedView onCreateView(ViewGroup parent) {//TODO Auto-generated method stuView view=LayoutInflater.from(getContext()).inflate(
R.layout.preference_screen, parent,false);returnview;
}publicvoidbindView(ListView listview) {intlength=mEntries.length;inti=0;
listStr=newArrayList>();for(i=0; i
HashMapmap=newHashMap();
map.put("keyname", mEntries[i].toString());
listStr.add(map);
}
simple=newSimpleAdapter(getContext(), listStr, R.layout.dialog_view,newString[] {"keyname"},newint[] { R.id.text });
listview.setAdapter(simple);
listview.setOnItemClickListener(this);
}publicvoidshowDialog() {
listView=newListView(getContext());
bindView(listView);
dialog=newDialog(getContext(), android.R.style.Theme_NoTitleBar);
dialog.setContentView(listView);
dialog.setOnDismissListener(this);
dialog.show();
}
@OverridepublicvoidonItemClick(AdapterView>parent, View view,intposition,longid) {//TODO Auto-generated method stubmValue=listStr.get(position).get("keyname").toString();
persistString(mValue);
callChangeListener(mValue);
dialog.dismiss();
}
@OverridepublicvoidonDismiss(DialogInterface dialog) {//TODO Auto-generated method stub}privateOnPreferenceChangeListener temp;publicinterfaceOnPreferenceChangeListener {publicboolean onPreferenceChange(Preference preference, Object newValue);
}publicvoidsetOnPreferenceChangeListener(
OnPreferenceChangeListener preference) {this.temp=preference;
}publicvoidsetValue(String value) {
summary.setText(value);
}publicboolean callChangeListener(Object newValue) {returntemp==null?true: temp.onPreferenceChange(this, newValue);
}publicvoidpostBindPreferences() {if(mHandler.hasMessages(MSG_BIND_PREFERENCES))return;
mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
}
}
然后在preferenceActivity 界面在回调函数:onPreferenceChange调用postBindPreferences即可更新。
Tip:这里的onPreferenceChange 调用postBindPreferences 不是必须的,你同样可以在内部里面实现,通过执行某一操作发送消息也可。
好了,在这里我要感谢那几位朋友对我的帮助,提出了很多宝贵的意见。谢谢。