您的位置:首页 > 产品设计 > UI/UE

Android 完美解决自定义preference与ActivityGroup UI更新的问题 推荐

2010-10-07 11:43 621 查看
之前发过一篇有关于自定义preference 在ActivityGroup 的包容下出现UI不能更新的问题,当时还以为是Android 的一个BUG 现在想想真可笑 。其实是自己对机制的理解不够深刻,看来以后要多看看源码才行。

本篇讲述内容大致为如何自定义preference 开始到与ActivityGroup 互用下UI更新的解决方法。

首先从扩展preference开始:

类文件必须继承自Preference并实现构造函数,这里我一般实现两个构造函数分别如下(类名为:test):

public test(Context context) {

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 添加一个数组引用,就可使用如下代码:

int resouceId = attrs.getAttributeResourceValue(null, "Entries", 0);

if (resouceId > 0) {

mEntries = getContext().getResources().getTextArray(resouceId);

}

这里的mEntries 是头部声明的一个数组,我们可以在xml文件通过 Entries=数组索引得到一个数组。在这里不深入为大家示范了。

我们扩展preference 有时想让其UI更丰富更好看,这里我们可以通过引用一个layout 文件为其指定UI,可以通过实现如下两个回调函数:

@Override

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处理,如下代码:

@Override

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);

}

如果你有接触过sharePreference 相信一眼就能看出这里它为我们做了什么。

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那么一定可以行得通,我查看一下源码,下面把源码贴出来:

@Override

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 的源码提供出来:

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;

public class PreferenceScreenExt extends PreferenceGroup implements

OnItemClickListener, 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

protected void onBindView(View view) {

// 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

protected View onCreateView(ViewGroup parent) {

// 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

public void onItemClick(AdapterView<?> parent, View view, int position,

long id) {

// TODO Auto-generated method stub

mValue = listStr.get(position).get("keyname").toString();

persistString(mValue);

callChangeListener(mValue);

dialog.dismiss();

}

@Override

public void onDismiss(DialogInterface dialog) {

// 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 不是必须的,你同样可以在内部里面实现,通过执行某一操作发送消息也可。

好了,在这里我要感谢那几位朋友对我的帮助,提出了很多宝贵的意见。谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