Làm cách nào để thêm Action Bar từ thư viện hỗ trợ vào PreferenceActivity?


128

Khả năng tương thích Action Bar đã được thêm vào thư viện hỗ trợ, phiên bản 18. Hiện tại nó có ActionBarActivitylớp để tạo các hoạt động với Action Bar trên các phiên bản Android cũ hơn.

Có cách nào để thêm Action Bar từ thư viện hỗ trợ vào PreferenceActivitykhông?

Trước đây tôi đã sử dụng ActionBarSherlock và nó có SherlockPreferenceActivity.


có thể vẫn còn có liên quan: ActionBar in PreferenceActivity
Matthias Robbers

1
Tôi vừa chỉnh sửa câu trả lời của mình, đã tìm thấy một triển khai thực hiện ưu tiên làm việc dựa trên support-v4 hoạt động tuyệt vời với ActionBarActivity. Tôi đang sử dụng nó một mình.
Ostkontentitan

Nếu bạn muốn, bạn có thể sử dụng giải pháp của tôi: github.com/AndroidDeveloperLB/M vật liệuStuffL Library
nhà phát triển Android

Câu trả lời:


128

EDIT: Trong appcompat-v7 22.1.0 Google đã thêm lớp trừu tượng AppCompatDelegate làm đại biểu mà bạn có thể sử dụng để mở rộng hỗ trợ của AppCompat cho bất kỳ hoạt động nào.

Sử dụng nó như thế này:

...
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;
    }
}

Không còn hack nữa. Mã được lấy từ AppCompatPreferenceActivity.java .


xin vui lòng đặt đoạn mã cần thiết của bạn trong câu trả lời của bạn. để tránh các vấn đề liên kết bị hỏng tiềm năng.
Yohanes Khosiawan

cảm ơn, điều này hiệu quả với tôi - tôi sẽ thay đổi tuyến tính mới trong cuộc gọi tăng cao đầu tiên thành:(ViewGroup) getWindow().getDecorView().getRootView()
ahmedre

2
@petrsyn Nó thực sự hoạt động. Nhìn vào đâyở đây để tìm hiểu làm thế nào bạn nên làm điều đó.
Ľubomír Kučera

1
@swooby Bạn có thể bị lỗi này .
Ľubomír Kučera

1
Được rồi, vậy tôi phải đặt thanh công cụ ở đâu trong xml? Tôi nhận được một NullPointerException
Martin Erlic

78

Hiện tại không có cách nào để đạt được với AppCompat. Tôi đã mở một lỗi nội bộ.


6
Cảm ơn @Chris. Sẽ rất tốt để có tính năng này.
Pablo

12
@Chris, bạn có ý kiến ​​gì khi chúng tôi có thể mong đợi PreferenceActivityđược thêm vào ActionBarCompatkhông?
imbryk

3
Tôi nghĩ rằng rất có thể tính năng này sẽ được thực hiện. Số thiết bị chạy các phiên bản Android cũ hơn (<4.0) chưa đến 30% vào thời điểm này và con số này sẽ giảm mỗi tháng.
La Mã

1
Điều này đã được nhập gần một năm trước và không có giải pháp ??
Ai đó ở đâu đó

1
wtf vẫn chờ chứ ??
Broak

24

Tôi đã quản lý để tạo một cách giải quyết tương tự như những gì Google Play Store sử dụng. Liên kết đến câu trả lời gốc

Vui lòng tìm Repo GitHub: Tại đây


Rất giống với mã của riêng bạn nhưng đã thêm xml để cho phép đặt tiêu đề:

Tiếp tục sử dụng 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 :

thí dụ


CẬP NHẬT (Tương thích Gingerbread):

Như đã chỉ ra ở đây , Gingerbread Devices đang trả về NullPulumException trên dòng này:

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

SỬA CHỮA:

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

Bất kỳ vấn đề với ở trên cho tôi biết!


CẬP NHẬT 2: LÀM VIỆC TINTING

Như đã chỉ ra trong nhiều ghi chú dev PreferenceActivitykhông hỗ trợ pha màu các phần tử, tuy nhiên bằng cách sử dụng một vài lớp bên trong, bạn CÓ THỂ đạt được điều này. Đó là cho đến khi các lớp này được gỡ bỏ. (Hoạt động bằng appCompat support-v7 v21.0.3).

