如何禁用PreferenceActivity中的Preference Header

时间:2014-06-17 13:45:00

标签: android android-listview android-preferences

我的Activity扩展了PreferenceActivity

onBuildHeaders中,我加载了我在XML中定义的preference-headers

@Override
public void onBuildHeaders(List<Header> target) {               
     loadHeadersFromResource(R.xml.admin_preference_headers, target);
} 

这是XML文件本身。

<?xml version="1.0" encoding="utf-8"?>
<preference-headers
        xmlns:android="http://schemas.android.com/apk/res/android">
    <header android:fragment="..."
            android:icon="@drawable/image"
            android:title="title"
            android:summary="summery" />
</preference-headers>

如何停用这些标题,使它们变灰,使它们无法点击?

我的猜测是操纵PreferenceActivity的ListView项目,但我不确定该怎么做。

3 个答案:

答案 0 :(得分:2)

一种可能性是覆盖扩展isEnabled(int position)的适配器中的ArrayAdapter<Header>。这会使项目默认不可点击,然后您可以在getView(int position, View convertView, ViewGroup parent)方法中将文本设为灰色。

然后在PreferenceActivity中,您将覆盖setListAdapter(ListAdapter adapter)以使用自定义适配器调用super

答案 1 :(得分:1)

dodkoc的回答是正确的,但还不完整。

假设您根据偏好设置禁用标头。在这种情况下,如果您只是覆盖setListAdapter()方法并将其与自定义适配器一起提供,则更改该首选项时,标题列表将不会更新。这是因为PreferenceActivitysetListAdapter()方法中仅调用onCreate()一次。拨打invalidateHeaders()也无济于事。

