gregkorossy / Android-Support-Preference-V7-Fix

Android androidx.preference support library has some issues, this lib tries to fix them.
https://discord.gg/87NVsSK
Apache License 2.0
497 stars 46 forks source link

Issue when changing from support library v7 to androidx #186

Closed andreluizreis closed 6 years ago

andreluizreis commented 6 years ago

My settings page looked like that before.

screenshot_20181005-132806

Now it's looking like that.

screenshot_2018-10-05-13-23-54

This is API 16 Old phone

screenshot_1538762265

Manifest

<activity
            android:name=".frontend.settings.SettingsActivity"
            android:label="@string/settings"
            android:screenOrientation="portrait"
            android:theme="@style/SettingsTheme">
</activity>

I am using this Theme:

<style name="SettingsTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorPrimary</item>
        <item name="android:textColorPrimary">@android:color/black</item>
        <item name="android:textColorSecondary">@color/grey</item>
</style>

I am importing these libraries:

implementation 'androidx.preference:preference:1.0.0' 
implementation 'com.takisoft.preferencex:preferencex:1.0.0'

pref_notification.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <androidx.preference.PreferenceCategory
        android:key=""
        android:layout="@layout/preferences_notif_title"
        android:title="@string/notif_title_business_related" />

    <androidx.preference.PreferenceCategory
        android:key="title_email_job"
        android:layout="@layout/preferences_category"
        android:title="@string/notif_title_email_job">

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-request-new"
            android:title="@string/notif_email_new_customer_requests" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-request-reminder"
            android:title="@string/notif_email_request_reminders" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-bid-activity"
            android:summary="@string/notif_email_new_customer_activity_summary"
            android:title="@string/notif_email_new_customer_activity" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-bid-view"
            android:title="@string/notif_email_customer_viewing_bids" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-bid-other"
            android:summary="@string/notif_email_other_bid_activity_summary"
            android:title="@string/notif_email_other_bid_activity" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-profile-reminder"
            android:title="@string/notif_email_profile_setup_reminder" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-bm-budget-alert"
            android:title="@string/notif_email_bidmatch_budget_alert" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-bm-reminder"
            android:title="@string/notif_email_bidMatch_setup_reminder" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-pro-account-activity"
            android:title="@string/notif_email_account_activity" />
    </androidx.preference.PreferenceCategory>

    <androidx.preference.PreferenceCategory
        android:key="title_push"
        android:layout="@layout/preferences_category"
        android:title="@string/notif_title_get_push_notif_when">

        <androidx.preference.SwitchPreferenceCompat
            android:key="push-pro-request-new"
            android:title="@string/notif_push_new_customer_requests" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="push-pro-bid-activity"
            android:summary="@string/notif_push_new_customer_activity_summary"
            android:title="@string/notif_push_new_customer_activity" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="push-pro-bid-view"
            android:title="@string/notif_push_customer_viewing_bids" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="push-pro-bid-follow-up"
            android:title="@string/notif_push_bid_follow_up" />

    </androidx.preference.PreferenceCategory>

    <androidx.preference.PreferenceCategory
        android:key="title_email_me_with"
        android:layout="@layout/preferences_category"
        android:title="@string/notification_title_email_me_with">

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-pro-tips"
            android:summary="@string/notif_with_tips_success_summary"
            android:title="@string/notif_with_tips_success" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-pro-newsletter"
            android:summary="@string/notif_with_announcements_summary"
            android:title="@string/notif_with_announcements" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-pro-feedback"
            android:summary="@string/notif_with_help_bidvine_summary"
            android:title="@string/notif_with_help_bidvine" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-pro-offers"
            android:summary="@string/notif_with_discount_offers_summary"
            android:title="@string/notif_with_discount_offers" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-pro-insights"
            android:summary="@string/notif_with_market_report_summary"
            android:title="@string/notif_with_market_report" />

    </androidx.preference.PreferenceCategory>

    <androidx.preference.PreferenceCategory
        android:key=""
        android:layout="@layout/preferences_notif_title"
        android:title="@string/notif_title_customer_related" />

    <androidx.preference.PreferenceCategory
        android:key="title_email_about_job_requests"
        android:layout="@layout/preferences_category"
        android:title="@string/notif_title_emails_about_job_requests">

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-cust-request-activity"
            android:summary="@string/notif_pros_send_you_bids"
            android:title="@string/notif_request_activity" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-cust-request-reminder"
            android:summary="@string/notif_you_have_upcoming_projects"
            android:title="@string/notif_job_reminders_and_updates" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="email-cust-review"
            android:summary="@string/notif_you_review_pros_you_hired"
            android:title="@string/notif_reviews" />

    </androidx.preference.PreferenceCategory>

    <androidx.preference.PreferenceCategory
        android:key="title_get_push_notifications"
        android:layout="@layout/preferences_category"
        android:title="@string/notif_title_get_push_notif_when">

        <androidx.preference.SwitchPreferenceCompat
            android:key="push-cust"
            android:title="@string/notif_all_push_notif" />
    </androidx.preference.PreferenceCategory>

    <androidx.preference.PreferenceCategory
        android:key=""
        android:layout="@layout/preferences_category"
        android:title="@string/notif_title_text_me_about">

        <androidx.preference.SwitchPreferenceCompat
            android:key="sms-cust"
            android:title="@string/notif_all_text_notif" />
    </androidx.preference.PreferenceCategory>

    <androidx.preference.PreferenceCategory
        android:key="title_email_me_about"
        android:layout="@layout/preferences_category"
        android:title="@string/notif_title_email_me_about">

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-cust-tips"
            android:summary="@string/notif_helpful_tips_inspiration_summary"
            android:title="@string/notif_helpful_tips_inspiration" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-cust-recommendations"
            android:summary="@string/notif_recommendations_summary"
            android:title="@string/notif_recommendations" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-cust-offers"
            android:summary="@string/notif_special_offers_summary"
            android:title="@string/notif_special_offers" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-cust-feedback"
            android:summary="@string/notif_invitations_give_feedback_summary"
            android:title="@string/notif_invitations_give_feedback" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-cust-reminders"
            android:summary="@string/notif_reminders_summary"
            android:title="@string/notif_reminders" />

        <androidx.preference.SwitchPreferenceCompat
            android:key="marketing-cust-other"
            android:summary="@string/notif_other_summary"
            android:title="@string/notif_other" />

    </androidx.preference.PreferenceCategory>
