Destek (v21) Araç Çubuğu ile Tercih Ekranı Oluşturma


116

Tercih ekranındaki destek kitaplığındaki yeni Materyal Tasarımı araç çubuğunu kullanırken sorun yaşıyordum.

Aşağıdaki gibi bir settings.xml dosyam var:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/AddingItems"
        android:key="pref_key_storage_settings">

        <ListPreference
            android:key="pref_key_new_items"
            android:title="@string/LocationOfNewItems"
            android:summary="@string/LocationOfNewItemsSummary"
            android:entries="@array/new_items_entry"
            android:entryValues="@array/new_item_entry_value"
            android:defaultValue="1"/>

    </PreferenceCategory>
</PreferenceScreen>

Dizeler başka bir yerde tanımlanmıştır.


stackoverflow.com/a/27455363/2247612 Bu yanıtın Destek Kitaplığı için mükemmel bir çözümü var
harishannam

Yanıtlar:


110

Lütfen GitHub Repo'yu bulun: Burada


Partiye biraz geç kaldım, ancak kullanmaya devam etmek için kullandığım çözüm bu PreferenceActivity:

settings_toolbar.xml :

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?attr/actionBarSize"
    app:navigationContentDescription="@string/abc_action_bar_up_description"
    android:background="?attr/colorPrimary"
    app:navigationIcon="?attr/homeAsUpIndicator"
    app:title="@string/action_settings"
    />

SettingsActivity.java :

public class SettingsActivity extends PreferenceActivity {

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
        Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
        root.addView(bar, 0); // insert at top
        bar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

}

Result :

misal


GÜNCELLEME (Gingerbread Uyumluluğu):

Yorumlara göre, Gingerbread Cihazları bu satırda NullPointerException döndürüyor:

LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();

Düzeltme:

SettingsActivity.java :

public class SettingsActivity extends PreferenceActivity {

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        Toolbar bar;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            LinearLayout root = (LinearLayout) findViewById(android.R.id.list).getParent().getParent().getParent();
            bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
            root.addView(bar, 0); // insert at top
        } else {
            ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
            ListView content = (ListView) root.getChildAt(0);

            root.removeAllViews();

            bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
            

            int height;
            TypedValue tv = new TypedValue();
            if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
                height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
            }else{
                height = bar.getHeight();
            }

            content.setPadding(0, height, 0, 0);

            root.addView(content);
            root.addView(bar);
        }

        bar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
}

Yukarıdakilerle ilgili herhangi bir sorun varsa bana bildirin!


GÜNCELLEME 2: RENKLENDİRME ÇÖZÜMÜ

Birçok geliştirme notunda belirtildiği PreferenceActivitygibi, öğelerin renklendirilmesini desteklemez, ancak birkaç dahili sınıfı kullanarak bunu başarabilirsiniz. Yani bu sınıflar kaldırılana kadar. (AppCompat support-v7 v21.0.3 kullanılarak çalışır).

Aşağıdaki içe aktarımları ekleyin:

import android.support.v7.internal.widget.TintCheckBox;
import android.support.v7.internal.widget.TintCheckedTextView;
import android.support.v7.internal.widget.TintEditText;
import android.support.v7.internal.widget.TintRadioButton;
import android.support.v7.internal.widget.TintSpinner;

Ardından onCreateViewyöntemi geçersiz kılın :

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    // Allow super to try and create a view first
    final View result = super.onCreateView(name, context, attrs);
    if (result != null) {
        return result;
    }

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        // If we're running pre-L, we need to 'inject' our tint aware Views in place of the
        // standard framework versions
        switch (name) {
            case "EditText":
                return new TintEditText(this, attrs);
            case "Spinner":
                return new TintSpinner(this, attrs);
            case "CheckBox":
                return new TintCheckBox(this, attrs);
            case "RadioButton":
                return new TintRadioButton(this, attrs);
            case "CheckedTextView":
                return new TintCheckedTextView(this, attrs);
        }
    }

    return null;
}

Result:

örnek 2


AppCompat 22.1