Thêm các nhập khẩu sau:

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;

Sau đó ghi đè onCreateViewphương thức:

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

ví dụ 2


Ứng dụng 22.1

AppCompat 22.1 đã giới thiệu các yếu tố pha màu mới, có nghĩa là không còn cần phải sử dụng các lớp bên trong để đạt được hiệu quả tương tự như bản cập nhật cuối cùng. Thay vào đó hãy làm theo điều này (vẫn ghi đè 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;
}

MÀN HÌNH ƯU ĐÃI

Rất nhiều người đang gặp vấn đề với việc bao gồm Thanh công cụ trong các lồng nhau <PreferenceScreen />, tuy nhiên, tôi đã tìm thấy một giải pháp !! - Sau rất nhiều thử nghiệm và sai sót!

Thêm phần sau vào 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();
        }
    });
}

Lý do PreferenceScreenlà một nỗi đau như vậy là vì chúng dựa trên hộp thoại bao bọc, vì vậy chúng ta cần phải nắm bắt bố cục hộp thoại để thêm thanh công cụ vào nó.


Thanh công cụ Shadow

Bằng cách thiết kế nhập, Toolbarkhông cho phép độ cao và bóng trong các thiết bị trước v21, vì vậy nếu bạn muốn có độ cao trên mình, Toolbarbạn cần phải bọc nó trong 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>

Đừng quên thêm thư viện Hỗ trợ thiết kế dưới dạng phụ thuộc trong build.gradletệp:

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

Tôi đã điều tra vấn đề chồng chéo được báo cáo và tôi không thể tái tạo vấn đề.

Mã đầy đủ được sử dụng như trên tạo ra các điều sau đây:

nhập mô tả hình ảnh ở đây

Nếu tôi thiếu một cái gì đó xin vui lòng cho tôi biết thông qua repo này và tôi sẽ điều tra.


1
Tuyệt quá! Hoạt động như một bùa mê :)
Tobi

Tại sao điều này không hoạt động cho PreferenceScreen lồng nhau, mà chỉ cho PreferenceScreen chính?
Tobi

@Tobi Tôi đã cập nhật câu trả lời của mình để giải quyết vấn đề lồng nhau, và giải thích lý do tại sao. :)
David Passmore

Cảm ơn! Lời khuyên của bạn "AppCompat 22.1" đã giúp tôi rất nhiều :) Rất hữu ích!
Martin Pfeffer

1
Tại sao lại PreferenceActivity đau như vậy ở mông để sử dụng ??? nó được cho là để tiết kiệm thời gian Tôi cũng có thể thực hiện một hoạt động thường xuyên và tự mình bố trí tất cả các cài đặt trong bố cục tuyến tính. Fuuuuck!
Ai đó ở đâu đó

12

Đã tìm thấy triển khai PreferenceFragment dựa trên Fragment-v4:

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

Chỉnh sửa: Tôi vừa thử nó và nó hoạt động rất tốt!


@Konstantin, bạn có thể thêm một số ví dụ mã? Tôi đã tải xuống nó, thay đổi quá trình nhập từ android.preference.PreferenceFragment thành android.support.v4.preference.PreferenceFragment và tôi thấy nó đã thêm một số tiêu đề ở giữa màn hình, nhưng không phải là ActionBar trên đầu trang
Gavriel

Nó không thêm thanh hành động là công việc của hoạt động. Thật không may, tôi không có samplecode trong tay nhưng nó sẽ hoạt động tương tự như: developer.android.com/reference/android/preference/
Kẻ

3

Tích hợp PreferenceActivityvới ABC là không thể, ít nhất là đối với tôi. Tôi đã thử hai khả năng tôi có thể tìm thấy nhưng không có khả năng nào:

Lựa chọn 1:

ActionBarPreferenceActivitykéo dài PreferenceActivity. Khi bạn làm điều này, bạn bị hạn chế bởi ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). Ngoài ra, bạn cần phải thực hiện ActionBar.Callbacksmà không thể truy cập

Lựa chọn 2:

