Bug 46807 - 9-patch images should return NinePatchDrawable and not BitmapDrawable
Summary: 9-patch images should return NinePatchDrawable and not BitmapDrawable
Alias: None
Product: Android
Classification: Xamarin
Component: General ()
Version: 7.0 (C8)
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: dean.ellis
Depends on:
Reported: 2016-11-11 15:38 UTC by Jon Douglas [MSFT]
Modified: 2017-03-30 13:13 UTC (History)
6 users (show)

Is this bug a regression?: ---
Last known good build:

9 Patches (1.04 KB, application/x-zip-compressed)
2017-01-10 18:01 UTC, Jon Douglas [MSFT]
Android Studio 9-Patches (1.19 KB, application/x-zip-compressed)
2017-01-10 18:10 UTC, Jon Douglas [MSFT]
Sample Reproduction Project (188.16 KB, application/x-zip-compressed)
2017-03-29 18:52 UTC, Dan Rigby

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Jon Douglas [MSFT] 2016-11-11 15:38:40 UTC

It seems that 9-patch images in Xamarin may have regressed in the recent past. 9-patch images are quite special to the "aapt" tooling and it seems that we do not treat them as special as they need to be.


    var d = Resources.GetDrawable(Resource.Drawable.abc_textfield_default_mtrl_alpha);

d will be a type of: Android.Graphics.Drawables.BitmapDrawable

Now this already sounds really fishy to me because consuming a 9-patch image in this fashion will result in the following error:

11-11 08:18:39.991 I/MonoDroid( 3554): UNHANDLED EXCEPTION:
11-11 08:18:39.995 I/MonoDroid( 3554): Android.Views.InflateException: Binary XML file line #1: Error inflating class EditText ---> Android.Content.Res.Resources+NotFoundException: File res/drawable-v21/abc_edit_text_material.xml from drawable resource ID #0x7f020015 ---> Org.XmlPull.V1.XmlPullParserException: Binary XML file line #24: <nine-patch> requires a valid 9-patch source image

The kicker here is that this is a resource coming directly from the AppCompat v7 library. More specifically it is trying to use a <nine-patch> above:


So the error message is telling us that our <nine-patch> srcs are invalid. Well that seems fair enough because they are invalid in terms of what actual `type` is returned when getting this resource.

Now let's jump into normal Android:

    Drawable d = AppCompatResources.getDrawable(this, R.drawable.abc_textfield_default_mtrl_alpha);
    Class c = d.getClass();

d will be a type of: android.graphics.drawable.NinePatchDrawable

This is correct behavior as it not only returns the correct type back, but it also does not throw an error when accessing a <nine-patch>.


1. Download https://github.com/xamarinhq/app-acquaint
2. Open up App/Acquaint.Native.sln
3. Run the Droid project on API 21 (Note: This is the easiest way to reproduce the failure since Lollipop uses these <nine-patch> items in a drawable as noted above - res/drawable-v21/abc_edit_text_material.xml.
4. You should run into an unhandled exception like above on the following line of the SetupActivity.cs:

            _MainLayout = LayoutInflater.Inflate(Resource.Layout.Setup, null);

If you'd like to test the native Android side for behavior differences, you can simply create a new project with the "Navigation Drawer Activity" template as it will contain this resource. You can then add the two lines of code from above to get the drawable class type.

*Version Information

Xamarin -
Comment 1 dean.ellis 2017-01-09 10:20:45 UTC
I would be interested to see if you can attach the pre and post processed 9 patch file. This would narrow down is this is a problem with the build system or the binding. A post build 9 patch should have an extra chunk in it which identify it as a 9 patch image. 

Comment 2 Jon Douglas [MSFT] 2017-01-10 18:01:45 UTC
Created attachment 19160 [details]
9 Patches

Before: The 9-patch named "abc_textfield_default_mtrl_alpha" found in the `AppData/Local/Xamarin/Xamarin.Android.Support.v7.AppCompat`

After: The 9-patch named "abc_textfield_default_mtrl_alpha" found in the .apk's res/ folder

From my initial comparisons, they seem to be the exact same file(with no added bits). I'm not sure if these are the correct locations to grab them from, but please let me know if there's anything else I can do.
Comment 3 Jon Douglas [MSFT] 2017-01-10 18:10:32 UTC
Created attachment 19161 [details]
Android Studio 9-Patches

Doing the same comparison with Android Studio and the same bitmap, I'm seeing that the pre/post processed 9 patch files are different:

Comment 4 Jon Douglas [MSFT] 2017-01-10 18:18:30 UTC
(In reply to Jon Douglas from comment #3)
> Created attachment 19161 [details]
> Android Studio 9-Patches
> Doing the same comparison with Android Studio and the same bitmap, I'm
> seeing that the pre/post processed 9 patch files are different:
> https://www.screencast.com/t/yCcxWNZEM

Please note that I used the Android Studio paths of:

Before: NinePatchExample\app\src\main\res\ (Inside the application resources)

After: NinePatchExample\app\build\outputs\apk\app-debug\res\ (Inside of the .apk)
Comment 5 dean.ellis 2017-03-29 10:47:00 UTC

I just tried this on the very latest master of monodroid.

New project, updated the nuget packages to the latest. Built then added the following

var d = Resources.GetDrawable (Resource.Drawable.abc_textfield_default_mtrl_alpha);
			if (d == null)
				throw new Exception ("error d should exist");
			var nine = d as Android.Graphics.Drawables.NinePatchDrawable;
			if (nine == null)
				throw new Exception ("error d should be a NinePatchDrawable");

And it works. d is a NinePatchDrawable object.

So it I think this is working. I will see if I can add a runtime test to our suite though.
Comment 6 Jon Douglas [MSFT] 2017-03-29 15:48:21 UTC
Perfect! I think a test would be very helpful here so these types of errors don't pop up every now and then.

Thanks Dean!
Comment 7 Dan Rigby 2017-03-29 18:50:43 UTC
I can still reproduce with the latest stable release tools & NuGet packages. I've created a minimum viable reproduction project and will attach it.
Comment 8 Dan Rigby 2017-03-29 18:52:32 UTC
Created attachment 21029 [details]
Sample Reproduction Project

Open the sample project in Visual Studio, build, deploy, & run on an Android 5.0.x (API 21) emulator or device.
Comment 9 dean.ellis 2017-03-30 13:13:04 UTC
Looks like someone added 

<AndroidResgenExtraArgs>--no-crunch </AndroidResgenExtraArgs>

to the project.


This stops the crunching of .png files as part of the app build process. But crunching is also what processes the 9 patch files. So if you switch it off.. the 9 patch files don't get processed. Hence this issue.

So I'm gonna mark this as answered.