AppCompat 22.1, yeni renklendirilmiş öğeler sundu, bu da son güncellemeyle aynı etkiyi elde etmek için dahili sınıfları kullanmaya artık gerek olmadığı anlamına geliyor. Bunun yerine şunu izleyin (hala geçersiz kılınmaktadır onCreateView):

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    // Allow super to try and create a view first
    final View result = super.onCreateView(name, context, attrs);
    if (result != null) {
        return result;
    }

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        // If we're running pre-L, we need to 'inject' our tint aware Views in place of the
        // standard framework versions
        switch (name) {
            case "EditText":
                return new AppCompatEditText(this, attrs);
            case "Spinner":
                return new AppCompatSpinner(this, attrs);
            case "CheckBox":
                return new AppCompatCheckBox(this, attrs);
            case "RadioButton":
                return new AppCompatRadioButton(this, attrs);
            case "CheckedTextView":
                return new AppCompatCheckedTextView(this, attrs);
        }
    }

    return null;
}

NESTED TERCİH EKRANLARI

Araç Çubuğunu iç içe yerleştirme konusunda birçok kişi sorun yaşıyor <PreferenceScreen />ancak ben bir çözüm buldum !! - Çok fazla deneme yanılmadan sonra!

Aşağıdakileri ekleyin SettingsActivity:

@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    super.onPreferenceTreeClick(preferenceScreen, preference);

    // If the user has clicked on a preference screen, set up the screen
    if (preference instanceof PreferenceScreen) {
        setUpNestedScreen((PreferenceScreen) preference);
    }

    return false;
}

public void setUpNestedScreen(PreferenceScreen preferenceScreen) {
    final Dialog dialog = preferenceScreen.getDialog();

    Toolbar bar;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        LinearLayout root = (LinearLayout) dialog.findViewById(android.R.id.list).getParent();
        bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
        root.addView(bar, 0); // insert at top
    } else {
        ViewGroup root = (ViewGroup) dialog.findViewById(android.R.id.content);
        ListView content = (ListView) root.getChildAt(0);

        root.removeAllViews();

        bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);

        int height;
        TypedValue tv = new TypedValue();
        if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
            height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
        }else{
            height = bar.getHeight();
        }

        content.setPadding(0, height, 0, 0);

        root.addView(content);
        root.addView(bar);
    }

    bar.setTitle(preferenceScreen.getTitle());

    bar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            dialog.dismiss();
        }
    });
}

Bunun PreferenceScreenbu kadar acı verici olmasının nedeni, bir sarmalayıcı iletişim kutusu olarak temel almalarıdır, bu nedenle, araç çubuğunu eklemek için iletişim kutusu düzenini yakalamamız gerekir.


Araç Çubuğu Gölgesi

Tasarımın içe Toolbaraktarılması, v21 öncesi cihazlarda yükseklik ve gölgelendirmeye izin vermez, bu nedenle, sizde yükseklik olmasını Toolbaristiyorsanız, aşağıdakilere sarmanız gerekir AppBarLayout:

settings_toolbar.xml :

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

   <android.support.v7.widget.Toolbar
       .../>

</android.support.design.widget.AppBarLayout>

build.gradleDosyaya bir bağımlılık olarak Tasarım Desteği kitaplığını eklemeyi unutmayın :

compile 'com.android.support:support-v4:22.2.0'
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:design:22.2.0'

Android 6.0

Bildirilen çakışan sorunu araştırdım ve sorunu yeniden oluşturamıyorum.

Yukarıdaki gibi kullanımda olan tam kod aşağıdakileri üretir:

görüntü açıklamasını buraya girin

Eksik bir şey varsa lütfen bu depo aracılığıyla bana bildirin, ben de araştıracağım.


gingebread'de nullpointer istisnası verir, kök null .. herhangi bir çözüm?
andQlimax

çözümünüz harika çalışıyor. ancak bu yaklaşımla ilgili bir sorun var, ActionBarActivity'yi genişletmeden infact (dokümantasyondan) malzeme temasını <5.0 üzerinde almak için zorunludur, colorAccent (sadece bir örnek vermek için) <5.0 cihazlarındaki onay kutularına uygulanmaz. Bu gerçek bir acı gibi görünüyor ... Belki tercih etkinliğini kaldırmam ve bir tercih ekranını simüle etmek için doğrusal bir düzen kullanmam gerekiyor, aksi takdirde api seviyesi 8'den 21'e kadar olan cihazlarda materyal temasını kullanmanın bir yolunu görmüyorum. "only"> 11 :(
veQlimax

1
@andQlimax Renklendirme sorunu için bir çözümle cevabımı güncelledim
David Passmore

3
@DavidPassmore benim için tercih listesi araç çubuğunda örtüşüyor
Shashank Srivastava

1
@ShashankSrivastava Android 6 kullanıyorsanız bunu yansıtıyor, bunun için bir çözüm üzerinde çalışıyorum. Güncelleme için teşekkürler.
David Passmore