ActionBarPreferenceActivitykéo dài ActionBarActivity. Cách tiếp cận này đòi hỏi phải viết lại một hoàn toàn mới PreferenceActivity, PreferenceManagervà có thể PreferenceFragmentcó nghĩa là bạn cần truy cập vào các lớp ẩn như com.android.internal.util.XmlUtils Các giải pháp này chỉ có thể đến từ Google devs thực hiện một ActionBarWrappermà có thể được thêm vào bất kỳ hoạt động.

Nếu bạn thực sự cần một hoạt động ưu tiên, lời khuyên của tôi bây giờ là ActionBarSherlock.

Tuy nhiên, tôi quản lý để thực hiện nó ở đây .


1
Per4.0 mang đến sự cố. Tôi đã rẽ nhánh nó và cải thiện. gist.github.com/0e9fe2119b901921b160
TeeTracker

Kiểm tra giải pháp của tôi. Nó không sử dụng bất kỳ stackoverflow
Sufian

Nó không giải quyết được gì cả! Hy vọng bạn hiểu vấn đề trong tay
Maxwell Weru

3

Bối cảnh vấn đề:

OP muốn biết làm thế nào chúng ta có thể đặt MenuItems trong ActionBarcủa PreferenceActivitycho tiền Honeycomb vì thư viện hỗ trợ Android có một lỗi mà không cho phép điều này xảy ra.

Giải pháp của tôi:

Tôi đã tìm thấy một cách sạch sẽ hơn nhiều so với đề xuất, để đạt được mục tiêu (và tìm thấy nó trong Tài liệu Android ):

android:parentActivityName

Tên lớp của cha mẹ logic của hoạt động. Tên ở đây phải khớp với tên lớp được đặt cho thuộc tính android: name của phần tử tương ứng.

Hệ thống đọc thuộc tính này để xác định hoạt động nào sẽ được bắt đầu khi sử dụng nhấn nút Lên trên thanh hành động. Hệ thống cũng có thể sử dụng thông tin này để tổng hợp một nhóm hoạt động trở lại với TaskStackBuilder.

Để hỗ trợ các cấp API 4 - 16, bạn cũng có thể khai báo hoạt động chính với một phần tử chỉ định giá trị cho "android.support.PARENT_ACTIVITY". Ví dụ:

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

Bây giờ làm những gì bạn thường làm trong của bạn onOptionsItemSelected(). Vì nó là một phần của Tài liệu Android, nên nó không có tác dụng phụ.

Chúc mừng mã hóa. :)

Cập nhật:

Giải pháp này không còn hoạt động nếu bạn đang nhắm mục tiêu Lollipop. Nếu bạn đang sử dụng AppCompat, này câu trả lời là những gì bạn nên tìm kiếm.


Làm thế nào điều này giúp có được thanh hành động trong hoạt động Ưu tiên?
Frozen Crayon

@ArjunU. giải pháp dễ dàng - thử nó và tìm ra nó.
Sufian

@ArjunU. Vấn đề là PreferencesActivitysẽ không có cách nào để đặt vật phẩm ActionBar, đặc biệt là nút quay lại. Câu trả lời của tôi là một sửa chữa tốt cho điều đó.
Sufian

Cảm ơn các downvote. Bất kỳ lời giải thích làm thế nào tôi có thể cải thiện câu trả lời của tôi hoặc những gì sai?
Sufian

1
@Sufian Cảm ơn bạn đã liên kết với câu trả lời của tôi, rất vui vì nó giúp được tất cả mọi người:)
David Passmore

1

Tôi đã có thể nhận được android.app.Actionbarbằng cách sử dụng getActionBar(). Nó đã trả về một giá trị null lúc đầu ... sau đó tôi đã đi đến bảng kê khai và thay đổi chủ đề thành:

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

Sau đó, tôi đã có thể có thanh hành động một lần nữa. Tôi cho rằng điều này sẽ chỉ hoạt động đối với các cấp độ xây dựng nhất định. Vì vậy, bạn có thể muốn thực hiện kiểm tra số bản dựng hoặc kiểm tra xem giá trị được trả về có phải không.

Nó sẽ ổn với tôi vì ứng dụng tôi đang làm việc là dành cho ICS/4.0+.


Đây là một giải pháp đơn giản hơn, một giải pháp không sử dụng bất kỳ cách giải quyết stackoverflow.com/a/25603448/1276636
Sufian

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.