33

Problem

I want to override a layout file from android namespace, e.g. R.layout.popup_menu_item_layout (which is referenced from code as com.android.internal.R.layout.popup_menu_item_layout). By saying override, I assume declaring an xml file in the project which would be prioritized over the layout that framework owns.

Note, this is just an example layout, so the question concerns to each layout that's present in sdk/platforms/android-XX/data/res/layout directory.

What I've tried

tools:override

There's an undocumented tools:override tag available, which overrides specific resources. See this answer for an example, which overrides values from Design Support Library, not from Android framework.

Applying tools:override="true" to the root tag of the layout won't take effect.


XML layout references - refs.xml

As described in this post, declaring a refs.xml file in /values/ directory with following content:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item type="layout" name="activity_main">@layout/activity_second</item>
</resources>

will refer to activity_second.xml once activity_main.xml is used. There's an answer that suggests using this technique in order to substitute Snackbar's layout.

This also won't take effect.

Question

Is there any legitimate way to override/substitute a layout file from android package?

azizbekian
  • 60,783
  • 13
  • 169
  • 249
  • 2
    If you are the one inflating the layout, just modify your code to use the replacement. If you are trying to replace the layout that something else is inflating (e.g., `PopupMenu`), then AFAIK that is not possible. You can try forking that class and creating your own version that then uses your replacement layout. – CommonsWare Jun 16 '17 at 13:00
  • 1
    Usually those APIs are hidden far behind the public ones ( `PopupMenu` uses `SomeClassA`, which uses `SomeClassB`, which inflates this layout), which means that I have to stick with multi-level reflection, hoping that I can find enough seams to achieve the desired effect. Thanks for the reply, though. – azizbekian Jun 16 '17 at 13:03
  • "which means that I have to stick with multi-level reflection" -- or, fork more classes. – CommonsWare Jun 16 '17 at 13:13
  • By saying *"fork"* you mean creating appropriate package (e.g. `com/android/internal/view/menu`) within my project and add corresponding Java class with changed behavior? – azizbekian Jun 16 '17 at 13:15
  • No, by "fork" I mean copying the classes into your own Java package (`com/azizbekian/menu/`), modifying them, and using the modified versions with your modified resources. – CommonsWare Jun 16 '17 at 14:21
  • What if those classes have package private dependencies? – azizbekian Jun 16 '17 at 14:22
  • 2
    You wind up forking those too. Eventually, either you get a complete set of classes or you hit something that is unforkable (e.g., `android.view.View`, things with `native` methods). In the latter case, you move along and solve your overall problem in some other fashion. So, for example, I forked [the entirety of Android 7.0's network security configuration](https://github.com/commonsguy/cwac-netsecurity) this way, jettisoning bits that could not be used on older devices, as part of creating a backport. – CommonsWare Jun 16 '17 at 14:24
  • 2
    Have you tried simply copying the layout file into your code base? The name has to remain the same, also you need to be careful if you have multiple layout sizes to copy those as well. You'll also have to resolve all the dimension errors that come along the way. I remember this worked for me when I needed to modify a layout in activity from google cast support library (media controller), this way I was able to get rid of the 2 pane layout and change colors, branding.. everything. – Andrej Jurkin Jun 19 '17 at 15:52
  • @Andrej Jurkin, yes, I have tried. That doesn't work, because `com.android.internal.R != com.mypackage.R`. It seems to me, that it's somehow achievable if you try to override a layout from another library (that is attached to the project), see the `tools:override` use-case I mentioned in the question. But that technique is not applicable for layout from framework itself. – azizbekian Jun 20 '17 at 06:29
  • Afaik only way to do this is to put a runtime resource overlay on the android package, and for that you need to be an phone manufacturer ;) – JohanShogun Jun 22 '17 at 15:13
  • @Andrey Jurkin, yes, this technique can be a savior - if it wasn't that I wouldn't be able to allow text wrapping for titles in the NavigationView. I already thought that I'll have to resort to the CommonsWare's approach of forking all the classes involved. But luckily simple adding of `design_navigation_menu_item` with `maxLines=2` attribute for the `CheckedEditText` solved the problem. Nevertheless, I still think it does not smell good. – Varvara Kalinina Jun 29 '17 at 14:34
  • @Varvara Kalinina, the point is, that `design_...` is not from framework itself, it's just a library that we are attaching to our projects, thus we have a bit possibilities to affect those resources (as mentioned in the question). But that doesn't seem to work for framework resources. – azizbekian Jun 29 '17 at 18:49
  • @azizbekian Yes, I understand that this solution is not applicable to your problem – Varvara Kalinina Jun 30 '17 at 09:53
  • 1
    Have you try to add in your project a module library with package name "com.android.internal", that will generates your own com.android.internal.R, and despite how compilation solve dependency conflict, maybe you'll be able to override layouts links ? – smora Aug 15 '17 at 10:42
  • @smora, nice try! Haven't thought about that method. Unfortunately, it does **not** work. Thank you for a nice approach suggestion! – azizbekian Aug 28 '17 at 09:06
  • sorry that couldnt work.. next step could be to include your own gradle task in build process that will rewrite R references table, maybe you could substitute id with one of yours. I'd never tried, its certainly not as simple as that, with no guarantee of success, and begin to going out of "legitimate" scope ! – smora Aug 29 '17 at 10:20

