15

Android Studio (using SDK 19, 21 or 22) shows an error that Eclipse ADT (using SDK 19) does not:

Error:9-patch image D:\Workspaces....\res\drawable-hdpi\btn_bg_common_press.9.png malformed. Error:Frame pixels must be either solid or transparent (not intermediate alphas). - Found at pixel #4 along top edge.

Or another error:

Error:Ticks in transparent frame must be black or red.

both within aapt

Error:Error: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'E:\Android\sdk-Android-Studio\build-tools\19.1.0\aapt.exe'' finished with non-zero exit value 42

btn_bg_common_press.9.png

Example of file is above, but there are 20+ such files that worked well.

How do I make Android Studio or Gradle skip this error and not fail without having to modify those files one-by-one?

If it is not possible with Gradle, what command-line tool could I use to replace all transparent pixel with non-transparent?

The build.gradle file for the application module (where resources are) is below.

I have tried both with SDK 19 and SDK 21 and build tools 19.1, 21.1.2, 22.

A similar issue on AOSP, Issue 159464: Android studio: mergeDebugResources FAILED when importing Eclipse project.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.1.+'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

//---
task wrapper(type: Wrapper) {
    gradleVersion = '2.2.1'
}

apply plugin: 'com.android.application'

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile project(':afinal')
    compile 'com.android.support:appcompat-v7:19.0.+'
    //compile 'com.android.support:appcompat-v7:21.0.+'
}

//---
android {
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
    }

    compileSdkVersion 19
    buildToolsVersion "19.1.0"
    //compileSdkVersion 21
    //buildToolsVersion "21.1.2"
    //compileSdkVersion Integer.parseInt(project.COMPILE_SDK_VERSION)
    //buildToolsVersion project.BUILD_TOOLS_VERSION

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
            zipAlignEnabled true
            //signingConfig signingConfigs.release
        }
        debug {
            zipAlignEnabled true
        }
    }

    lintOptions {
        //checkReleaseBuilds false
        // Or, if you prefer, you can continue to check for errors in release builds,
        // but continue the build even when errors are found:
        abortOnError false // false also required by https://wiki.jenkins-ci.org/display/JENKINS/Android+Lint+Plugin
    }
}//android

Android Gradle plugins sources are at https://android.googlesource.com/platform/tools/build/+/master.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Paul Verest
  • 60,022
  • 51
  • 208
  • 332

4 Answers4

8

How automatically solve the issue (without checking all images in different resolutions)

Not possible. Since you want the .png to behave like a nine-patch (expand along the edges, not stretch the whole bitmap), you're going to have to format them as such ergo you have to modify the files.

Proposed alternative

Since the shape is so simple you'd be better off deleting all variants of this file (saving space, time and headache in the process) and create /res/drawable/btn_bg_common_press.xml drawable with following contents:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <corners android:radius="16dp"/>
    <solid android:color="#ccc"/>
    <stroke
        android:color="#0c0"
        android:width="2dp"/>
</shape>

You can use dimen and color resources instead of hardcoded values. Additionally you might want to add the padding element inside shape

<shape...>
    <padding
        android:top="8dp"
        android:left="8dp"
        android:bottom="8dp"
        android:right="8dp"/>
</shape>

and/or wrap the shape element inside an inset element.

<inset
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:insetTop="8dp"
    android:insetLeft="8dp"
    android:insetBottom="8dp"
    android:insetRight="8dp">
    <shape...>
</inset>

If you do that, the drawable will have implied padding and you won't have to set it when styling widgets. Since you have multiple state drawables for the button I suggest you convert all of them to XML to make sure the line thickness and corners match.

Despite the root element's name the <shape> actually inflates to GradientDrawable (meaning you can fill it with gradient instead of solid color). Review GradientDrawable docs for all its options. Do not ever use ShapeDrawable programmatically, it just doesn't work.

Analyzing 9-patches

Consider the three following enlarged images.

Nine-patch samples

Candidate #1 is a nine-patch. It has reserved 1px on each side for stretch and padding specification. This one will behave as an ordinary bitmap when stretched. If the width will be greater than height, so will the border thickness on sides. The border will scale proportionally.

Candidate #2 is also a nine-patch and is actually saying it's going to stretch everything besides 1px border and will have implied padding of 3px on each side. It will have a nice crisp 1px border when stretched.

Candidate #3 is NOT a nine-patch. It scales the same way as #1.

Now let's take a look at enlarged version of the image you included in OP:

Enlarged image from OP

As you can see it's not a nine-patch, so it won't get interpreted as one and the build tools are kind enough to warn you of that. Even if older build tools were more forgiving and added transparent 1px on each side for you, the result would have behaved like an ordinary bitmap, meaning when stretched it would look like specimen A instead of expected result specimen B.

Comparison of a bitmap and a nine-patch

Here's more reading on nine-patches. This explains what the extra 1px on each side is used for.