107

A PreferenceFragmentalternatifi olarak kullanabilirsiniz PreferenceActivity. İşte sarma Activityörneği:

public class MyPreferenceActivity extends ActionBarActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pref_with_actionbar);

        android.support.v7.widget.Toolbar toolbar = (android.support.v7.widget.Toolbar) findViewById(uk.japplications.jcommon.R.id.toolbar);
        setSupportActionBar(toolbar);

        getFragmentManager().beginTransaction().replace(R.id.content_frame, new MyPreferenceFragment()).commit();
    }
}

Ve işte düzen dosyası (pref_with_actionbar):

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_height="@dimen/action_bar_height"
        android:layout_width="match_parent"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:theme="@style/ToolbarTheme.Base"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_below="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

Ve son olarak PreferenceFragment:

public static class MyPreferenceFragment extends PreferenceFragment{
    @Override
    public void onCreate(final Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);
    }
}

Umarım bu birine yardımcı olur.


39
Bu yaklaşımı denedim. Sorun, araç çubuğunu alt tercih ekranlarında görüntülememesidir.
Madhur Ahuja

2
Sanırım kök tercih XML'sine yerleştirilmiş PreferenceScreen'den bahsediyor.
Lucas S.

5
Yaklaşımı beğendim, ancak hedef api API 11'den
düşükse

12
İkisiyle de işe yaramayacak. Pratik olarak, malzeme olarak tasarlanmış, araç çubuğuna sahip, iç içe geçmiş tercih ekranları oluşturmanın herhangi bir yolu yok gibi görünüyor. ActionBarActivityAraç çubuğunu ve ilgili işlevleri almak için bir kullanırsanız onBuildHeaders(), etkinlikte geçersiz kılma olmayacak ve gerçek bir tercih desteği olmayacaktır. Eskisini kullanırsanız, PreferenceActivityaraç çubuğuna ve ilgili işlevlere sahip değilsiniz (evet, bir Toolbarve düzenine sahip olabilirsiniz ancak arayamazsınız setSupportActionBar(). Bu nedenle, tercih başlıkları veya iç içe geçmiş tercih ekranlarında takılıp kaldık.
Gábor

1
Gabor'un yorumuna katılıyorum. Bu çözüm genel olarak çalışmıyor. Araç Çubuğunu taklit eden (ActionBar yok, ancak kimin umurunda) ve ayrıca AppCompatDelegate ile yerleşik olarak yayınlanan yeni destek kitaplığı içeren daha iyi bir tane var.
Eugene Wechsler

48

Tamamen yeni güncelleme.

Biraz deney yaparak, iç içe geçmiş tercih ekranları için çalışan AppCompat 22.1+ çözümünü bulmuş gibiyim.

İlk olarak, birçok cevapta belirtildiği gibi (burada bir tane dahil), yenisini kullanmanız gerekecek AppCompatDelegate. Ya AppCompatPreferenceActivity.javadestek demolarındaki dosyayı kullanın ( https://android.googlesource.com/platform/development/+/58bf5b99e6132332afb8b44b4c8cedf5756ad464/samples/Support7Demos/src/com/example/android/supportv7/app/ctivity.javaPreference ) ondan veya ilgili işlevleri kendinize kopyalayın PreferenceActivity. Burada ilk yaklaşımı göstereceğim:

public class SettingsActivity extends AppCompatPreferenceActivity {

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

    setContentView(R.layout.settings_page);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    ActionBar bar = getSupportActionBar();
    bar.setHomeButtonEnabled(true);
    bar.setDisplayHomeAsUpEnabled(true);
    bar.setDisplayShowTitleEnabled(true);
    bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
    bar.setTitle(...);
  }

  @Override
  protected boolean isValidFragment(String fragmentName) {
    return SettingsFragment.class.getName().equals(fragmentName);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case android.R.id.home:
        onBackPressed();
        break;
    }
    return super.onOptionsItemSelected(item);
  }
}

Eşlik eden düzen oldukça basit ve olağandır ( layout/settings_page.xml):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="0dp"
    android:orientation="vertical"
    android:padding="0dp">
  <android.support.v7.widget.Toolbar
      android:id="@+id/toolbar"
      android:layout_width="match_parent"
      android:layout_height="?attr/actionBarSize"
      android:background="?attr/colorPrimary"
      android:elevation="4dp"
      android:theme="@style/..."/>
  <ListView
      android:id="@id/android:list"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>
</LinearLayout>

