จะเพิ่ม Action Bar จากไลบรารี่สนับสนุนลงใน PreferenceActivity ได้อย่างไร


128

ความเข้ากันได้ของ Action Bar ได้รับการเพิ่มเข้าไปในห้องสมุดสนับสนุนการแก้ไขที่ 18 ตอนนี้มีActionBarActivityคลาสสำหรับสร้างกิจกรรมกับ Action Bar บน Android เวอร์ชันเก่า

มีวิธีการใด ๆ เพื่อเพิ่มแถบการทำงานจากห้องสมุดสนับสนุนเข้ามาPreferenceActivity?

ก่อนหน้านี้ผมใช้ActionBarSherlockSherlockPreferenceActivityและมี


อาจเกี่ยวข้อง: ActionBar ใน PreferenceActivity
Matthias Robbers

1
ฉันเพิ่งแก้ไขคำตอบของฉันพบการใช้งานการนำเสนอการตั้งค่าการทำงานตาม support-v4 ที่ใช้งานได้ดีกับ ActionBarActivity ฉันกำลังใช้งานด้วยตัวเอง
Ostkontentitan

หากคุณต้องการคุณสามารถใช้วิธีแก้ปัญหาของฉัน: github.com/AndroidDeveloperLB/MaterialStuffLibrary
นักพัฒนา android

คำตอบ:


128

แก้ไข: ใน appcompat-v7 22.1.0 Google เพิ่ม AppCompatDelegate นามธรรมระดับเป็นตัวแทนคุณสามารถใช้เพื่อขยายการสนับสนุนของ AppCompat เพื่อกิจกรรมใด ๆ

ใช้แบบนี้:

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

ไม่มีการแฮ็คอีกต่อไป รหัสนำมาจากAppCompatPreferenceActivity.java


โปรดใส่ตัวอย่างรหัสที่สำคัญของคุณในคำตอบของคุณ เพื่อหลีกเลี่ยงปัญหาลิงค์เสีย
Yohanes Khosiawan 许先汉

ขอบคุณนี้ใช้งานได้สำหรับฉัน - ฉันต้องการเปลี่ยน LinearLayout ใหม่ในการโทรครั้งแรกที่ขยายไปยัง:(ViewGroup) getWindow().getDecorView().getRootView()
ahmedre

2
@petrsyn ใช้งานได้จริง ดูที่นี่และที่นี่เพื่อค้นหาว่าคุณควรทำอย่างไร
omubomírKučera

1
@swooby คุณอาจจะต้องทนทุกข์ทรมานจากนี้ข้อผิดพลาด
ĽubomírKučera

1
ตกลงฉันจะวางแถบเครื่องมือใน xml ที่ไหน? ฉันได้รับ NullPointerException
Martin Erlic

78

ขณะนี้ยังไม่มีวิธีที่จะทำให้สำเร็จด้วย AppCompat ฉันเปิดบั๊กภายในแล้ว


6
ขอบคุณ @Chris จะดีถ้ามีคุณสมบัตินี้
Pablo

12
@Chris คุณมีความคิดใด ๆ เมื่อเราคาดว่าPreferenceActivityจะเพิ่มActionBarCompatหรือไม่
imbryk

3
ฉันคิดว่ามันไม่น่าเป็นไปได้มากที่คุณลักษณะนี้จะถูกนำไปใช้ จำนวนอุปกรณ์ที่ใช้ Android เวอร์ชั่นเก่ากว่า (<4.0) น้อยกว่า 30% ณ จุดนี้และจำนวนนี้จะลดลงทุกเดือน
โรมัน

1
นี้ถูกป้อนเกือบหนึ่งปีที่ผ่านมาและไม่มีวิธีแก้ปัญหา?
คนอยู่ที่ไหนสักแห่ง

1
wtf ยังคงรออยู่หรือไม่?
Broak

24

ฉันจัดการเพื่อสร้างวิธีแก้ปัญหาคล้ายกับที่ Google Play Store ใช้ ลิงก์ไปยังคำตอบดั้งเดิม

กรุณาหา GitHub Repo: ที่นี่


คล้ายกันมากกับรหัสของคุณ แต่เพิ่ม xml เพื่ออนุญาตให้ตั้งชื่อ:

ยังคงใช้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 :

ตัวอย่าง


อัปเดต (ความเข้ากันได้ Gingerbread):

ตามที่กล่าวไว้ที่นี่อุปกรณ์ Gingerbread กำลังส่งคืน NullPointerException ในบรรทัดนี้:

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

การแก้ไข:

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

ปัญหาใด ๆ ข้างต้นแจ้งให้เราทราบ!


อัปเดต 2: การย้อมสีการหลีกเลี่ยงปัญหา

ตามที่ระบุไว้ในบันทึกย่อ dev จำนวนมากPreferenceActivityไม่สนับสนุนการย้อมสีขององค์ประกอบอย่างไรก็ตามด้วยการใช้คลาสภายในไม่กี่อย่างที่คุณสามารถทำได้ นั่นคือจนกว่าคลาสเหล่านี้จะถูกลบออก (ทำงานได้โดยใช้ appCompat support-v7 v21.0.3)