</androidx.preference.PreferenceScreen>

I'm extending from PreferenceFragmentCompat calling this method as mentioned.

@Override
    public void onCreatePreferencesFix(Bundle savedInstanceState, String rootKey) {
        // Select between Provider preferences or Full preferences(Provider + customer)
        User user = Preferences.getUserModel();
        if (user != null) {
            String count = user.getPreviousRequestsCount();
            if (!TextUtils.isEmpty(count) && !count.equals("0")) {
                setPreferencesFromResource(R.xml.pref_notification_full, rootKey);
                return;
            }
        }

        setPreferencesFromResource(R.xml.pref_notification_pro, rootKey);
    }
gregkorossy commented 6 years ago

There are two problems with your XML:

  1. Do not use fully qualified class names (e.g. androidx.preference.PreferenceCategory) because the lib overrides some of the preferences by defining the same class and prefixing it with the appropriate package name. The solution is simple: just remove androidx.preference. everywhere.
  2. The new design language says that categories should have lines between them (hence the line after the first element). You can either remove that first empty category (and make the title of it the title of the toolbar instead) or use a hidden API (see #111) to tell the lib not to render the line below the first element by adding the attribute app:allowDividerBelow="false" to the first category.
andreluizreis commented 6 years ago

@Gericop Now that I removed the androidx.preference. from the XML. I am receiving the old error back: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getPaddingStart()' on a null object reference

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getPaddingStart()' on a null object reference
        at androidx.core.view.ViewCompat.getPaddingStart(ViewCompat.java:1527)
        at com.takisoft.preferencex.PreferenceCategory.onBindViewHolder(PreferenceCategory.java:146)
        at androidx.preference.PreferenceGroupAdapter.onBindViewHolder(PreferenceGroupAdapter.java:381)
        at androidx.preference.PreferenceGroupAdapter.onBindViewHolder(PreferenceGroupAdapter.java:45)
        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6781)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6823)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5752)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6019)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854)
        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230)
        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1557)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517)
        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3641)
        at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4194)
        at android.view.View.layout(View.java:16784)
        at android.view.ViewGroup.layout(ViewGroup.java:5333)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
        at android.view.View.layout(View.java:16784)
        at android.view.ViewGroup.layout(ViewGroup.java:5333)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1702)
        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1556)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1465)
        at android.view.View.layout(View.java:16784)
        at android.view.ViewGroup.layout(ViewGroup.java:5333)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
        at android.view.View.layout(View.java:16784)
        at android.view.ViewGroup.layout(ViewGroup.java:5333)
        at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:444)
        at android.view.View.layout(View.java:16784)
        at android.view.ViewGroup.layout(ViewGroup.java:5333)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
        at android.view.View.layout(View.java:16784)
        at android.view.ViewGroup.layout(ViewGroup.java:5333)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1702)
        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1556)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1465)
        at android.view.View.layout(View.java:16784)
        at android.view.ViewGroup.layout(ViewGroup.java:5333)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
        at android.view.View.layout(View.java:16784)
        at android.view.ViewGroup.layout(ViewGroup.java:5333)
        at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2370)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2079)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1230)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6731)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:777)
        at android.view.Choreographer.doCallbacks(Choreographe
gregkorossy commented 6 years ago

Okay, I see the problem. In your custom layout, you set for the PreferenceCategory, there's no parent of the TextView (id=@android:id/title). For the time being, make sure there's a parent view for the TextView (or just stop using the custom layout, it looks weird to me).

andreluizreis commented 6 years ago

@Gericop, the left padding was solved when I added app:iconSpaceReserved="false" in the Switches. The link that you sent me helped me a lot (https://stackoverflow.com/questions/51518758/preferencefragmentcompat-has-padding-on-preferencecategory-that-i-cant-get-rid). However, it only works for API 21 and above.

When I test with API 16, the paddings still have problems and your fix now doesn't make difference. If I remove the library, the issue continues the same.

I don't understand why the divider moved to the PreferenceCategory and not between the items as it was before.

andreluizreis commented 6 years ago

I saw that the new material design guideline shows the divider between groups and not items. So, if I follow the new guideline, my problem is this extra header title that I have in my app. I need to find a way to remove the divider from them. Because I am adding an extra PreferenceCategory between some groups.

gregkorossy commented 6 years ago

@andreluizreis Like is said before, the divider can be removed: use a hidden API (see #111) to tell the lib not to render the line below the first element by adding the attribute app:allowDividerBelow="false" to the first category. Also, I did not send you that link. The solution is to not use fully qualified names (e.g. androidx.preference.PreferenceCategory) but the class name alone (e.g. PreferenceCategory) and specify app:iconSpaceReserved="false" for the category as well. Note that you might need to update your custom category layout so that the title TextView will be inside a parent view.

andreluizreis commented 6 years ago

@Gericop I did all the modifications that you advised me. It's working most of the screens now. However, the only downside is that to use SwitchPreferenceCompat, I need to keep androidx.preference.PreferenceScreen. If I remove androidx.preference, I will receive an error element is not allowed here. Right now, only API 16 with small screens the padding is wrong. I will try to fix that change the dimensions of padding dynamically.

gregkorossy commented 6 years ago

@andreluizreis That is just a lint error, you can ignore it.

andreluizreis commented 6 years ago

@Gericop, Thanks for helping me solve this problem these days. I will keep my design like the image below. I removed the header titles and I split into two different fragments. Now, I don't have more problems with the dividers.

We can close this issue now. I really appreciate the attention that you gave me. I wish you the best.

screenshot_1539197715

gregkorossy commented 6 years ago

@andreluizreis You can also use nested PreferenceScreens to show the fine-grained settings on separate pages. Check out the sample app for details.