Tercihlerin kendileri her zamanki gibi tanımlanır ( xml/settings.xml):

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
  <header
      android:fragment="com.example.SettingsFragment"
      android:summary="@string/..."
      android:title="@string/...">
    <extra
        android:name="page"
        android:value="page1"/>
  </header>
  <header
      android:fragment="com.example.SettingsFragment"
      android:summary="@string/..."
      android:title="@string/...">
    <extra
        android:name="page"
        android:value="page2"/>
  </header>
  ...
</preference-headers>

Bu noktaya kadar internetteki çözümlerde gerçek bir fark yok. Aslında, iç içe geçmiş ekranlarınız olmasa bile, başlığınız yoksa, sadece tek bir ekranınız olsa bile bunu kullanabilirsiniz.

Daha PreferenceFragmentderin sayfaların tümü için extra, başlıklardaki parametrelerle farklılaştırılmış bir ortak kullanırız . Her sayfanın PreferenceScreeniçinde ortak bir iç kısmı olan ayrı bir XML'si olacaktır ( xml/settings_page1.xmlve diğerleri). Parça, araç çubuğu dahil olmak üzere etkinlikle aynı düzeni kullanır.

public class SettingsFragment extends PreferenceFragment {

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getActivity().setTheme(R.style...);

    if (getArguments() != null) {
      String page = getArguments().getString("page");
      if (page != null)
        switch (page) {
          case "page1":
            addPreferencesFromResource(R.xml.settings_page1);
            break;
          case "page2":
            addPreferencesFromResource(R.xml.settings_page2);
            break;
          ...
        }
    }
  }

  @Override
  public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View layout = inflater.inflate(R.layout.settings_page, container, false);
    if (layout != null) {
      AppCompatPreferenceActivity activity = (AppCompatPreferenceActivity) getActivity();
      Toolbar toolbar = (Toolbar) layout.findViewById(R.id.toolbar);
      activity.setSupportActionBar(toolbar);

      ActionBar bar = activity.getSupportActionBar();
      bar.setHomeButtonEnabled(true);
      bar.setDisplayHomeAsUpEnabled(true);
      bar.setDisplayShowTitleEnabled(true);
      bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
      bar.setTitle(getPreferenceScreen().getTitle());
    }
    return layout;
  }

  @Override
  public void onResume() {
    super.onResume();

    if (getView() != null) {
      View frame = (View) getView().getParent();
      if (frame != null)
        frame.setPadding(0, 0, 0, 0);
    }
  }
}

Son olarak, bunun gerçekte nasıl çalıştığına dair hızlı bir özet. Yeni AppCompatDelegate, sadece AppCompat'taki aktivitelerden genişleyenleri değil, AppCompat özellikleriyle herhangi bir aktiviteyi kullanmamıza izin veriyor. Bu, eskiyi yenisine PreferenceActivityçevirebileceğimiz ve araç çubuğunu her zamanki gibi ekleyebileceğimiz anlamına gelir . Bu noktadan sonra, mevcut belgelerden herhangi bir sapma olmaksızın, tercih ekranları ve başlıklar ile ilgili eski çözümlere bağlı kalabiliriz. Sadece bir önemli nokta var: Hatalara onCreate()yol açacağı için aktivitede kullanmayın . onBuildHeaders()Araç çubuğunu eklemek gibi tüm işlemler için kullanın .

Tek gerçek fark, iç içe geçmiş ekranlarla çalışmasını sağlayan şey, parçalarda aynı yaklaşımı kullanabilmenizdir. Onların kullanabilirsiniz onCreateView()aktivitede olduğu gibi araç çubuğu aynı şekilde ekleyerek, sistemin bir yerine kendi düzenini şişirme, aynı şekilde.


2
Ne harika bir küçük çözüm! Materyal araç çubuğunu soyundan gelen PreferenceScreen üzerinde gösterecek bulduğum tek çözüm bu. Aferin efendim.
Dize

Yukarı simgesi için appcompat kitaplığındaki kaynağı kullanıyorum:R.drawable.abc_ic_ab_back_mtrl_am_alpha
Ridcully

Bu çözümle, Araç Çubuğunun içerikle kaydırılacağını düşünüyorum, doğru mu? Çünkü dahili ListView'deki bir öğedir.
tasomaniac

Bu güncellenmiş, yeni çözümle değil. Bu beklendiği gibi çalışıyor.
Gábor