เพิ่มการนำเข้าต่อไปนี้:

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;

จากนั้นแทนที่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 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:

ตัวอย่างที่ 2


AppCompat 22.1

AppCompat 22.1 แนะนำองค์ประกอบสีใหม่ซึ่งหมายความว่าไม่จำเป็นต้องใช้คลาสภายในเพื่อให้ได้ผลเช่นเดียวกับการอัพเดตล่าสุดอีกต่อไป ทำตามนี้แทน (ยังคงเอาชนะ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;
}

หน้าจอการตั้งค่าแบบซ้อน

ผู้คนจำนวนมากกำลังประสบปัญหาเกี่ยวกับการรวมถึง Toolbar ในที่ซ้อนกัน<PreferenceScreen />แต่ฉันได้พบวิธีการแก้ปัญหา !! - หลังจากการทดลองและข้อผิดพลาดมากมาย!

เพิ่มสิ่งต่อไปนี้ในของคุณ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();
        }
    });
}

สาเหตุที่ทำให้PreferenceScreenเกิดความเจ็บปวดนั้นเป็นเพราะพวกเขาใช้กล่องโต้ตอบห่อหุ้มดังนั้นเราต้องจับเค้าโครงกล่องโต้ตอบเพื่อเพิ่มแถบเครื่องมือ


แถบเครื่องมือเงา

โดยการนำเข้าการออกแบบToolbarไม่อนุญาตให้มีการยกระดับและเงาในอุปกรณ์ pre-v21 ดังนั้นหากคุณต้องการยกระดับความสูงของToolbarคุณคุณต้องห่อมันใน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.gradleไฟล์:

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

ฉันได้ตรวจสอบปัญหาที่ทับซ้อนกันของรายงานและฉันไม่สามารถทำซ้ำปัญหาได้

รหัสเต็มรูปแบบในการใช้งานดังกล่าวผลิตดังต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

ถ้าฉันหายไปบางอย่างโปรดแจ้งให้เราทราบผ่านrepo นี้และฉันจะตรวจสอบ


1
ที่ดี! ใช้งานได้อย่างมีเสน่ห์ :)
Tobi

ทำไมสิ่งนี้ไม่ทำงานสำหรับ PreferenceScreen ที่ซ้อนกัน แต่สำหรับ PreferenceScreen หลักเท่านั้น
Tobi

@Tobi ฉันได้อัปเดตคำตอบของฉันเพื่อแก้ไขปัญหาที่ซ้อนกันและอธิบายว่าทำไม :)
David Passmore

ขอบคุณ! คำแนะนำของคุณ "AppCompat 22.1" ได้หลอกลวงสำหรับฉัน :) มีประโยชน์มาก!
Martin Pfeffer

1
ทำไมPreferenceActivity ความเจ็บปวดในตูดถึงใช้ ??? มันควรจะประหยัดเวลา ฉันอาจทำกิจกรรมปกติและวางการตั้งค่าทั้งหมดด้วยตนเองในรูปแบบเชิงเส้นด้วยตนเอง Fuuuuck!
คนอยู่ที่ไหนสักแห่ง

12

พบการปรับใช้ PreferenceFragment ตาม support-v4 Fragment:

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

แก้ไข: ฉันเพิ่งทดสอบและใช้งานได้ดี!


@Konstantin คุณสามารถเพิ่มตัวอย่างรหัสได้ไหม ฉันดาวน์โหลดเปลี่ยนการอิมพอร์ตจาก android.preference.PreferenceFragment เป็น android.support.v4.preference.PreferenceFragment และฉันเห็นว่ามันเพิ่มส่วนหัวบางส่วนไว้ที่กึ่งกลางหน้าจอ แต่ไม่ใช่ ActionBar ด้านบน
Gavriel

ไม่ได้เพิ่ม actionbar ที่เป็นหน้าที่ของกิจกรรม โชคไม่ดีที่ฉันไม่มีโค้ดตัวอย่างอยู่ในมือ แต่มันก็ควรจะทำงานคล้ายกับ: developer.android.com/reference/android/preference/
......

3

การรวมเข้าPreferenceActivityกับ ABC เป็นไปไม่ได้อย่างน้อยสำหรับฉัน ฉันลองสองวิธีที่เป็นไปได้ แต่ไม่มีใครทำได้:

ตัวเลือกที่ 1:

ActionBarPreferenceActivityPreferenceActivityขยาย เมื่อคุณทำเช่นนี้คุณจะได้รับการ จำกัด ActionBarActivityDelegate.createDelegate(ActionBarActivity activity)โดย นอกจากนี้คุณต้องใช้ActionBar.Callbacksสิ่งที่ไม่สามารถเข้าถึงได้

ตัวเลือก 2:

