Bagaimana cara menambahkan Action Bar dari perpustakaan dukungan ke PreferenceActivity?

128

Kompatibilitas Action Bar telah ditambahkan ke perpustakaan dukungan, revisi 18. Sekarang memiliki ActionBarActivitykelas untuk membuat aktivitas dengan Action Bar pada versi Android yang lebih lama.

Apakah ada cara untuk menambahkan Action Bar dari perpustakaan dukungan ke PreferenceActivity?

Sebelumnya saya menggunakan ActionBarSherlock dan itu SherlockPreferenceActivity.

Roma
sumber
mungkin masih relevan: ActionBar di PreferenceActivity
Matthias Robbers
1
saya baru saja mengedit jawaban saya, menemukan implementasi implementasi preferensifragment berdasarkan dukungan-v4 yang bekerja sangat baik dengan ActionBarActivity. Saya menggunakannya sendiri.
Ostkontentitan
Jika mau, Anda dapat menggunakan solusi saya: github.com/AndroidDeveloperLB/MaterialStuffLibrary
pengembang android

Jawaban:

128

EDIT: Di appcompat-v7 22.1.0 Google menambahkan kelas abstrak AppCompatDelegate sebagai delegasi yang dapat Anda gunakan untuk memperluas dukungan AppCompat ke aktivitas apa pun.

Gunakan seperti ini:

...
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
...

public class SettingsActivity extends PreferenceActivity {

    private AppCompatDelegate mDelegate;

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

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

    public ActionBar getSupportActionBar() {
        return getDelegate().getSupportActionBar();
    }

    public void setSupportActionBar(@Nullable Toolbar toolbar) {
        getDelegate().setSupportActionBar(toolbar);
    }

    @Override
    public MenuInflater getMenuInflater() {
        return getDelegate().getMenuInflater();
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

    @Override
    public void setContentView(View view) {
        getDelegate().setContentView(view);
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().setContentView(view, params);
    }

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().addContentView(view, params);
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        getDelegate().onPostResume();
    }

    @Override
    protected void onTitleChanged(CharSequence title, int color) {
        super.onTitleChanged(title, color);
        getDelegate().setTitle(title);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getDelegate().onConfigurationChanged(newConfig);
    }

    @Override
    protected void onStop() {
        super.onStop();
        getDelegate().onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getDelegate().onDestroy();
    }

    public void invalidateOptionsMenu() {
        getDelegate().invalidateOptionsMenu();
    }

    private AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, null);
        }
        return mDelegate;
    }
}

Tidak ada lagi peretasan. Kode diambil dari AppCompatPreferenceActivity.java .

Ľubomír Kučera
sumber
harap cantumkan cuplikan kode penting Anda dalam jawaban Anda. untuk menghindari kemungkinan masalah tautan rusak.
Yohanes Khosiawan 许先汉
terima kasih, ini bekerja untuk saya - saya akan mengubah LinearLayout baru dalam panggilan inflasi pertama ke:(ViewGroup) getWindow().getDecorView().getRootView()
ahmedre
2
@petrsyn Ini benar-benar berfungsi. Lihat di sini dan di sini untuk mencari tahu bagaimana Anda harus melakukannya.
Ľubomír Kučera
1
@swooby Anda mungkin menderita dari bug ini .
Ľubomír Kučera
1
Oke jadi di mana saya meletakkan bilah alat di xml? Saya mendapatkan NullPointerException
Martin Erlic
78

Saat ini tidak ada cara untuk mencapai dengan AppCompat. Saya sudah membuka bug secara internal.

Chris Banes
sumber
6
Terima kasih @ Chris. Akan menyenangkan memiliki fitur ini.
Pablo
12
@ Chris, apakah Anda tahu kapan kami dapat PreferenceActivitymenambahkannya ActionBarCompat?
imbryk
3
Saya pikir sangat tidak mungkin bahwa fitur ini akan diimplementasikan. Jumlah perangkat yang menjalankan Android versi lama (<4.0) kurang dari 30% saat ini, dan jumlah ini turun setiap bulan.
Roman
1
Ini dimasukkan hampir setahun yang lalu dan tidak ada resolusi ??
Seseorang di suatu tempat
1
apakah masih menunggu ??
Broak
24

Saya telah berhasil membuat solusi yang mirip dengan apa yang digunakan Google Play Store. Tautan ke Jawaban Asli

Silakan temukan Repo GitHub: Di Sini


Sangat mirip dengan kode Anda sendiri tetapi menambahkan xml untuk memungkinkan judul ditetapkan:

