Using <include> tag with ?attr/myAttr
Asked Answered
G

3

8

I'm trying to include different layouts in my View depending on the parent Theme.

Following the idea:

attrs.xml

<attr name="themeLayout" format="reference" />

styles.xml

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="themeLayout">@layout/layout_a</item>
</style>

<style name="AppThemeSecond" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="themeLayout">@layout/layout_b</item>
</style>

activity.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <include layout="?attr/themeLayout" />

</RelativeLayout>

When I run the Code above I'll get the following Exception:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.package/com.my.package.MainActivity}: android.view.InflateException: You must specifiy a valid layout reference. The layout ID ?attr/themeLayout is not valid.
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
            at android.app.ActivityThread.access$800(ActivityThread.java:151)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5254)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
     Caused by: android.view.InflateException: You must specifiy a valid layout reference. The layout ID ?attr/themeLayout is not valid.
            at android.view.LayoutInflater.parseInclude(LayoutInflater.java:866)

What am I doing wrong? Is it possible to do this at all?

Note #1: I set the Theme of my MainActivity to android:theme="@style/AppTheme"

Note #2: In the Design-View of the Layout Editor everything works as expected. If I switch the Theme, the include gets updated properly.
See the following Screenshots as well:

preview design tab android studio

Gnat answered 15/5, 2015 at 12:21 Comment(3)
Never thought on doing this way... hope somebody answers because it is a cool problem you´ve foundBabe
This way you can not include layout base on theme may be have to create and load theme base layout.Mcgray
@HareshChhelana I'm not sure what you're trying to say. It think the weird part is that the Layout-Editor can resolve the attribute, but the Code at runtime isn't able to do so as well.Gnat
P
4

Unfortunately this is not possible but I really like the idea. I have tracked the flow of LayoutInflater and it requieres the layout attribute to be TypedValue.TYPE_REFERENCE which means ?attr is not allowed. They even left a comment in the method to explain.

public int getAttributeResourceValue(int idx, int defaultValue) {
    int t = nativeGetAttributeDataType(mParseState, idx);
    // Note: don't attempt to convert any other types, because
    // we want to count on aapt doing the conversion for us.
    if (t == TypedValue.TYPE_REFERENCE) {
        return nativeGetAttributeData(mParseState, idx);
    }
    return defaultValue;
}

android.content.res.XmlBlock.java:385

Basically you have done nothing wrong -- the inflation in Preview works differently which caused the confusion.

Poised answered 15/5, 2015 at 14:14 Comment(3)
Thanks for the great answer. There's just one more thing I don't get: What is format="reference" if not a TypedValue.TYPE_REFERENCE?Gnat
That only tells you that <item name="themeLayout"> has to be of TYPE_REFERENCE. If you parse the layout XML and find a ?attr/themeLayout it is a TYPE_ATTRIBUTE.Poised
This makes sense. Thank you for the explanation. Luckily reVerse' approach works for me and it's nearly similiar to the <include /> tag stuffGnat
S
4

Since Lamorak explained everything in detail in his "in-depth" answer I guess it's save to say that there isn't any solution with the <include /> tag for your problem.
So following a different approach that worked in my test project:

#1 - Replace <include /> with <ViewStub />

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ViewStub
        android:id="@+id/myStub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inflatedId="@+id/myInflatedStub"
        android:layout="?attr/themeLayout" />

</RelativeLayout>

#2 - Inflate the ViewStub

ViewStub myStub = (ViewStub) findViewById(R.id.myStub);
myStub.inflate();

Any drawbacks with this method? Well you won't see the ViewStub-Layout in your Design-Tab and using the <merge /> tag won't work.

Shaikh answered 15/5, 2015 at 17:58 Comment(0)
D
1

After some research, I found this problem-"has been fixed in Android23. But bellows, exception will be thrown.

Donets answered 7/6, 2016 at 8:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.