Garip bir şekilde, bu çözüm PreferenceFragmentCompatyerine a'yı tanımıyor gibi görünüyor PreferenceFragment. Bir preference-headerile xmlns:app="http://schemas.android.com/apk/res-auto" ve sonra app:fragmentyerine kurmak, android:fragmentherhangi bir yeni tercih ekranı yüklemez. Yani geriye dönük uyumlulukla ilgili sorunlar mı yaşıyorsunuz ... öneriler?
fattire

18

PreferenceHeaders'ı kullanmak istiyorsanız, aşağıdaki yaklaşımı kullanabilirsiniz:

import android.support.v7.widget.Toolbar;

public class MyPreferenceActivity extends PreferenceActivity

   Toolbar mToolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
        LinearLayout content = (LinearLayout) root.getChildAt(0);
        LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.activity_settings, null);

        root.removeAllViews();
        toolbarContainer.addView(content);
        root.addView(toolbarContainer);

        mToolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar);
    }

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

    // Other methods

}

düzen / activity_settings.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_height="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:theme="@style/AppTheme"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

</LinearLayout>

Burada tercih ettiğiniz düzeni kullanabilirsiniz, sadece bunu Java kodunda da ayarladığınızdan emin olun.

Ve son olarak, başlıkları olan dosyanız (xml / pref_headers.xml)

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

    <header
        android:fragment="com.example.FirstFragment"
        android:title="@string/pref_header_first" />
    <header
        android:fragment="com.example.SecondFragment"
        android:title="@string/pref_header_second" />

</preference-headers>

root.addView (araç); neden? root.addView (toolbarContainer);
Crossle Şarkı

Hata, yeniden adlandırırken bir değişkeni gözden kaçırdım, düzeltildi.
Sven Dubbeld

1
Mükemmel cevap. Buradaki anahtar android.R.id.content, bunun yerine tercih listesinin kendisi için bir ListViewile android.R.id.listgeçirdiğimizi (ve parçasız, başlıksız, yol kullanıyorsanız hala yapıyoruz) göz önünde bulundurmaktır .
davidcsb

2
Bence Android'in kodunu kontrol etmek, neye ihtiyacı olduğunu görmek için, hirerchy görünümleriyle uğraşmak yerine (sahip olduğu görünümleri kaldır / ekle) daha iyi. Bence böylesi daha güvenli. "Preference_list_content" dosyasına bakmanızı öneririm.
android geliştiricisi

2
Bu, bu konudaki en iyi cevap. Bu makalenin yazarı, onu kullandığım tam referans uygulamasına genişletti. Aslında bu, uygulamanızda gelişmiş tercihleri ​​desteklemek için çalışan tek çözümdür.
Eugene Wechsler

17

Android Destek Kitaplığı 22.1.0'ın ve yeni AppCompatDelegate'in yayınlanmasıyla, burada geriye dönük uyumluluğa sahip malzeme destekli PreferenceActivity uygulamasının güzel bir örneğini bulabilirsiniz.

Güncelle İç içe ekranlarda da çalışır.

https://android.googlesource.com/platform/development/+/marshmallow-mr3-release/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java


1
Oh, bu harika bir haber! Bu nedenle, bu yeni perspektifte "genişletme PreferenceActivity" temelli çözümlerin "ActionBarActivity'yi genişletme" seçeneğinden daha iyi olduğu görülüyor.
Eugene Wechsler

1
@EugeneWechsler Evet, gerçekten, ActionBarActivity artık kullanımdan kaldırıldı.
MrBrightside

Bu çözüm, iç içe geçmiş ekranlarda da çalışıyor mu? Daha iyi bir örnek var mı?
Tomas

@Tomas Henüz denemedim, ancak iç içe geçmiş ekranlarda da çalışması gerekiyor. Sizin için çalışıyorsa, lütfen bize bildirin.
MrBrightside

Çok teşekkürler ! Benim için Galaxy Nexus (4.3) ve iç içe ekranlı emülatörde (lolipop) çalışıyor.
Tim Autin

6

Yukarıdaki yanıtlar ayrıntılı görünmekle birlikte, API 7 desteği ile Araç Çubuğu'nu kullanmak için hızlı bir düzeltme çözümü istiyorsanız ve bu süre PreferenceActivityboyunca bu projeden yardım aldım.

https://github.com/AndroidDeveloperLB/ActionBarPreferenceActivity

activity_settings.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/app_theme_light"
    app:popupTheme="@style/Theme.AppCompat.Light"
    app:theme="@style/Theme.AppCompat" />

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/padding_medium" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

SettingsActivity.java