Terus menggunakan 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 :

contoh


UPDATE (Kompatibilitas roti jahe):

Seperti yang ditunjukkan di sini , Perangkat Gingerbread mengembalikan NullPointerException pada baris ini:

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

MEMPERBAIKI:

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();
            }
        });
    }
}

Masalah apa pun di atas beri tahu saya!


UPDATE 2: TUTING WORKAROUND

Seperti yang ditunjukkan dalam banyak catatan dev PreferenceActivitytidak mendukung pewarnaan elemen, namun dengan memanfaatkan beberapa kelas internal Anda BISA mencapai ini. Itu sampai kelas-kelas ini dihapus. (Bekerja menggunakan dukungan appCompat-v7 v21.0.3).

Tambahkan impor berikut:

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;

Kemudian timpa onCreateViewmetode:

@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:

contoh 2


AppCompat 22.1

AppCompat 22.1 memperkenalkan elemen berwarna baru, yang berarti bahwa tidak ada lagi kebutuhan untuk menggunakan kelas internal untuk mencapai efek yang sama dengan pembaruan terakhir. Alih-alih ikuti ini (masih menimpa 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;
}

LAYAR PREFERENSI YANG BERTAHAN

Banyak orang mengalami masalah dengan memasukkan Toolbar di dalam nested <PreferenceScreen />, namun, saya telah menemukan solusinya !! - Setelah banyak coba-coba!

Tambahkan yang berikut ke Anda 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();
        }
    });
}

Alasan yang PreferenceScreenmenyusahkan adalah karena mereka didasarkan sebagai dialog pembungkus, jadi kita perlu menangkap tata letak dialog untuk menambahkan bilah alat ke dalamnya.


Bayangan Bilah Alat

Dengan mengimpor desain, Toolbarini tidak memungkinkan elevasi dan bayangan pada perangkat pre-v21, jadi jika Anda ingin memiliki elevasi pada Anda, ToolbarAnda harus membungkusnya dalam 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>

Tidak lupa untuk menambahkan tambahkan pustaka Dukungan Desain sebagai ketergantungan pada build.gradlefile:

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

Saya telah menyelidiki masalah tumpang tindih yang dilaporkan dan saya tidak dapat mereproduksi masalah ini.

Kode lengkap yang digunakan seperti di atas menghasilkan yang berikut:

masukkan deskripsi gambar di sini

Jika saya kehilangan sesuatu tolong beri tahu saya melalui repo ini dan saya akan menyelidiki.

David Passmore
sumber
1
Bagus! Bekerja seperti pesona :)
Tobi
Mengapa ini tidak berfungsi untuk PreferenceScreen bersarang, tetapi hanya untuk PreferenceScreen utama?
Tobi
@Tobi Saya telah memperbarui jawaban saya untuk menyelesaikan masalah yang bersarang, dan menjelaskan alasannya. :)
David Passmore
Terima kasih! Saran Anda "AppCompat 22.1" melakukan trik untuk saya :) Sangat berguna!
Martin Pfeffer
1
mengapa ada PreferenceActivity rasa sakit di pantat untuk digunakan ??? itu seharusnya menghemat waktu. Saya mungkin juga membuat aktivitas reguler dan secara manual menata semua pengaturan dalam tata letak linier sendiri. Fuuuuck!
Seseorang di suatu tempat
12

Menemukan implementasi PreferenceFragment berdasarkan pada support-v4 Fragment:

https://github.com/kolavar/android-support-v4-preferencefragment

Sunting: Saya baru saja mengujinya dan kerjanya sangat bagus!

Ostkontentitan
sumber
@Konstantin, dapatkah Anda menambahkan beberapa contoh kode? Saya mengunduhnya, mengubah impor dari android.preference.PreferenceFragment ke android.support.v4.preference.PreferenceFragment, dan saya melihatnya menambahkan beberapa header di tengah layar, tetapi bukan ActionBar di atas
Gavriel
Itu tidak menambahkan bilah tindakan itu pekerjaan kegiatan. Sayangnya saya tidak punya samplecode di tangan tetapi harus bekerja mirip dengan: developer.android.com/reference/android/preference/…
Ostkontentitan
3

Mengintegrasikan PreferenceActivitydengan ABC tidak mungkin, setidaknya bagi saya. Saya mencoba dua kemungkinan yang bisa saya temukan tetapi tidak ada yang berhasil:

Pilihan 1:

ActionBarPreferenceActivitymeluas PreferenceActivity. Ketika Anda melakukan ini, Anda dibatasi oleh ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). Anda juga perlu menerapkan ActionBar.Callbacksyang tidak dapat diakses

