Cannot inflate android.widget.Toolbar after using PreferenceFragmentCompat [cause: RtlSpacingHelper]

136 Views Asked by At

A FragmentActivity that is correctly inflating android.widget.Toolbar stops doing so after the first deployment of androidx.preference.PreferenceFragmentCompat.

The stacktrace indicates the underlying cause of the failure to be a null instance of android.widget.RtlSpacingHelper:

android.view.InflateException: Binary XML file line #18: Binary XML file line #18: Error inflating class <unknown>

Caused by: android.view.InflateException: Binary XML file line #18: Error inflating class <unknown>

Caused by: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Constructor.newInstance0(Native Method)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
    at android.view.LayoutInflater.createView(LayoutInflater.java:652)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:794)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:734)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:496)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:427)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:378)
    at common.WideFrag.onCreateView(WideFrag.java:168)
    at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3104)
    at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:524)
    at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
    at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1899)
    at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1817)
    at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1760)
    at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:547)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6780)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
    
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.RtlSpacingHelper.setDirection(boolean)' on a null object reference
    at android.widget.Toolbar.onRtlPropertiesChanged(Toolbar.java:825)
    at android.view.View.resolvePadding(View.java:16493)
    at android.view.ViewGroup.resolvePadding(ViewGroup.java:7006)
    at android.view.View.initializeScrollbarsInternal(View.java:5740)
    at android.view.View.<init>(View.java:4988)
    at android.view.ViewGroup.<init>(ViewGroup.java:579)
    at android.widget.Toolbar.<init>(Toolbar.java:291)
    at android.widget.Toolbar.<init>(Toolbar.java:287)
    at android.widget.Toolbar.<init>(Toolbar.java:283)
    ... 23 more
    
WideFrag.onCreateView: InflateException for toolbar.xml=2131558714 (0x7f0d013a): android.view.InflateException: Binary XML file line #18: Error inflating class <unknown>

The Toolbar ('toolbar.xml') is as follows:

<android.widget.Toolbar android:id="@+id/toolbar"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="@dimen/listItemHeightMenu"
        android:background="@color/khaki_tp"
        android:elevation="6dp"
        android:visibility="gone"
        > <!-- NB: Stacktrace refers to this line -->
    </android.widget.Toolbar>

Here is a torn-down minimalist version of the offending Fragment. It contains its own instantiator for the convenience of anyone wishing to recreate the problem. If your app uses android.widget.Toolbar then it will stop working after this Fragment is run but note that androidx.appcompat.widget.Toolbar will still inflate normally!

package yourPackage;
import static java.lang.String.format;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceFragmentCompat;
import yourPackage.R;

/** Prove bug upsetting Toolbar inflation (1/2) */
public class PrefsBug1 extends PreferenceFragmentCompat {
    public static String TAG = "Buggy-tag1";

    public static void newInstance(FragmentActivity A) {
        PrefsBug1 frag = new PrefsBug1();
        int ctr = R.id.fragment_container;
        FragmentManager fm = A.getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(ctr, frag, TAG);
        ft.addToBackStack(TAG);
        String msg;
        try { ft.commit();
            msg=(format("Added '%s' to container %d [%s] (backstack was %d).", TAG, ctr, "R.id.fragment_container", fm.getBackStackEntryCount()));}
        catch (Exception e) { msg = e.getMessage(); }
        Log.d(TAG, msg);
    }

    @Override
    public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
        setPreferencesFromResource(R.xml.pref_debug, rootKey);
    }
}

Here is the sample PrefScreen ('pref_debug.xml'):

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory android:title="A PREFERENCE">
        <EditTextPreference
            android:key="pref_fontScale"
            android:title="pref_fontScale_title"
            android:summary="Override default font scale"
            android:inputType="number|numberDecimal"
            android:defaultValue="0.0" />
    </PreferenceCategory>
</PreferenceScreen>

Finally, in the SDK, it is disappointing that while android.widget.RtlSpacingHelper is not to be found in the android.jar belonging to Android API 33, extension level 3 Platform, RtlSpacingHelper.java nevertheless came with the platform source.

But the real question is how the offending Fragment affects all future behaviour of the Activity and might there be a circumvention?

1

There are 1 best solutions below

0
Bad Loser On

Change your <Toolbar> XML to refer to a subclass:

public class ToolbarX extends android.widget.Toolbar {
    public ToolbarX(Context context) { this(context, null); }
    public ToolbarX(Context context, AttributeSet attrs) { this(context, attrs, R.attr.toolbarStyle); }
    public ToolbarX(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr, 0); }
    @Override public void onRtlPropertiesChanged(int layoutDirection) {}
}

(Pure unadulterated programming filth often saves the day)