public class SettingsActivity extends PreferenceActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_settings);

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

    addPreferencesFromResource(R.xml.preferences);

    toolbar.setClickable(true);
    toolbar.setNavigationIcon(getResIdFromAttribute(this, R.attr.homeAsUpIndicator));
    toolbar.setTitle(R.string.menu_settings);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            finish();
        }
    });

}

private static int getResIdFromAttribute(final Activity activity, final int attr) {
    if (attr == 0) {
        return 0;
    }
    final TypedValue typedvalueattr = new TypedValue();
    activity.getTheme().resolveAttribute(attr, typedvalueattr, true);
    return typedvalueattr.resourceId;
}
}

6

Ben de v7 destek araç çubuğunu ( API 25 ) AppCompatPreferenceActivity'ye (bir SettingsActivity eklerken AndroidStudio tarafından otomatik olarak oluşturulan) eklemek için bir çözüm arıyordum . Birkaç çözümü okuduktan ve her birini denedikten sonra, oluşturulan PreferenceFragment örneklerini bir araç çubuğuyla da görüntülemek için uğraştım.

İşe yarayan modifiye bir çözüm " Gabor " dan geldi.

Karşılaştığım uyarılardan biri, 'onBuildHeaders'ın yalnızca bir kez ateşlenmesiydi. Bir cihazı (bir telefon gibi) yana çevirirseniz, görünüm yeniden oluşturulur ve PreferenceActivity yeniden araç çubuğu olmadan bırakılır, ancak PreferenceFragments kendi aygıtlarını korur.

Yön değiştiğinde araç çubuğunu yeniden oluşturmak için çalışırken, 'setContentView' öğesini çağırmak için 'onPostCreate' kullanmayı denedim, PreferenceFragments daha sonra boş olarak gösterilecek.

Bu konu hakkında okuyabildiğim hemen hemen her ipucu ve cevapla ortaya çıkardığım kaldıraçlar. Umarım diğerleri de yararlı bulur.

Java ile başlayacağız

İlk (oluşturulur) içinde AppCompatPreferenceActivity.java Öyle gibi 'setSupportActionBar' modifiye:

public void setSupportActionBar(@Nullable Toolbar toolbar) {
    getDelegate().setSupportActionBar(toolbar);
    ActionBar bar = getDelegate().getSupportActionBar();
    bar.setHomeButtonEnabled(true);
    bar.setDisplayHomeAsUpEnabled(true);
}

İkinci olarak , AppCompatPreferenceFragment.java adında yeni bir sınıf oluşturdum (bu şekilde kalmasa da şu anda kullanılmayan bir addır!):

abstract class AppCompatPreferenceFragment extends PreferenceFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_settings, container, false);
        if (view != null) {
            Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar_settings);
            ((AppCompatPreferenceActivity) getActivity()).setSupportActionBar(toolbar);
        }
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        View frame = (View) getView().getParent();
        if (frame != null) frame.setPadding(0, 0, 0, 0);
    }
}

Bu, Gabor'un yanıtının işe yarayan kısmı.

Son olarak , tutarlılık sağlamak için SettingsActivity.java'da bazı değişiklikler yapmamız gerekiyor :

public class SettingsActivity extends AppCompatPreferenceActivity {

