Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cover a 9-PATCH-PNG entirely?

I try to implement a hover effect (effect when button is pressed) through putting a semi transparent PNG file on top of the button background and the button icon. Unfortunatly the button background file is a 9-PATCH-PNG which causes some trouble here: It "swallows" everything on top of its layer and doesnt allow to cover the stretchable areas (the fine light line around) of the nine-patch-png. In other words, the black lines the top and left edge of the 9 PATCH PNG cause not only stretching, but also padding behaviour.

Removing the 9-Patch-Information is not a good solution.

Here u can see my Button. The blue background is a 9 PATCH PNG. The thin light line around the button is unwanted.

alt text

This layer-list is assigned to the button attribute "background":

<?xml version="1.0" encoding="utf-8"?>
<layer-list
  xmlns:android="http://schemas.android.com/apk/res/android">
  <item
    android:drawable="@drawable/home_btn_bg_blue_without_padding" />
  <item>
    <bitmap
      android:src="@drawable/home_icon_test"
      android:gravity="center" />
  </item>
  <item
    android:drawable="@drawable/layer_black_50" />
</layer-list>

Setting the offsets of the layer to "-1" on each border is not valid. Have u guys suggestions?

Update

I tried following, which shall avoid scaling, suggested from here. But didn't work either:

<!-- To avoid scaling, the following example uses a <bitmap> element with centered gravity: -->
<item>
  <bitmap android:src="@drawable/image"
          android:gravity="center" />
</item>

My version (There are still the stretchable areas of the 9-patch-png uncovered):

alt text

<?xml version="1.0" encoding="utf-8"?>
<layer-list
  xmlns:android="http://schemas.android.com/apk/res/android">
  <item
    android:drawable="@drawable/home_btn_bg_blue_hover_without_padding" />
  <item>
    <bitmap
      android:src="@drawable/home_icon_test"
      android:gravity="center" />
  </item>
  <item>
    <bitmap android:src="@drawable/layer_black_100"
          android:height="100dp"
          android:width="100dp"/></item>
</layer-list>

Update 2

Could that work for me? Making Overlaid image transparent on touch in Android?

like image 774
OneWorld Avatar asked Sep 21 '10 16:09

OneWorld


2 Answers

[NeverMind] The internal comments for LayerDrawable.getPadding claim that it takes the padding from the first drawable in the list. If this comment is telling the truth, you could get the behavior you want by putting an arbitrary (perhaps empty) image before your 9 patch in the list.

A quick reading of the code, however, implies that it actually uses the sum of all the item's paddings, which means that there's no way to eliminate your problem using the default LayerDrawable. The statement implies the solution: implement a subclass of LayerDrawable which overrides "getPadding" to return {0, 0, 0, 0}. You may have to initialize your subclass in code rather than by loading an XML layout, but this isn't particularly difficult. [/NeverMind]

Update: The solution above doesn't work, because the problem isn't the padding itself, it's the fact that the default implementation sets the bounds of each image to be the sum of the paddings of the preceding images. In other words, it enforces nesting, which is what most people will want. The proper solution is still to override LayerDrawable, but you replace "onBoundsChange" instead. A complete, tested demo follows:

package com.beekeeper.ninepatchcover;

import android.app.Activity;
import android.graphics.*;
import android.graphics.drawable.*;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.ImageButton;

public class NinePatchCover extends Activity {
  private Drawable mCover0;
  private Drawable mCover1;

  /** Called when the activity is first created. */
  @Override public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    final Drawable button =
      getResources().getDrawable(android.R.drawable.btn_default);
    final Bitmap iconBitmap =
      BitmapFactory.decodeResource(getResources(),
                                   android.R.drawable.ic_menu_mylocation);
    final BitmapDrawable icon = new BitmapDrawable(iconBitmap);
    icon.setGravity(Gravity.CENTER);
    mCover0 =
      getResources().getDrawable(android.R.drawable.title_bar);
    mCover1 =
      getResources().getDrawable(android.R.drawable.title_bar);

    final LayerDrawable unsolved =
      new LayerDrawable(new Drawable[]{button, icon, mCover0});
    final LayerDrawable solved =
      new MyLayerDrawable(new Drawable[]{button, icon, mCover1,}, mCover1);

    ((ImageButton)findViewById(R.id.uncovered)).setBackgroundDrawable(unsolved);
    ((ImageButton)findViewById(R.id.covered)).setBackgroundDrawable(solved);
  }

  class MyLayerDrawable extends LayerDrawable {
    Drawable mCover;

    public MyLayerDrawable(final Drawable[] layers, final Drawable cover) {
      super(layers);
      mCover = cover;
    }

    @Override protected void onBoundsChange(final Rect bounds) {
      super.onBoundsChange(bounds);
      mCover.setBounds(bounds);
    }
  }
}

using the following layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical" android:layout_width="fill_parent"
 android:layout_height="fill_parent"
>
 <ImageButton android:id="@+id/uncovered"
  android:layout_width="fill_parent" android:layout_height="wrap_content" />
 <ImageButton android:id="@+id/covered"
  android:layout_width="fill_parent" android:layout_height="wrap_content" />
</LinearLayout>

A sample screenshot follows:

NinePatchCover screen shot

Update 2:

As requested, here's how you can modify it to initialize a Selector within the code. Replace the initialization of "mCover1" with the following code:

final StateListDrawable sld = new StateListDrawable();
sld.addState(new int[]{android.R.attr.state_pressed},
                 new ColorDrawable(0xffff0000));
sld.addState(new int[]{android.R.attr.state_window_focused},
                 new ColorDrawable(0xff00ff00));
sld.addState(new int[]{},
                 getResources().getDrawable(android.R.drawable.title_bar));
mCover1 = sld;

This will show green in the normal case where the window is focused but the button isn't pressed, red when the button is pressed, and the default drawable (grey) when the window isn't focused. (Try dragging down the "windowshade" notification bar to see the window in it's unfocused state.)

like image 130
beekeeper Avatar answered Oct 17 '22 17:10

beekeeper


I got it working just fine :

res/layout/main.xml

...
<ImageButton
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:src="@drawable/button"
    />
...

res/drawable/button.xml

<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/frame" /> 
    <item>
        <bitmap
            android:src="@drawable/tomato"
            android:gravity="center"
            />
    </item>
</layer-list>

frame.9.png is my nine-patch-png. Tomato is a basic png with transparency around it.

Here is the result : alt text

Removing the transparent part around the tomato (filling up with pink) : alt text

Edit 2: This will make the tomato cover completely the patch-9-png :

<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
    <bitmap
        android:src="@drawable/frame"
        />
    </item>
    <item>
    <bitmap
        android:src="@drawable/tomato"
        />
    </item>
</layer-list>

Another way, with using an ImageButton is that you can use the patch-9-png as the background and the "content" as the src of the button. In that case, you need to set the padding to 0 for that src

like image 42
Matthieu Avatar answered Oct 17 '22 16:10

Matthieu