【问题标题】:How to create preference headers groups in Android PreferenceActivity?如何在 Android PreferenceActivity 中创建首选项标题组?
【发布时间】:2013-01-17 00:53:48
【问题描述】:

我正在使用首选项标头使用PreferenceActivity 创建设置活动。我正在尝试将标题划分为类别/组,例如这个(有无线和网络、设备、个人等类别):

无论如何,即使那个 Android 开发者网站是关于这种创建偏好活动的方式,我也找不到任何方法来创建与图像上相同的偏好活动。我唯一能做的就是简单的首选项标题列表。

我发现的唯一东西是this,但这有点……奇怪。所以这似乎不是一种选择。

所以我的问题是:如何使用首选项标头创建PreferenceActivity,并可以将标头划分为类别并可以使用主开/关开关?

我的一些代码:

preference_headers.xml

<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    <header 
        android:fragment="cz.vse.myevents.activity.SettingsActivity$EventsFragment"
        android:title="@string/settings_events"
        android:icon="@android:drawable/ic_menu_agenda" />
    <header 
        android:fragment="cz.vse.myevents.activity.SettingsActivity$OrganizationsFragment"
        android:title="@string/settings_subscribed_organizations"
        android:icon="@android:drawable/ic_menu_view"  />
</preference-headers>

设置活动

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

我不是发碎片资源,觉得没必要。

