Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android 完美解決自定義preference與ActivityGroup UI更新的問題

Android 完美解決自定義preference與ActivityGroup UI更新的問題

編輯:Android開發實例

之前發過一篇有關於自定義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
     * [email protected] #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 不是必須的,你同樣可以在內部裡面實現,通過執行某一操作發送消息也可。

好了,在這裡我要感謝那幾位朋友對我的幫助,提出了很多寶貴的意見。謝謝。

 

轉自:http://www.cnblogs.com/TerryBlog/archive/2010/10/07/1845024.html

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved