How to add Action Bar from support library into PreferenceActivity?

后端 未结 8 538
不思量自难忘°
不思量自难忘° 2020-11-27 09:15

Action Bar compatibility has been added into support library, revision 18. It now has ActionBarActivity class for creating activities with Action Bar on older versions of An

相关标签:
8条回答
  • 2020-11-27 09:48

    I was able to get android.app.Actionbar by using getActionBar(). It returned a null value at first... then I went to the manifest and changed the theme to:

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

    Then I was able to have the actionbar again. I'm assuming this will only work for certain build levels. So you might want to do a check for the build number or check if the value returned is null.

    It'll be fine for me because the app I'm working on is for ICS/4.0+.

    0 讨论(0)
  • 2020-11-27 09:50

    I have managed to create a workaround similar to what the Google Play Store uses. Link to Original Answer

    Please find the GitHub Repo: Here


    Very Similar to your own code but added xml to allow for set title:

    Continuing to use 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 :

    example


    UPDATE (Gingerbread Compatibility) :

    As pointed out here, Gingerbread Devices are returning NullPointerException on this line:

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

    FIX:

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

    Any issues with the above let me know!


    UPDATE 2: TINTING WORKAROUND

    As pointed out in many dev notes PreferenceActivity does not support tinting of elements, however by utilising a few internal classes you CAN achieve this. That is until these classes are removed. (Works using appCompat support-v7 v21.0.3).

    Add the following imports:

    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;
    

    Then override the onCreateView method:

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

    example 2


    AppCompat 22.1

    AppCompat 22.1 introduced new tinted elements, meaning that there is no longer a need to utilise the internal classes to achieve the same effect as the last update. Instead follow this (still overriding 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 PREFERENCE SCREENS

    A lot of people are experiencing issues with including the Toolbar in nested <PreferenceScreen />s however, I have found a solution!! - After a lot of trial and error!

    Add the following to your 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();
            }
        });
    }
    

    The reason that PreferenceScreen's are such a pain is because they are based as a wrapper dialog, so we need to capture the dialog layout to add the toolbar to it.


    Toolbar Shadow

    By design importing the Toolbar does not allow for elevation and shadowing in pre-v21 devices, so if you would like to have elevation on your Toolbar you need to wrap it in a 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>
    

    Not forgetting to add the add the Design Support library as a dependency in build.gradle file:

    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

    I have investigated the reported overlapping issue and I cannot reproduce the issue.

    The full code in use as above produces the following:

    If I am missing something please let me know via this repo and I will investigate.

    0 讨论(0)
  • 2020-11-27 09:50

    Found a PreferenceFragment implementation based on support-v4 Fragment:

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

    Edit: I just tested it and its working great!

    0 讨论(0)
  • 2020-11-27 09:53

    EDIT: In appcompat-v7 22.1.0 Google added the AppCompatDelegate abstract class as a delegate you can use to extend AppCompat's support to any activity.

    Use it like this:

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

    No more hacking. Code taken from AppCompatPreferenceActivity.java.

    0 讨论(0)
  • 2020-11-27 09:55

    There is currently no way to achieve with AppCompat. I've opened a bug internally.

    0 讨论(0)
  • 2020-11-27 10:02

    Problem Background:

    The OP wants to know how can we put MenuItems in the ActionBar of PreferenceActivity for pre-Honeycomb because Android's support library has a bug which doesn't allow this to happen.

    My Solution:

    I've found a much cleaner way, than already proposed, to achieve the target (and found it in the Android Docs):

    android:parentActivityName

    The class name of the logical parent of the activity. The name here must match the class name given to the corresponding element's android:name attribute.

    The system reads this attribute to determine which activity should be started when the use presses the Up button in the action bar. The system can also use this information to synthesize a back stack of activities with TaskStackBuilder.

    To support API levels 4 - 16, you can also declare the parent activity with a element that specifies a value for "android.support.PARENT_ACTIVITY". For example:

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

    Now do what you would normally do in your onOptionsItemSelected(). Since it's a part of Android Docs, it has no side-affects.

    Happy coding. :)

    Update:

    This solution no longer works if you're targeting Lollipop. If you're using AppCompat, this answer is what you should be looking for.

    0 讨论(0)
提交回复
热议问题