4 Answers4

21

I know this is an old question but I also wanted to override a library layout with my own, here's how I did it.

The layout in question was called design_bottom_navigation_item

In refs.xml I added the following:

<resources xmlns:tools="http://schemas.android.com/tools">
    <item name="design_bottom_navigation_item" type="layout" tools:override="true">@layout/bottom_navigation_item</item>
</resources>

There are 4 parts to this which I'll explain.

  • Name: This is the name of the layout you want to override
  • Type: The type of resource you are trying to override, in this case a layout.
  • tools:override: This is how you tell Android Studio to override the library layout with your own.
  • Value: This is where you specify what resource you want to use instead.

You can do this with any resource type this way.

Scott Cooper
  • 2,779
  • 2
  • 23
  • 29
  • 2
    The layout you are referring to is **not** from framework, it's from a library, that you are attaching to your project (to be precise - design support library). Unfortunately your answer is in no help to me, because by the approach that you mention you cannot override e.g. `R.layout.popup_menu_item_layout`. Your solution is already pointed out in the question itself. Thanks. – azizbekian Dec 12 '17 at 17:00
  • Works perfect for the substitution of a buggy xml layout in a third party lib! – isabsent Dec 02 '20 at 06:57
0

What is that you're trying to do?

If the idea to only replace how the menu-item will look like, you can try the following:

  1. Create a custom MyMenuAdapter extends MenuAdapter
  2. Override the getView method to return the view from your adapter.
gvaish
  • 9,374
  • 3
  • 38
  • 43
  • See in question: `Note, this is just an example layout, so the question concerns to each layout that's present in sdk/platforms/android-XX/data/res/layout directory.` Unfortunately your answer is not even close. – azizbekian Oct 17 '17 at 18:18
  • Oh... each file. I missed that. So, basically, you're looking to replace the internal layout file used. That is known as *custom firmware* and cannot be done without recompiling the OS. If you're looking it just for your app - you'll have to replace all the files, recompile and use the new code in your app, and don't forget to use a different package name, e.g.: `com.myapp.repackaged.com.layout.internal` of whatever – gvaish Oct 18 '17 at 05:43
0

You are trying to customise your sdk on the application itself, at runtime.

That's just not how it works.

If you use an SDK on your project(on any technologies), and you need to modify some behavior, you will tweak this SDK and after that, compile your project with this news customized version.
Trying to modify it at runtime is not a good idea.
You will face multiple issues (retro compatibility, security trigger, TREBLE incompatibility , dependency issue, etc)

You have 4 possibilities to do what you want:

  1. Make your own android rom where you will apply your modification
  2. Copy the resources you need to modify on a fake xmlObject with the tag, after the onPostCreate of your application, you will be able to modify the when inflation. You can generalize this behavior and it will simulate an sdk overlay.
  3. Make your own sdk :)
  4. Multi-level reflection, but, no way you succeed with a stable version

Of course, none of this solutions is applicable for a public app.

Community
  • 1
  • 1
Antoine Draune
  • 323
  • 1
  • 3
  • 12
0

don't know your issue have fixed or not but simple solution for this is create new layout that is same layout name of framework (in this case is popup_menu_item_layout). Then go to android google source to copy xml content popup_menu_item_layout

So you can custom anything u want. But remember don't change any id of views.

Xiao King
  • 369
  • 1
  • 13