`

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。

  拿到布局文件后,字体,字体大小,颜色等布局元素你想怎么弄就怎么弄了。

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


  • 大小: 12.6 KB
1
1
分享到:
评论

相关推荐

    PreferenceActivity简介

    这样,PreferenceActivity会解析XML文件,并自动在界面上生成对应的UI元素。 为了响应用户的操作,如点击事件,你可以重写onOptionsItemSelected(MenuItem item)方法,检查被选中的菜单项是否来自...

    自定义PreferenceActivity

    然而,标准的`PreferenceActivity`样式可能无法满足所有设计需求,因此开发者有时需要自定义`PreferenceActivity`来实现特定的UI效果。 自定义`PreferenceActivity`主要包括以下几个方面: 1. **布局文件定制**: ...

    PreferenceActivity

    使用 PreferenceActivity设置个性信息, 参考:android 设置类PreferenceActivity http://blog.csdn.net/xiaobijia/article/details/41479235

    PreferenceActivity用法简介Android X

    PreferenceActivity用法简介 Android X

    继承PreferenceActivity

    4. **更新UI**:当偏好设置发生变化时,可能需要更新相应的UI元素。这可以通过监听`SharedPreferences`的变化来实现,例如使用`SharedPreferences.OnSharedPreferenceChangeListener`。 5. **适配新版本**:由于`...

    仿IOS的PreferenceActivity界面

    "仿IOS的PreferenceActivity界面"就是一个这样的实践,它主要涉及到Android的Preference类和Activity的使用,以及UI样式的美化。PreferenceActivity是Android系统提供的一种用于展示设置界面的特殊Activity,它能够...

    Android 属性页PreferenceActivity的实现

    Google推荐使用`PreferenceFragmentCompat`或`PreferenceFragment`来代替,它们可以在任何版本的Android上运行,并提供了更好的兼容性和更现代的UI设计。例如,使用`PreferenceFragmentCompat`的方式如下: ```java...

    Android软件开发之PreferenceActivity中组件的使用

    在Android应用开发中,PreferenceActivity是用于创建设置界面的一个特殊Activity。它允许开发者以XML方式定义用户界面元素,如开关、选择列表等,使得创建设置界面变得更加简洁和规范。本教程将深入探讨如何在...

    PreferenceActivity 修改成Activity

    8. **保存和恢复状态**: 如果需要在配置更改(如屏幕旋转)后保持设置状态,可以在`onSaveInstanceState`和`onRestoreInstanceState`方法中处理。 9. **测试和调试**: 完成以上步骤后,运行应用并测试设置界面的...

    PreferenceActivity 类的使用,它可以保存设置

    在实际开发中,我们可能会遇到需要自定义Preference的情况,比如需要添加额外的事件监听或者改变默认的UI行为。这时,我们可以创建一个继承自Preference的子类,并覆盖相应的方法。然后在XML中使用这个自定义的类名...

    自定义PreferenceActivity的样式和界面

    在Android开发中,PreferenceActivity是用于创建设置界面的标准组件,它允许开发者以XML方式定义用户界面,然后在活动中...在实际项目中,我们应该根据具体需求进行适当的调整和优化,确保自定义的界面既美观又实用。

    android PreferenceActivity中的组件源码

    android PreferenceActivity中的组件源码~

    Android PreferenceActivity

    开发者可以通过重写`onCreate()`方法并设置监听器来获取用户对设置项的更改。 4. **导航**:在`PreferenceScreen`中,可以使用`&lt;intent&gt;`标签来定义跳转其他Activity或Intent的行为,这样用户可以在设置界面中进行...

    Android之PreferenceActivity简介

    在PreferenceActivity中,当用户更改了设置后,通常需要刷新应用的状态或者更新UI。这可以通过监听Preference的更改事件来实现。例如,可以在`onPreferenceChange`回调方法中处理这些事件,以确保应用的行为与用户的...

    玩转Android--UI篇--PreferenceActivity(开启wifi和音乐等)

    在Android开发中,UI设计是至关重要的一环,而PreferenceActivity是Android系统提供的一种用于构建设置界面的特殊Activity。本篇文章将深入探讨如何利用PreferenceActivity来创建用户交互界面,特别是涉及开启WiFi和...

    preferenceActivity的简单使用

    使用`PreferenceActivity`可以让开发者避免编写大量的UI代码,只需在XML布局文件中定义偏好设置,然后在代码中加载即可。 ### 2. 创建`PreferenceActivity` 首先,我们需要创建一个新的Activity,并让它继承自`...

Global site tag (gtag.js) - Google Analytics