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

it2022-05-09  23

之前发过一篇有关于自定义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()获取IntetngetKey()获取当前我们在XML为其注册的KEYgetLayoutResource()得到当前layout 的来源getOnPreferenceChangeListener()值改变的监听事件getPreferenceManager()获得一个preference管理getSharedPreferences()通过获得管理获取当前的sharePreferencesgetSummary()获得当前我们在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 不是必须的,你同样可以在内部里面实现,通过执行某一操作发送消息也可。

好了,在这里我要感谢那几位朋友对我的帮助,提出了很多宝贵的意见。谢谢。

转载于:https://www.cnblogs.com/TerryBlog/archive/2010/10/07/1845024.html

相关资源:数据结构—成绩单生成器

最新回复(0)