所以我就是这样做的:

  1. 创建自定义适配器。我坚持使用PreferenceActivity源代码中的适配器类,并添加了一个方法,当应该启用给定位置的标头时,该方法返回true。
  2. <强> HeaderAdapter

    private static class HeaderAdapter extends ArrayAdapter<Header> {
    
        private static class HeaderViewHolder {
            ImageView icon;
            TextView title;
            TextView summary;
        }
    
        private LayoutInflater mInflater;
        private int mLayoutResId;
        private boolean mRemoveIconIfEmpty;
    
        public HeaderAdapter(Context context, List<Header> objects, int layoutResId, boolean removeIconBehavior) {
            super(context, 0, objects);
            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            mLayoutResId = layoutResId;
            mRemoveIconIfEmpty = removeIconBehavior;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            HeaderViewHolder holder;
            View view;
            if (convertView == null) {
                view = mInflater.inflate(mLayoutResId, parent, false);
                holder = new HeaderViewHolder();
                holder.icon = (ImageView) view.findViewById(R.id.preference_header_list_item_icon);
                holder.title = (TextView) view.findViewById(R.id.preference_header_list_item_title);
                holder.summary = (TextView) view.findViewById(R.id.preference_header_list_item_summary);
                view.setTag(holder);
            } else {
                view = convertView;
                holder = (HeaderViewHolder) view.getTag();
            }
            // All view fields must be updated every time, because the view may be recycled
            Header header = getItem(position);
            if (mRemoveIconIfEmpty) {
                if (header.iconRes == 0) {
                    holder.icon.setVisibility(View.GONE);
                } else {
                    holder.icon.setVisibility(View.VISIBLE);
                    holder.icon.setImageResource(header.iconRes);
                }
            } else {
                holder.icon.setImageResource(header.iconRes);
            }
            boolean isEnabled = isHeaderEnabled(position);
            holder.title.setText(header.getTitle(getContext().getResources()));
            int enabledColor = getContext().getResources().getColor(R.color.text);
            int disabledTitleColor = getContext().getResources().getColor(R.color.text_disabled);
            holder.title.setTextColor(isEnabled ? enabledColor : disabledTitleColor);
            CharSequence summary = header.getSummary(getContext().getResources());
            if (!TextUtils.isEmpty(summary)) {
                holder.summary.setVisibility(View.VISIBLE);
                holder.summary.setText(summary);
                int disabledSummaryColor = getContext().getResources().getColor(R.color.secondary_text_disabled);
                holder.summary.setTextColor(isEnabled ? enabledColor : disabledSummaryColor);
            } else {
                holder.summary.setVisibility(View.GONE);
            }
            return view;
        }
    
        public boolean isHeaderEnabled(int position) {
            boolean isEnabled = true;
            if (position == 2) {
                String prefValue = PreferenceManager.getDefaultSharedPreferences(getContext()).getString(PREF_KEY, null);
                if (prefValue != null) {
                    if (//TODO Your condition to disable the header based on prefValue)
                        isEnabled = false;
                }
            }
            return isEnabled;
        }
    
    }
    

    注意:您不希望覆盖isEnabled()中的ArrayAdapter方法,以便为要禁用的项目返回false。这会混淆Android 5.0(Lollipop)中ListView中的分隔符。如果项目的isEnabled()返回false,则该项目周围的分隔符将在Android 5.0中消失。因此,我创建了另一个名为isHeaderEnabled()的方法来检查项目是否应该启用。

    1. 我还将源代码中的标题列表项的布局复制到我的布局文件夹中,并重命名为ids
    2. <强> preference_header_item.xml

          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:background="?android:attr/activatedBackgroundIndicator"
              android:gravity="center_vertical"
              android:minHeight="48dp"
              android:paddingRight="?android:attr/scrollbarSize">
      
              <ImageView
                  android:id="@+id/preference_header_list_item_icon"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_gravity="center"
                  android:layout_marginEnd="6dp"
                  android:layout_marginStart="6dp" />
      
              <RelativeLayout
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_marginBottom="6dp"
                  android:layout_marginLeft="2dp"
                  android:layout_marginRight="6dp"
                  android:layout_marginTop="6dp"
                  android:layout_weight="1">
      
                  <TextView
                      android:id="@+id/preference_header_list_item_title"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:ellipsize="marquee"
                      android:fadingEdge="horizontal"
                      android:singleLine="true"
                      android:textAppearance="?android:attr/textAppearanceMedium" />
      
                  <TextView
                      android:id="@+id/preference_header_list_item_summary"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:layout_alignLeft="@+id/preference_header_list_item_title"
                      android:layout_below="@+id/preference_header_list_item_title"
                      android:ellipsize="end"
                      android:maxLines="2"
                      android:textAppearance="?android:attr/textAppearanceSmall" />
      
              </RelativeLayout>
      
          </LinearLayout>
      
      1. 覆盖onPostResume()中的onBuildHeaders()onListItemClick()PreferenceActivity方法。在onPostResume()方法中,我调用invalidateHeader()重新创建标题列表。但是这个列表没有传递给列表适配器,因此ListView不会自动更新。所以我在onBuildHeaders()方法中手动执行此操作。最后,由于我没有覆盖isEnabled()中的ArrayAdapter方法以针对禁用的项返回false,因此用户仍然可以单击已禁用的标头。因此,我覆盖了onListItemClick()方法并绕过了对已禁用标头的超级调用。
      2. SettingsActivity.java(仅显示相关方法)

        @Override
        protected void onPostResume() {
            super.onPostResume();
            invalidateHeaders();
        }
        
        @Override
        public void onBuildHeaders(List<Header> target) {
            loadHeadersFromResource(R.xml.pref_headers, target);
            // Updating summary to indicate why the header is disabled as per the android design guideline
            String prefValue = PreferenceManager.getDefaultSharedPreferences(this).getString(PREF_KEY, null);
            if (prefValue != null) {
                if (//TODO Your condition to disable the header based on prefValue) {
                    target.get(2).summary = "Not applicable because you have not enabled something";
                } else {
                    target.get(2).summary = null;
                }
            }
            // Creating a new adapter based on the new header list and passing it to the ListView
            HeaderAdapter adapter = new HeaderAdapter(this, target, R.layout.preference_header_list_item, true);
            setListAdapter(adapter);
        }
        
        @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
            if (((HeaderAdapter) getListView().getAdapter()).isHeaderEnabled(position))
                super.onListItemClick(l, v, position, id);
        }
        

        注意:如果您处于多窗格模式,则可能需要在相关偏好设置的invalidateHeaders()方法中调用OnPreferenceChangeListener.onPreferenceChange(),因为onResume()将在不同标题的内容之间移动时不会被调用。

答案 2 :(得分:0)

在我看来,调整PreferenceActivity现有布局的最简单方法是获取当前的适配器,并通过对其进行装饰来尽可能少地进行更改。

@Override
public void onBuildHeaders(List<Header> target) {
    loadHeadersFromResource(...);

    final ListAdapter originalAdapter = getListAdapter();
    final ArrayAdapter<Header> adapter = new ArrayAdapter<Header>(this, 0, target) {
        @NonNull
        @Override
        public View getView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
            final View view = originalAdapter.getView(position, convertView, parent);
            // alter some view based on its position
            if (position == 2) {
                ((TextView) view.findViewById(android.R.id.title))
                    .setTextColor(ContextCompat.getColor(Activity.this, android.R.color.secondary_text_dark_nodisable));
            }
            return view;
        }
    };
    setListAdapter(adapter);
}

这样,您就不必在PreferencesActivity中遇到各种UX不一致。

除非注意:不能使用PreferenceActivity或任何与之相关的API 。这是纯粹的邪恶!