Eugen Pechanec
  • 37,669
  • 7
  • 103
  • 124
  • those files are correct nine-patch images that have worked well before with Eclipse build. Still this is nice piece of info. But I regard this as build issue. – Paul Verest Mar 11 '15 at 09:00
  • @PaulVerest It's not a build issue, your resources are in bad format. Please see the updated answer, I've added a whole lot about nine-patches. – Eugen Pechanec Mar 11 '15 at 09:33
  • wow, that is great intro into nine-patch (that I didn't know). Now need to check with designer... Meanwhile build is to pass. – Paul Verest Mar 11 '15 at 10:01
  • You can try [Android Asset Studio](http://romannurik.github.io/AndroidAssetStudio/nine-patches.html) to convert current bitmaps to nine-patches. To avoid doing it one by one, try taking your highest density bitmap (preferably xxhdpi) and select corresponding value in "Source density" field. – Eugen Pechanec Mar 11 '15 at 10:42
  • @PaulVerest I hope I helped at least some bit. Which approach did you choose in the end? – Eugen Pechanec Mar 17 '15 at 07:52
  • I had to review .9.png files usage, then delete or correct them to be nine-patch (actually just quickly drawing 1px line above and on the line). I still think that is bad of gradle to force stricter rules and fail over them. – Paul Verest Mar 17 '15 at 08:05
  • @PaulVerest Personally I think it's best if gradle doesn't make asumptions about the image, you might end up with something you didn't want. XML drawable is really the way to go in this case. I even managed to backport material button with the growing shadow when pressed (but without ripples obviously). And you save some space. – Eugen Pechanec Mar 17 '15 at 08:17
  • The situation was that images came from designer, were used by other team members and when I came to add gradle build I ran into set of problems including this one. Advice would be having gradle CI build running from the beginning so anything that would not fit would be discovered earlier. – Paul Verest Mar 18 '15 at 02:23
7

The example given is not a 9 patch image, as it has been told.

If you don't want to modify each resource in order to transform it to a valid 9 patch resource, you could try removing ".9" in the resource's name. This way, the behavior should be the same, and you won't get build errors.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jofre Mateu
  • 2,390
  • 15
  • 26
  • I wonder, why I didn't try this one at first! I renamed all the 9PNG files just have `.png` extension and it solved the build error. – Randika Vishman Dec 02 '15 at 05:09
  • I have seen this solution many times by other people, but I haven't tried removing the .9 because I thought I would loose the 9patch effect. The unique thing about your answer is that you reassured me that I wouldn't loose the effect. Thank you, your solution works. – kc ochibili Jun 13 '17 at 08:19
3

Android now has two PNG crunchers, the AAPT one and a Java one. To ignore the PNG error in build process, you can force Gradle build to use the old AAPT by setting the below line in your build.gradle file:

android.aaptOptions.useAaptPngCruncher = true

Still this may give you the same error, as the AAPT tool from the latest SDK might be checking for this error too. So you have two options:

  • Find an older AAPT, for example, <SDK path>/build-tools/17.0.0/aapt, overwrite your current SDK AAPT with this old one. Your build issues may now be resolved. If this doesn't work, try the below option.

  • Write a simple Bash or Perl script, name it "aapt" and put it in your current SDK build-tools folder. This script will call the old version 17 AAPT for your *9.png, and use the new AAPT for everything else. See this Stack Overflow answer for a sample script.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ashoke
  • 6,441
  • 2
  • 26
  • 25
  • Thank you for ideas. However gradle and Android Studio do not recognize `useAaptPngCruncher` and it is missing for `aaptOptions` list in http://tools.android.com/tech-docs/new-build-system/user-guide . See http://stackoverflow.com/questions/28934333/how-can-i-use-android-aaptoptions. Posssibly it was present in some beta and absent now. Secondly, I use Eclipse with SDK 19 and build tools 19.1 and it works fine. So the problem is not with `aapt` itself but how it is used (from gradle build) – Paul Verest Mar 11 '15 at 02:43
  • Using `useNewCruncher false` does not help either. – Paul Verest Mar 11 '15 at 02:53
  • @PaulVerest seems like Eclipse is probably ignoring the error and continuing the build process. Gradle is stopping the build...so a workaround would be to simply wrap the aapt into another shell script named 'aapt' and let this proxy script call the original aapt but always return success, so the gradle build can continue the build process. – ashoke Mar 12 '15 at 00:07
  • 1
    I get the following error `Error:(18, 0) No such property: useAaptPngCruncher for class: com.android.build.gradle.internal.dsl.AaptOptions_Decorated` – Shravan May 03 '15 at 22:14
0

To solve this problem ensure that you fill all edges with black or red points. This solved my problem for all SDK versions. If you don't want to scale vertically or horizontally or both of them, just fill all edges. In this case the image doesn't scale.

Without error

With malformed error

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sancho
  • 598
  • 2
  • 8
  • 22