Pilihan 2:

ActionBarPreferenceActivitymeluas ActionBarActivity. Pendekatan ini membutuhkan penulisan ulang yang benar-benar baru PreferenceActivity, PreferenceManagerdan mungkin PreferenceFragmentartinya Anda memerlukan akses ke kelas tersembunyi seperti com.android.internal.util.XmlUtils Solusi ini hanya dapat datang dari Google devs yang mengimplementasikan dan ActionBarWrapperdapat ditambahkan ke aktivitas apa pun.

Jika Anda benar-benar membutuhkan kegiatan preferensi, saran saya untuk saat ini adalah ActionBarSherlock.

Namun, saya berhasil mengimplementasikannya di sini .

Maxwell Weru
sumber
1
Per4.0 membawa crash. Saya sudah bercabang dan ditingkatkan. gist.github.com/0e9fe2119b901921b160
TeeTracker
Periksa solusi saya. Itu tidak menggunakan solusi stackoverflow.com/a/25603448/1276636
Sufian
Itu tidak menyelesaikan apa pun! Semoga Anda memahami masalah yang dihadapi
Maxwell Weru
3

Latar Belakang Masalah:

OP ingin tahu bagaimana kita dapat menempatkan MenuItems di ActionBardari PreferenceActivityuntuk pra-Honeycomb karena dukungan perpustakaan Android memiliki bug yang tidak memungkinkan ini terjadi.

Solusi Saya:

Saya telah menemukan cara yang jauh lebih bersih, daripada yang sudah diusulkan, untuk mencapai target (dan menemukannya di Android Docs ):

android:parentActivityName

Nama kelas dari induk logis dari aktivitas. Nama di sini harus cocok dengan nama kelas yang diberikan ke android: atribut name elemen yang sesuai.

Sistem membaca atribut ini untuk menentukan aktivitas mana yang harus dimulai ketika penggunaan menekan tombol Atas di baris tindakan. Sistem juga dapat menggunakan informasi ini untuk mensintesis setumpuk aktivitas dengan TaskStackBuilder.

Untuk mendukung level API 4 - 16, Anda juga dapat mendeklarasikan aktivitas induk dengan elemen yang menentukan nilai untuk "android.support.PARENT_ACTIVITY". Sebagai contoh:

<activity
    android:name="com.example.app.ChildActivity"
    android:label="@string/title_child_activity"
    android:parentActivityName="com.example.myfirstapp.MainActivity" >
    <!-- Parent activity meta-data to support API level 4+ -->
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.example.app.MainActivity" />
</activity>

Sekarang lakukan apa yang biasanya Anda lakukan di onOptionsItemSelected(). Karena ini adalah bagian dari Android Documents, itu tidak memiliki efek samping.

Selamat coding. :)

Memperbarui:

Solusi ini tidak lagi berfungsi jika Anda menargetkan Lollipop. Jika Anda menggunakan AppCompat, jawaban ini adalah yang seharusnya Anda cari.

Sufian
sumber
Bagaimana ini membantu mendapatkan bilah tindakan di aktivitas Preferensi?
Frozen Crayon
@ArjunU. solusi mudah - coba dan cari tahu.
Sufian
@ArjunU. Masalahnya adalah bahwa PreferencesActivitytidak ada cara untuk menempatkan barang ActionBar, terutama tombol kembali. Jawaban saya adalah perbaikan yang bagus untuk itu.
Sufian
Terima kasih untuk downvote. Adakah penjelasan bagaimana saya dapat meningkatkan jawaban saya atau apa yang salah?
Sufian
1
@Sufian Terima kasih telah menghubungkan ke jawaban saya, senang itu membantu semua orang:)
David Passmore
1

Saya bisa android.app.Actionbarmenggunakan getActionBar(). Awalnya mengembalikan nilai nol ... lalu saya pergi ke manifes dan mengubah tema menjadi:

android:theme="@style/Theme.AppCompat"

Kemudian saya dapat memiliki actionbar lagi. Saya berasumsi ini hanya akan berfungsi untuk level build tertentu. Jadi, Anda mungkin ingin melakukan pemeriksaan untuk nomor build atau memeriksa apakah nilai yang dikembalikan adalah nol.

Ini akan baik-baik saja bagi saya karena aplikasi yang sedang saya kerjakan adalah untuk ICS/4.0+.

RCB
sumber
Inilah solusi yang lebih sederhana, yang tidak menggunakan workarounds stackoverflow.com/a/25603448/1276636
Sufian