【问题讨论】:

    标签: android preferences


    【解决方案1】:

    这实际上非常简单。根据我的发现,根PreferenceActivity本身不支持添加类别/章节标题,似乎只能添加Headers——这不是很有趣。

    因此,您首先需要做的不是在 PreferenceActivity 本身中做任何繁重的工作,而是直接加载 PreferenceFragment

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setTitle("Settings");
    
        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new PreferencesFragment())
                .commit();
    
    }
    
    public static class PreferencesFragment extends PreferenceFragment {
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            addPreferencesFromResource(R.xml.prefs);
        }
    }
    

    完成此操作后,您现在可以在 PreferenceFragment 中完成所有工作,好消息是您现在可以使用类别

    您的 R.xml.prefs 文件应如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
        <PreferenceCategory
            android:summary="Login credentials"
            android:title="Login credentials" >
            <EditTextPreference
                android:key="username"
                android:summary="Username"
                android:title="Username" />
            <EditTextPreference
                android:key="password"
                android:summary="Password"
                android:title="Password" />
        </PreferenceCategory>
    
        <PreferenceCategory
            android:summary="Settings"
            android:title="Settings" >
            <CheckBoxPreference
                android:key="persist"
                android:summary="Yes/No"
                android:title="Keep me signed in" />
    
        </PreferenceCategory>
    </PreferenceScreen>
    

    只需为您要添加的每个新类别创建一个PreferenceCategory

    【讨论】:

      【解决方案2】:

      要详细说明T. Folsom 的答案,这是我的实现:

      res/layout/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:baselineAligned="false"
          android:gravity="center_vertical"
          android:minHeight="48dp"
          android:paddingRight="?android:attr/scrollbarSize" >
      
          <LinearLayout
              android:layout_width="@dimen/header_icon_width"
              android:layout_height="wrap_content"
              android:layout_marginLeft="6dip"
              android:layout_marginRight="6dip" >
      
              <ImageView
                  android:id="@+id/icon"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_gravity="center" />
          </LinearLayout>
      
          <RelativeLayout
              android:layout_width="0dip"
              android:layout_height="wrap_content"
              android:layout_marginBottom="6dip"
              android:layout_marginLeft="2dip"
              android:layout_marginRight="6dip"
              android:layout_marginTop="6dip"
              android:layout_weight="1" >
      
              <TextView
                  android:id="@+android:id/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="@+android:id/summary"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_below="@android:id/title"
                  android:ellipsize="end"
                  android:maxLines="2"
                  android:textAppearance="?android:attr/textAppearanceSmall" />
          </RelativeLayout>
      
      </LinearLayout>
      

      res/values/dimens.xml

      <resources>
      
          <dimen name="header_icon_width">28dp</dimen>
      
      </resources>
      

      在您的 PreferenceActivity 类中:

          @Override
      protected void onCreate(Bundle savedInstanceState) {
      
          if (savedInstanceState != null) {
              /*
               *  the headers must be restored before the super call in order
               *  to be ready for the call to setListAdapter() 
               */
              if (savedInstanceState.containsKey("headers")) {
                  setHeaders((ArrayList<Header>)savedInstanceState.getSerializable("headers"));
              }
          }
      
          // as suggest by https://stackoverflow.com/questions/15551673/android-headers-categories-in-preferenceactivity-with-preferencefragment
          if(onIsMultiPane()) getIntent().putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, PreferencesFragment.class.getName());
      
          super.onCreate(savedInstanceState);
      
          ...
      
      }
      
      @Override
      protected void onResume() {
          super.onResume();
      
          // https://stackoverflow.com/questions/15551673/android-headers-categories-in-preferenceactivity-with-preferencefragment
          // Select the displayed fragment in the headers (when using a tablet) :
          // This should be done by Android, it is a bug fix
          if(getHeaders() != null) {
      
              final String displayedFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
              if (displayedFragment != null) {
                  for (final Header header : getHeaders()) {
                      if (displayedFragment.equals(header.fragment)) {
                          switchToHeader(header);
                          break;
                      }
                  }
              }
          }
      
          ...
      
      }
      
      /**
       * Populate the activity with the top-level headers.
       */
      @Override
      public void onBuildHeaders(List<Header> target) {
          // we have to save the headers as the API call getHeaders() is hidden.
          setHeaders(target);
          loadHeadersFromResource(R.xml.settings_headers, target);
      }
      
      private List<Header> headers;
      
      private void setHeaders(List<Header> headers) {
          this.headers = headers;
      }
      
      private List<Header> getHeaders() {
          return headers;
      }
      
      @Override
      protected void onSaveInstanceState(Bundle outState) {
          outState.putSerializable("headers", (ArrayList<PreferenceActivity.Header>)headers);
          super.onSaveInstanceState(outState);
      }
      
      @Override
      public void setListAdapter(ListAdapter adapter) {
          if (adapter == null) {
              super.setListAdapter(null);
          } else {
              super.setListAdapter(new HeaderAdapter(this, getHeaders()));
          }
      }
      
      private static class HeaderAdapter extends ArrayAdapter<Header> {
          static final int HEADER_TYPE_CATEGORY = 0;
          static final int HEADER_TYPE_NORMAL = 1;
          private static final int HEADER_TYPE_COUNT = HEADER_TYPE_NORMAL + 1;
      
          private static class HeaderViewHolder {
              ImageView icon;
              TextView title;
              TextView summary;
          }
      
          private LayoutInflater mInflater;
      
          static int getHeaderType(Header header) {
              if (header.fragment == null && header.intent == null) {
                  return HEADER_TYPE_CATEGORY;
              } else {
                  return HEADER_TYPE_NORMAL;
              }
          }
      
          @Override
          public int getItemViewType(int position) {
              Header header = getItem(position);
              return getHeaderType(header);
          }
      
          @Override
          public boolean areAllItemsEnabled() {
              return false; // because of categories
          }
      
          @Override
          public boolean isEnabled(int position) {
              return getItemViewType(position) != HEADER_TYPE_CATEGORY;
          }
      
          @Override
          public int getViewTypeCount() {
              return HEADER_TYPE_COUNT;
          }
      
          @Override
          public boolean hasStableIds() {
              return true;
          }
      
          public HeaderAdapter(Context context, List<Header> objects) {
              super(context, 0, objects);
      
              mInflater = (LayoutInflater) context
                      .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      
          }
      
          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
              HeaderViewHolder holder;
              Header header = getItem(position);
              int headerType = getHeaderType(header);
              View view = null;
      
              if (convertView == null) {
                  holder = new HeaderViewHolder();
                  switch (headerType) {
                  case HEADER_TYPE_CATEGORY:
                      view = new TextView(getContext(), null,
                              android.R.attr.listSeparatorTextViewStyle);
                      holder.title = (TextView) view;
                      break;
      
                  case HEADER_TYPE_NORMAL:
                      view = mInflater.inflate(R.layout.preference_header_item,
                              parent, false);
                      holder.icon = (ImageView) view.findViewById(R.id.icon);
                      holder.title = (TextView) view
                              .findViewById(android.R.id.title);
                      holder.summary = (TextView) view
                              .findViewById(android.R.id.summary);
                      break;
                  }
                  view.setTag(holder);
              } else {
                  view = convertView;
                  holder = (HeaderViewHolder) view.getTag();
              }
      
              // All view fields must be updated every time, because the view may
              // be recycled
              switch (headerType) {
              case HEADER_TYPE_CATEGORY:
                  holder.title.setText(header.getTitle(getContext()
                          .getResources()));
                  break;
              case HEADER_TYPE_NORMAL:
                  holder.icon.setImageResource(header.iconRes);
                  holder.title.setText(header.getTitle(getContext()
                          .getResources()));
                  CharSequence summary = header.getSummary(getContext()
                          .getResources());
                  if (!TextUtils.isEmpty(summary)) {
                      holder.summary.setVisibility(View.VISIBLE);
                      holder.summary.setText(summary);
                  } else {
                      holder.summary.setVisibility(View.GONE);
                  }
                  break;
              }
      
              return view;
          }
      
      }
      

      有了所有这些代码,创建标题很简单:

      <preference-headers xmlns:android="http://schemas.android.com/apk/res/android" >
           <header android:title="atitle" />
      </preference-headers>
      

      希望这对某人有所帮助。我知道我需要一些时间才能正常工作。

      【讨论】:

        【解决方案3】:

        这是偏好类别示例,您可以使用偏好类别并设置相应的片段并实现此目的,如果我误解了您的情况,请告诉我。

        这里是示例布局

        <PreferenceCategory android:title="Heading1">
                <Preference 
                    android:title="title1"
                    android:summary="summary1"
                    android:key="keyName"/>
        
               <Preference 
                    android:title="title2"
                    android:summary="summary2"
                    android:key="keyName"/>
        </PreferenceCategory>
        
        <PreferenceCategory android:title="Heading2">
                <Preference 
                    android:title="title3"
                    android:summary="summary3"
                    android:key="keyName"/>
        </PreferenceCategory>
        

        【讨论】:

        • 这会在片段(属性窗格)内创建标题。问题是关于在标题部分创建标题(由loadHeadersFromResource 创建)。
        【解决方案4】:

        似乎最好的解决方案是创建三个不同的代码块——一个用于蜂窝前,一个用于蜂窝后,一个用于平板电脑。

        首选项标题的使用仅在平板电脑上有效,因此它们仅在平板电脑上保留。这里没有使用分组。

        post-Honeycomb 上的首选项标题有点没用,所以最好是在 PreferenceFragment 中使用典型的 PreferenceScreen。可以通过PreferenceCategory轻松组群。

        最后,对于之前的 Honeycomb,不使用 PrefrenceFragment 的弃用方式是唯一的方式。

        遗憾的是有很多代码重复,但 Leandros 的答案中提到的 UnifiedPreference 库有问题 - 它完全忽略了 PreferenceFragment,所以它没用(至少对我来说)。

        【讨论】:

        【解决方案5】:

        AOSP settings_headers.xml 实现:

        <preference-headers
                xmlns:android="http://schemas.android.com/apk/res/android">
        
        
            <!-- WIRELESS and NETWORKS -->
            <header android:title="@string/header_category_wireless_networks" />
        
            <!-- Wifi -->
            <header
                android:id="@+id/wifi_settings"
                android:fragment="com.android.settings.wifi.WifiSettings"
                android:title="@string/wifi_settings_title"
                android:icon="@drawable/ic_settings_wireless" />
        
            <!-- Bluetooth -->
            <header
                android:id="@+id/bluetooth_settings"
                android:fragment="com.android.settings.bluetooth.BluetoothSettings"
                android:title="@string/bluetooth_settings_title"
                android:icon="@drawable/ic_settings_bluetooth2" />
        
            <!-- Data Usage -->
            <header
                android:id="@+id/data_usage_settings"
                android:fragment="com.android.settings.DataUsageSummary"
                android:title="@string/data_usage_summary_title"
                android:icon="@drawable/ic_settings_data_usage" />
        
            <!-- Operator hook -->
            <header
                android:fragment="com.android.settings.WirelessSettings"
                android:id="@+id/operator_settings">
                <intent android:action="com.android.settings.OPERATOR_APPLICATION_SETTING" />
            </header>
        
            <!-- Other wireless and network controls -->
            <header
                android:id="@+id/wireless_settings"
                android:title="@string/radio_controls_title"
                android:breadCrumbTitle="@string/wireless_networks_settings_title"
                android:fragment="com.android.settings.WirelessSettings"
                android:icon="@drawable/empty_icon" />
        
           <!-- Ethernet -->
           <header
                android:id="@+id/ethernet_settings"
                android:title="@string/eth_radio_ctrl_title"
                android:icon="@drawable/ic_settings_ethernet"
                android:fragment="com.android.settings.ethernet.EthernetSettings"/>
        
            <!-- DEVICE -->
            <header android:title="@string/header_category_device" />
        
            <!-- Sound -->
            <header
                android:id="@+id/sound_settings"
                android:icon="@drawable/ic_settings_sound"
                android:fragment="com.android.settings.SoundSettings"
                android:title="@string/sound_settings" />
        
            <!-- Display -->
            <header
                android:id="@+id/display_settings"
                android:icon="@drawable/ic_settings_display"
                android:fragment="com.android.settings.DisplaySettings"
                android:title="@string/display_settings" />
        
            <!-- Storage -->
            <header
                android:id="@+id/storage_settings"
                android:fragment="com.android.settings.deviceinfo.Memory"
                android:icon="@drawable/ic_settings_storage"
                android:title="@string/storage_settings" />
        
            <!-- Battery -->
            <header
                android:id="@+id/battery_settings"
                android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"
                android:icon="@drawable/ic_settings_battery"
                android:title="@string/power_usage_summary_title" />
        
            <!-- Application Settings -->
            <header
                android:fragment="com.android.settings.applications.ManageApplications"
                android:icon="@drawable/ic_settings_applications"
                android:title="@string/applications_settings"
                android:id="@+id/application_settings" />
        
            <!-- TEMPORARY FACTORY STARTER WILL BE REMOVED WITH UPDATED SETTINGS -->
            <header
                android:icon="@drawable/ic_settings_applications"
                android:title="Factory"
                android:id="@+id/application_settings" >
                <intent android:action="android.intent.action.MAIN"
                        android:targetPackage="com.jamdeo.tv.sample.factory"
                        android:targetClass="com.jamdeo.tv.sample.factory.TvFactoryMainActivity" />
            </header>
        
            <!-- Manufacturer hook -->
            <header
                android:fragment="com.android.settings.WirelessSettings"
                android:id="@+id/manufacturer_settings">
                <intent android:action="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />
            </header>
        
        
            <!-- PERSONAL -->
            <header android:title="@string/header_category_personal" />
        
            <!-- Data Sync. The settings activity will ensure this is resolved to an
                 activity on the system image, otherwise it will remove this
                 preference. -->
            <header
                android:fragment="com.android.settings.accounts.ManageAccountsSettings"
                android:icon="@drawable/ic_settings_sync"
                android:title="@string/sync_settings"
                android:id="@+id/sync_settings" />
        
            <!-- Location -->
            <header
                android:fragment="com.android.settings.LocationSettings"
                android:icon="@drawable/ic_settings_location"
                android:title="@string/location_settings_title"
                android:id="@+id/location_settings" />
        
            <!-- Security -->
            <header
                android:fragment="com.android.settings.SecuritySettings"
                android:icon="@drawable/ic_settings_security"
                android:title="@string/security_settings_title"
                android:id="@+id/security_settings" />
        
            <!-- Language -->
            <header
                android:id="@+id/language_settings"
                android:fragment="com.android.settings.inputmethod.InputMethodAndLanguageSettings"
                android:icon="@drawable/ic_settings_language"
                android:title="@string/language_settings" />
        
            <!-- Backup and reset -->
            <header
                android:fragment="com.android.settings.PrivacySettings"
                android:icon="@drawable/ic_settings_backup"
                android:title="@string/privacy_settings"
                android:id="@+id/privacy_settings" />
        
        
            <!-- SYSTEM -->
            <header android:title="@string/header_category_system" />
        
            <!-- Dock -->
            <header
                android:id="@+id/dock_settings"
                android:fragment="com.android.settings.DockSettings"
                android:icon="@drawable/ic_settings_dock"
                android:title="@string/dock_settings" />
        
            <!-- Date & Time -->
            <header
                android:id="@+id/date_time_settings"
                android:fragment="com.android.settings.DateTimeSettings"
                android:icon="@drawable/ic_settings_date_time"
                android:title="@string/date_and_time_settings_title" />
        
            <!-- Accessibility feedback -->
            <header
                android:id="@+id/accessibility_settings"
                android:fragment="com.android.settings.AccessibilitySettings"
                android:icon="@drawable/ic_settings_accessibility"
                android:title="@string/accessibility_settings" />
        
            <!-- Development -->
            <header
                android:id="@+id/development_settings"
                android:fragment="com.android.settings.DevelopmentSettings"
                android:icon="@drawable/ic_settings_development"
                android:title="@string/development_settings_title" />
        
            <!-- About Device -->
            <header
                android:id="@+id/about_settings"
                android:fragment="com.android.settings.DeviceInfoSettings"
                android:icon="@drawable/ic_settings_about"
                android:title="@string/about_settings" />
        
        </preference-headers>
        

        只需将&lt;header&gt;android:title 属性一起使用。

        【讨论】:

        • 这不起作用 - 只是创建了另一个不执行任何操作的标题并且不显示任何组划分。我认为在您的示例中缺少一些程序化的东西。
        • 我查看了设置活动的源代码,您需要做很多事情才能获得相同的结果。我最终代理了设置的适配器,并为我的标题返回了不同的视图。
        • 正如@James 所说,它不起作用。不要浪费你的时间。
        • 我认为最好的解决方案是不使用标题。仅适用于平板电脑。
        猜你喜欢
        • 2011-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多