博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 完美解决自定义preference与ActivityGroup UI更新的问题
阅读量:6476 次
发布时间:2019-06-23

本文共 9152 字,大约阅读时间需要 30 分钟。

之前发过一篇有关于自定义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 比较常用到的几个方法:

  • ( another)
    与另外一个preference比较,如果相等则返回0,不相等则返回小于0的数字。
  • ()
    获取上下文
  • ()
    得到一个SharePrefence 的Editor 对象
  • ()
    获取Intetn
  • ()
    获取当前我们在XML为其注册的KEY
  • ()
    得到当前layout 的来源
  • ()
    值改变的监听事件
  • ()
    获得一个preference管理
  • ()
    通过获得管理获取当前的sharePreferences
  • ()
    获得当前我们在XML为其注册的备注为summary 的值。Tip:在OnBindView 或者onCreateView 找到VIEW的时候如果存在summary 的View 对象必须为其设置summary
  • ()
    如上,不过这里是获取标题
  • ( newValue)
    如果你希望你扩展的Preference 可以支持当数值改变时候可以调用OnPreferenceChangeListener此监听方法,则必须调用此方法,查看该方法源码为:
     
    protected  boolean callChangeListener(Object newValue) {
            
    return  mOnChangeListener  ==  
    null   ?  
    true  : mOnChangeListener.onPreferenceChange(
    this , newValue);
        }
        

     

    源码简单不做过多介绍,只是实现一个接口。
  • (boolean defaultReturnValue)
    获得一个保存后的布尔值,查看一下源码:
     
    protected  boolean getPersistedBoolean(boolean defaultReturnValue) {
            
    if  ( ! shouldPersist()) {
                
    return  defaultReturnValue;
            }
            
            
    return  mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
        }

     

    如果你有接触过sharePreference 相信一眼就能看出这里它为我们做了什么。
  • (float defaultReturnValue)
    如上,这里获取一个Float 的值
  • (int defaultReturnValue)
    如上,获取一个int 的值
  • (long defaultReturnValue)
    如上,获取一个Long 型数值
  • ( defaultReturnValue)
    如上,获取一个String 数值
  • (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 不是必须的,你同样可以在内部里面实现,通过执行某一操作发送消息也可。

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

 本文转自 terry_龙 51CTO博客,原文链接:http://blog.51cto.com/terryblog/401819,如需转载请自行联系原作者

你可能感兴趣的文章
使用ReaderWriterLock类实现多用户读/单用户写同步
查看>>
第4次作业类测试代码+105032014158+余超勇
查看>>
有关于虚拟DOM
查看>>
codves 4927 线段树练习5
查看>>
幻方(zjut1005) 与 反幻方(hdu 3927)
查看>>
vue中nextTick的使用(转载)
查看>>
python修行之路(六 三级菜单实例)
查看>>
openssl编译安装-各种蛋疼
查看>>
MongoDB 概念解析
查看>>
配置_DruidDataSource参考配置
查看>>
《深入理解Oracle 12c数据库管理(第二版)》PDF
查看>>
电影:为人师表
查看>>
Description of security events in Windows 2003/7/2008
查看>>
(笑话)我老公不在家,你快点上来!
查看>>
sql里面的模糊查询 用到通配符 格式如右边:'%"&变量名称&"%'
查看>>
Tsung测试之配置文件
查看>>
C#图像处理:截图程序(包含鼠标)
查看>>
DotNetBar for Windows Forms 12.7.0.10_冰河之刃重打包版原创发布-带官方示例程序版
查看>>
HTML save data to CSV or excel
查看>>
python库参考学习网址
查看>>