    boolean mAttachedFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mAttachedFragment = false;
        super.onCreate(savedInstanceState);
    }

    @Override
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }

    @Override
    public void onAttachFragment(Fragment fragment) {
        mAttachedFragment = true;
        super.onAttachFragment(fragment);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        //if we didn't attach a fragment, go ahead and apply the layout
        if (!mAttachedFragment) {
            setContentView(R.layout.activity_settings);
            setSupportActionBar((Toolbar)findViewById(R.id.toolbar_settings));
        }
    }

    /**
     * This fragment shows general preferences only. It is used when the
     * activity is showing a two-pane settings UI.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class GeneralPreferenceFragment extends AppCompatPreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            addPreferencesFromResource(R.xml.pref_general);
            setHasOptionsMenu(true);

            bindPreferenceSummaryToValue(findPreference("example_text"));
            bindPreferenceSummaryToValue(findPreference("example_list"));
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
            if (id == android.R.id.home) {
                startActivity(new Intent(getActivity(), SettingsActivity.class));
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
}

Kısalık olması için bazı kodlar etkinlik dışında bırakılmıştır. Buradaki temel bileşenler ' onAttachedFragment ', ' onPostCreate ' ve ' GeneralPreferenceFragment ' artık PreferenceFragment yerine özel ' AppCompatPreferenceFragment'i genişletiyor .

Kod Özeti : Bir parça varsa, parça yeni düzeni enjekte eder ve değiştirilmiş 'setSupportActionBar' işlevini çağırır. Parça yoksa, SettingsActivity yeni düzeni 'onPostCreate'e enjekte eder.

Şimdi XML'e geçelim (çok basit):

activity_settings.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        layout="@layout/app_bar_settings"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

app_bar_settings.xml :

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/content_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".SettingsActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.NoActionBar.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar_settings"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.NoActionBar.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_settings" />

</android.support.design.widget.CoordinatorLayout>

content_settings.xml :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".SettingsActivity"
    tools:showIn="@layout/app_bar_settings">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

Son Sonuç :

SettingsActivity

GeneralPreferenceFragment


Umut verici görünüyor ama benim için çalışmıyor. imgur.com/lSSVCIo (Pixel C emülatörü).
Thomas Vos


5

AppCompatPreferenceActivitySupport v7 örneklerinden kullanılan yeni (muhtemelen daha temiz) bir çözümüm var . Bu kod elimde olduğunda, bir araç çubuğu içeren kendi düzenimi oluşturdum:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:fitsSystemWindows="true" tools:context="edu.adelphi.Adelphi.ui.activity.MainActivity">

    <android.support.design.widget.AppBarLayout android:id="@+id/appbar"
        android:layout_width="match_parent" android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
            android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay"/>

    </android.support.design.widget.AppBarLayout>

    <FrameLayout android:id="@+id/content"
        android:layout_width="match_parent" android:layout_height="match_parent"/>

</android.support.design.widget.CoordinatorLayout>

Ardından, yeni bir düzenimi oluşturmak için AppCompatPreferenceActivitydeğiştirdim setContentViewve sağlanan düzeni şunun içine yerleştirdim FrameLayout:

@Override
public void setContentView(@LayoutRes int layoutResID) {
    View view = getLayoutInflater().inflate(R.layout.toolbar, null);
    FrameLayout content = (FrameLayout) view.findViewById(R.id.content);
    getLayoutInflater().inflate(layoutResID, content, true);
    setContentView(view);
}

Ardından , araç çubuğundaki menü öğelerini AppCompatPreferenceActivityaramama setSupportActionBar((Toolbar) findViewById(R.id.toolbar))ve şişirmeme izin vererek sadece genişletiyorum . Tüm bunlar bir PreferenceActivity.


5

Herhangi bir yerleşik düzeni bozmadan burada basit ve temiz tutalım

import android.support.design.widget.AppBarLayout;
import android.support.v4.app.NavUtils;
import android.support.v7.widget.Toolbar;

private void setupActionBar() {
    Toolbar toolbar = new Toolbar(this);

    AppBarLayout appBarLayout = new AppBarLayout(this);
    appBarLayout.addView(toolbar);

    final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
    final ViewGroup window = (ViewGroup) root.getChildAt(0);
    window.addView(appBarLayout, 0);

    setSupportActionBar(toolbar);

    // Show the Up button in the action bar.
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
    });
}

Benim için işe yaramadı, root.getChildAt(0);döner null.
Eido95

4

Bunun üzerinde çalışırken bu basit çözümü buldum. Öncelikle ayarlar etkinliği için bir düzen oluşturmamız gerekiyor.

activity_settings.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.my.package">

    <android.support.v7.widget.Toolbar
        android:id="@+id/tool_bar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:elevation="@dimen/appbar_elevation"
        app:navigationIcon="?attr/homeAsUpIndicator"
        app:navigationContentDescription="@string/abc_action_bar_up_description"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <ListView
        android:id="@android:id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tool_bar" />

</RelativeLayout>

İle bir liste görünümü eklediğinizden emin olun android:id="@android:id/list", aksi takdirdeNullPointerException

Sonraki adım, onCreateayarlar etkinliğinize (Geçersiz kıl) yöntemi eklemektir .

Settings.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_settings);
    Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);
    toolbar.setTitle(R.string.action_settings);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            finish();
        }
    });
}

İçe aktardığınızdan emin olun android.suppoer.v7.widget.Toolbar. Bu, 16'nın üzerindeki tüm API'lerde (Jelly Bean ve üstü) hemen hemen çalışmalıdır.


1

James Cross'un işaretli çözümüne devam etmek istiyorum, çünkü bundan sonra SettingsActivity'yi de kapatmamak için yalnızca aktif iç içe ekranı (PreferenceFragment) kapatma sorunu var.

Aslında tüm iç içe geçmiş ekranlarda çalışıyor (bu yüzden başarılı olmadan denediğim Gábor çözümünü anlamıyorum, iyi çalışıyor ama birden fazla araç çubuğu karmaşası var) çünkü kullanıcı bir alt tercih ekranına tıkladığında <FrameLayout android:id="@+id/content_frame" .../>her zaman etkin ve görünür kalan Araç Çubuğu değil , yalnızca parça değiştirilir (bakın ), ancak her parçayı uygun şekilde kapatmak için özel bir davranış uygulanmalıdır.

Aşağıdaki yöntemleri SettingsActivitygenişleten ana sınıfta ActionBarActivityuygulanmalıdır. Gizli setupActionBar()mesajınınonCreate()

private void setupActionBar() {
    Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
    //Toolbar will now take on default Action Bar characteristics
    setSupportActionBar(toolbar);
    getSupportActionBar().setHomeButtonEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case android.R.id.home:
        onBackPressed();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0) {
        getFragmentManager().popBackStackImmediate();
        //If the last fragment was removed then reset the title of main
        // fragment (if so the previous popBackStack made entries = 0).
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            getSupportActionBar()
                .setTitle(R.string.action_settings_title);
        }
    } else {
        super.onBackPressed();
    }
}

For başlığı seçilen iç içe ekranın size Toolbar referans almak gerekir ve birlikte uygun başlığı ayarlamak toolbar.setTitle(R.string.pref_title_general);(örneğin).

Orada gerek uygulamak için getSupportActionBar()parçanın yalnızca görünüm değil Çubuğu, taahhüt her fırsatta değiştirilir beri tüm PreferenceFragment içinde;

Orada gerek (Gabor cevabını bakın) her preference.xml eklemek sahte ToolbarPreference sınıf oluşturmak için.


1

Burada, hem tercihlere hem de diyaloglara renk tonu ekleyen, bir eylem çubuğu ekleyen ve API 7'nin tüm sürümlerini destekleyen AOSP koduna dayalı olarak yaptığım bir kitaplık var:

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary


Koda bakıldığında, iç içe geçmiş tercihler için işe yaramıyor ...?
Tim Rae

@TimRae Neden bahsettiğini test ettiğimden emin değilim. Lütfen ne demek istediğinizi açıklayın. Tam olarak kullanmaya çalıştığınız senaryo nedir?
android geliştiricisi

Bir olduğunda PreferenceScreeniç a PreferenceScreengibi bu
Tim Rae

Ben asla böyle bir şey kullanmadım. belgeleri okurken: developer.android.com/reference/android/preference/… , ekranlar arasında geçiş yapmanın yardımcı olabileceğini görüyorum. Ben de eklemem gerektiğini mi söylüyorsun? Kontrol edeceğim . Teşekkür ederim. Ayrıca, bir dahaki sefere böyle bir şey için lütfen Github'ı kullanın (istekler ve sorunlar).
android geliştiricisi

Evet, tek bir ekran için çok fazla tercihiniz olduğunda kullanışlıdır ... Bu ileti dizisinde, insanların iç içe geçmiş ekranlardan bahsettiği birçok yer var, bu yüzden burada yorum yapmak için uygun bir yer olduğunu düşünüyorum
Tim Rae

1

Eh, bu bugün hala benim için bir sorun (18 Kasım 2015). Bu konudaki tüm çözümleri denedim ama çözemediğim iki ana şey vardı:

  • Araç çubuğu olmadan yuvalanmış tercih ekranları görüntülendi
  • Tercihler, Lollipop öncesi cihazlarda Materyal görünümüne sahip değildi

Böylece daha karmaşık bir çözüme sahip bir kitaplık oluşturmaya başladım. Temel olarak, bir Lollipop öncesi cihaz kullanıyorsak tercihlere dahili olarak stiller uygulamak zorunda kaldım ve ayrıca özel bir parça kullanarak iç içe geçmiş ekranları da işledim (PreferenceScreen anahtarından yararlanarak tüm iç içe hiyerarşiyi geri yükleyerek ).

Kitaplık şudur: https://github.com/ferrannp/material-preferences

Ve eğer kaynak koduyla ilgileniyorsanız (buraya yazmak için çok uzun), bu temelde onun özüdür: https://github.com/ferrannp/material-preferences/blob/master/library/src/main/ java / com / FNP / materialpreferences / PreferenceFragment.java

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.