读书人

PreferenceActivity UI 优化批改

发布时间: 2012-09-28 00:03:35 作者: rapoo

PreferenceActivity UI 优化修改

?虽然PreferenceActivity的UI比较搓,但是由于其良好的封装性和实用性,所以在一些场景还是有一定的使用价值。所以如何能优化它的UI让它和你程序相配就十分必要了。毕竟对于程序员来说能懒点就懒点,哈哈。

?首先,

?

public abstract class PreferenceActivity extends ListActivity implements        PreferenceManager.OnPreferenceTreeClickListener,        PreferenceFragment.OnPreferenceStartFragmentCallback

? 这就表明可以替换背景,可以替换Divider,Selector。

? 它的adapter是PreferenceGroupAdapter,见于PreferenceScreen:

?

    public void bind(ListView listView) {        listView.setOnItemClickListener(this);        listView.setAdapter(getRootAdapter());                onAttachedToActivity();    }

?? 这个方法在PreferenceActivity里被掉,用于加载adapter。

package android.preference;import java.util.ArrayList;import java.util.Collections;import java.util.List;import android.os.Handler;import android.preference.Preference.OnPreferenceChangeInternalListener;import android.view.View;import android.view.ViewGroup;import android.widget.Adapter;import android.widget.BaseAdapter;import android.widget.ListView;/** * An adapter that returns the {@link Preference} contained in this group. * In most cases, this adapter should be the base class for any custom * adapters from {@link Preference#getAdapter()}. * <p> * This adapter obeys the * {@link Preference}'s adapter rule (the * {@link Adapter#getView(int, View, ViewGroup)} should be used instead of * {@link Preference#getView(ViewGroup)} if a {@link Preference} has an * adapter via {@link Preference#getAdapter()}). * <p> * This adapter also propagates data change/invalidated notifications upward. * <p> * This adapter does not include this {@link PreferenceGroup} in the returned * adapter, use {@link PreferenceCategoryAdapter} instead. *  * @see PreferenceCategoryAdapter */class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeInternalListener {        private static final String TAG = "PreferenceGroupAdapter";    /**     * The group that we are providing data from.     */    private PreferenceGroup mPreferenceGroup;        /**     * Maps a position into this adapter -> {@link Preference}. These     * {@link Preference}s don't have to be direct children of this     * {@link PreferenceGroup}, they can be grand children or younger)     */    private List<Preference> mPreferenceList;        /**     * List of unique Preference and its subclasses' names. This is used to find     * out how many types of views this adapter can return. Once the count is     * returned, this cannot be modified (since the ListView only checks the     * count once--when the adapter is being set). We will not recycle views for     * Preference subclasses seen after the count has been returned.     */    private ArrayList<PreferenceLayout> mPreferenceLayouts;    private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();    /**     * Blocks the mPreferenceClassNames from being changed anymore.     */    private boolean mHasReturnedViewTypeCount = false;        private volatile boolean mIsSyncing = false;        private Handler mHandler = new Handler();         private Runnable mSyncRunnable = new Runnable() {        public void run() {            syncMyPreferences();        }    };    private static class PreferenceLayout implements Comparable<PreferenceLayout> {        private int resId;        private int widgetResId;        private String name;        public int compareTo(PreferenceLayout other) {            int compareNames = name.compareTo(other.name);            if (compareNames == 0) {                if (resId == other.resId) {                    if (widgetResId == other.widgetResId) {                        return 0;                    } else {                        return widgetResId - other.widgetResId;                    }                } else {                    return resId - other.resId;                }            } else {                return compareNames;            }        }    }    public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {        mPreferenceGroup = preferenceGroup;        // If this group gets or loses any children, let us know        mPreferenceGroup.setOnPreferenceChangeInternalListener(this);        mPreferenceList = new ArrayList<Preference>();        mPreferenceLayouts = new ArrayList<PreferenceLayout>();        syncMyPreferences();    }    private void syncMyPreferences() {        synchronized(this) {            if (mIsSyncing) {                return;            }            mIsSyncing = true;        }        List<Preference> newPreferenceList = new ArrayList<Preference>(mPreferenceList.size());        flattenPreferenceGroup(newPreferenceList, mPreferenceGroup);        mPreferenceList = newPreferenceList;                notifyDataSetChanged();        synchronized(this) {            mIsSyncing = false;            notifyAll();        }    }        private void flattenPreferenceGroup(List<Preference> preferences, PreferenceGroup group) {        // TODO: shouldn't always?        group.sortPreferences();        final int groupSize = group.getPreferenceCount();        for (int i = 0; i < groupSize; i++) {            final Preference preference = group.getPreference(i);                        preferences.add(preference);                        if (!mHasReturnedViewTypeCount && !preference.hasSpecifiedLayout()) {                addPreferenceClassName(preference);            }                        if (preference instanceof PreferenceGroup) {                final PreferenceGroup preferenceAsGroup = (PreferenceGroup) preference;                if (preferenceAsGroup.isOnSameScreenAsChildren()) {                    flattenPreferenceGroup(preferences, preferenceAsGroup);                }            }            preference.setOnPreferenceChangeInternalListener(this);        }    }    /**     * Creates a string that includes the preference name, layout id and widget layout id.     * If a particular preference type uses 2 different resources, they will be treated as     * different view types.     */    private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {        PreferenceLayout pl = in != null? in : new PreferenceLayout();        pl.name = preference.getClass().getName();        pl.resId = preference.getLayoutResource();        pl.widgetResId = preference.getWidgetLayoutResource();        return pl;    }    private void addPreferenceClassName(Preference preference) {        final PreferenceLayout pl = createPreferenceLayout(preference, null);        int insertPos = Collections.binarySearch(mPreferenceLayouts, pl);        // Only insert if it doesn't exist (when it is negative).        if (insertPos < 0) {            // Convert to insert index            insertPos = insertPos * -1 - 1;            mPreferenceLayouts.add(insertPos, pl);        }    }        public int getCount() {        return mPreferenceList.size();    }    public Preference getItem(int position) {        if (position < 0 || position >= getCount()) return null;        return mPreferenceList.get(position);    }    public long getItemId(int position) {        if (position < 0 || position >= getCount()) return ListView.INVALID_ROW_ID;        return this.getItem(position).getId();    }    public View getView(int position, View convertView, ViewGroup parent) {        final Preference preference = this.getItem(position);        // Build a PreferenceLayout to compare with known ones that are cacheable.        mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);        // If it's not one of the cached ones, set the convertView to null so that         // the layout gets re-created by the Preference.        if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0) {            convertView = null;        }        return preference.getView(convertView, parent);    }    @Override    public boolean isEnabled(int position) {        if (position < 0 || position >= getCount()) return true;        return this.getItem(position).isSelectable();    }    @Override    public boolean areAllItemsEnabled() {        // There should always be a preference group, and these groups are always        // disabled        return false;    }    public void onPreferenceChange(Preference preference) {        notifyDataSetChanged();    }    public void onPreferenceHierarchyChange(Preference preference) {        mHandler.removeCallbacks(mSyncRunnable);        mHandler.post(mSyncRunnable);    }    @Override    public boolean hasStableIds() {        return true;    }    @Override    public int getItemViewType(int position) {        if (!mHasReturnedViewTypeCount) {            mHasReturnedViewTypeCount = true;        }                final Preference preference = this.getItem(position);        if (preference.hasSpecifiedLayout()) {            return IGNORE_ITEM_VIEW_TYPE;        }        mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);        int viewType = Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout);        if (viewType < 0) {            // This is a class that was seen after we returned the count, so            // don't recycle it.            return IGNORE_ITEM_VIEW_TYPE;        } else {            return viewType;        }    }    @Override    public int getViewTypeCount() {        if (!mHasReturnedViewTypeCount) {            mHasReturnedViewTypeCount = true;        }                return Math.max(1, mPreferenceLayouts.size());    }}

? 它的getView里掉的是Preference的getView。Preference类似于View,是所有相关UI类的基类。以下是和UI相关的重要代码。

    public View getView(View convertView, ViewGroup parent) {        if (convertView == null) {            convertView = onCreateView(parent);        }        onBindView(convertView);        return convertView;    }    protected View onCreateView(ViewGroup parent) {        final LayoutInflater layoutInflater =            (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);                final View layout = layoutInflater.inflate(mLayoutResId, parent, false);                 final ViewGroup widgetFrame = (ViewGroup) layout                .findViewById(com.android.internal.R.id.widget_frame);        if (widgetFrame != null) {            if (mWidgetLayoutResId != 0) {                layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);            } else {                widgetFrame.setVisibility(View.GONE);            }        }        return layout;    }

?所以基本上每个Preference UI控件最多和mLayoutResId和mWidgetLayoutResId相关。

?首先查找它们的布局,比如PreferenceCategory,attr为com.android.internal.R.attr.preferenceCategoryStyle,

对应主题中的style为<item name="preferenceCategoryStyle">@android:style/Preference.Category</item>。

<style name="Preference.Category"><item name="android:layout">@android:layout/preference_category</item><!-- The title should not dim if the category is disabled, instead only the preference children should dim. --><item name="android:shouldDisableView">false</item><item name="android:selectable">false</item></style>

?

?

 如下列出所有的attr:<declare-styleable name="Theme">......<attr name="preferenceScreenStyle" format="reference"/><!-- Default style for PreferenceCategory. --><attr name="preferenceCategoryStyle" format="reference"/><!-- Default style for Preference. --><attr name="preferenceStyle" format="reference"/><!-- Default style for informational Preference. --><attr name="preferenceInformationStyle" format="reference"/><!-- Default style for CheckBoxPreference. --><attr name="checkBoxPreferenceStyle" format="reference"/><!-- Default style for YesNoPreference. --><attr name="yesNoPreferenceStyle" format="reference"/><!-- Default style for DialogPreference. --><attr name="dialogPreferenceStyle" format="reference"/><!-- Default style for EditTextPreference. --><attr name="editTextPreferenceStyle" format="reference"/><!-- Default style for RingtonePreference. --><attr name="ringtonePreferenceStyle" format="reference"/><!-- The preference layout that has the child/tabbed effect. --><attr name="preferenceLayoutChild" format="reference"/></declare-styleable>对应的style为:<style name="Theme">......?<!-- Preference styles --><item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item><item name="preferenceCategoryStyle">@android:style/Preference.Category</item><item name="preferenceStyle">@android:style/Preference</item><item name="preferenceInformationStyle">@android:style/Preference.Information</item><item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item><item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item><item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item><item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item><item name="ringtonePreferenceStyle">@android:style/Preference.RingtonePreference</item><item name="preferenceLayoutChild">@android:layout/preference_child</item></style>

?? 我没搞清楚的是这个style是在什么地方设进去的。所以如果要修改PreferenceCategory的UI,只需从系统源码中拷贝出这个布局XML,修改它的title的id:@+android:id/title为@android:id/title,同时??

<PreferenceCategory
??????? android:layout="@layout/preference_category"

??????? android:title="你好1" >

由于PreferenceCategory是直接加载preference_category的,所以替换了默认的preference.xml,所以就算你配上android:widgetLayout也没用。

? 修改CheckBoxPreference的UI需要:layout->preference.xml widgetLayout->preference_widget_checkbox.xml,同时修改id。

? 拿到布局文件后,字体,字体大小,颜色等布局元素你想怎么弄就怎么弄了。PreferenceActivity UI 优化批改

需要注意的是,如果你想要保存那写选中的数据,必须要对preference UI 控件设置key。因为保存XML时候必须要有key。


PreferenceActivity UI 优化批改

读书人网 >移动开发

热点推荐