ActionBarPreferenceActivityActionBarActivityขยาย วิธีนี้ต้องเขียนใหม่ทั้งหมดPreferenceActivity, PreferenceManagerและอาจจะเป็นPreferenceFragmentซึ่งหมายความว่าคุณต้องเข้าสู่การเรียนที่ซ่อนอยู่เช่นcom.android.internal.util.XmlUtils วิธีการนี้เท่านั้นที่สามารถมาจาก Google devs การดำเนินการActionBarWrapperที่สามารถเพิ่มกิจกรรมใด ๆ

ActionBarSherlockหากคุณต้องการกิจกรรมการตั้งค่าจริงๆคำแนะนำของฉันตอนนี้คือ

แต่ผมมีการจัดการที่จะใช้มันนี่


1
Per4.0 นำมาซึ่งความผิดพลาด ฉันแยกมันออกและปรับปรุง gist.github.com/0e9fe2119b901921b160
TeeTracker

ตรวจสอบทางออกของฉัน ไม่ใช้วิธีแก้ปัญหาstackoverflow.com/a/25603448/1276636
Sufian

มันไม่ได้แก้ปัญหาอะไร! หวังว่าคุณจะเข้าใจปัญหาในมือ
Maxwell Weru

3

พื้นหลังของปัญหา:

สหกรณ์ต้องการที่จะทราบว่าเราสามารถใส่MenuItems ในActionBarการPreferenceActivityล่วงหน้ารังผึ้งเพราะห้องสมุดการสนับสนุนของ Android มีข้อผิดพลาดที่ไม่ปล่อยให้เรื่องนี้เกิดขึ้น

โซลูชันของฉัน:

ฉันพบวิธีที่สะอาดกว่าที่เสนอไปแล้วเพื่อให้บรรลุเป้าหมาย (และพบในเอกสาร Android ):

android:parentActivityName

ชื่อคลาสของพาเรนต์แบบลอจิคัลของกิจกรรม ชื่อที่นี่จะต้องตรงกับชื่อคลาสที่กำหนดให้กับแอตทริบิวต์ android: name ขององค์ประกอบที่สอดคล้องกัน

ระบบจะอ่านแอททริบิวนี้เพื่อกำหนดว่ากิจกรรมใดควรเริ่มต้นเมื่อการใช้งานกดปุ่มขึ้นในแถบการกระทำ ระบบยังสามารถใช้ข้อมูลนี้เพื่อสังเคราะห์กิจกรรมด้านหลังสแต็กกับ TaskStackBuilder

เพื่อรองรับ API ระดับ 4 - 16 คุณสามารถประกาศกิจกรรมหลักด้วยองค์ประกอบที่ระบุค่าสำหรับ "android.support.PARENT_ACTIVITY" ตัวอย่างเช่น:

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

onOptionsItemSelected()ตอนนี้ทำสิ่งที่คุณทำตามปกติในของคุณ เนื่องจากเป็นส่วนหนึ่งของเอกสาร Android จึงไม่มีผลข้างเคียง

การเข้ารหัสที่มีความสุข :)

ปรับปรุง:

โซลูชันนี้ใช้งานไม่ได้อีกต่อไปหากคุณกำหนดเป้าหมายไปที่ Lollipop หากคุณใช้ AppCompat คำตอบนี้คือสิ่งที่คุณควรมองหา


สิ่งนี้จะช่วยให้ได้แถบการกระทำในกิจกรรมการตั้งค่าได้อย่างไร
Frozen Crayon

@ArjunU ทางออกที่ง่าย - ลองและคิดออก
Sufian

@ArjunU ปัญหาคือว่าPreferencesActivityไม่มีทางที่จะนำรายการไปActionBarโดยเฉพาะอย่างยิ่งปุ่มย้อนกลับ คำตอบของฉันคือการแก้ไขที่ดีสำหรับการที่
Sufian

ขอบคุณสำหรับ downvote คำอธิบายใด ๆ ที่ฉันสามารถปรับปรุงคำตอบของฉันหรือสิ่งที่ผิด?
Sufian

1
@Sufian ขอบคุณที่เชื่อมโยงกับคำตอบของฉันดีใจที่ได้ช่วยเหลือทุกคน:)
David Passmore

1

ผมสามารถที่จะได้รับโดยใช้android.app.Actionbar getActionBar()มันคืนค่า null ตอนแรก ... จากนั้นฉันไปที่รายการและเปลี่ยนชุดรูปแบบเป็น:

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

จากนั้นฉันก็สามารถมีแถบแอ็คชั่นได้อีกครั้ง ฉันสมมติว่าสิ่งนี้จะใช้ได้กับระดับการสร้างเท่านั้น ดังนั้นคุณอาจต้องการตรวจสอบหมายเลขบิลด์หรือตรวจสอบว่าค่าที่ส่งคืนเป็นโมฆะหรือไม่

มันจะไม่เป็นไรสำหรับฉันเพราะแอพที่ฉันกำลังทำอยู่สำหรับICS/4.0+


นี่เป็นวิธีที่ง่ายกว่าซึ่งไม่มีวิธีแก้ไขปัญหาstackoverflow.com/a/25603448/1276636
Sufian

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.