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.
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):
<?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_w开发者_JAVA百科ithout_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?
[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:
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.)
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 :
Removing the transparent part around the tomato (filling up with pink) :
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
I got a full overlay to work by manipulating the nine-patch. Instead of leaving the bottom and right sides (content) empty, try filling them in completely with black pixels.
I think there are two solutions to this issue.
Solution 1:
When you say "hover effect", do you mean while the button is being pressed down? If that's the case, then what you want to do use use a selector a.k.a. state list for the button background, where the selected state is different than your normal image:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@android:color/black" /> <!-- pressed -->
<item android:drawable="@drawable/home_btn_bg_blue_hover_without_padding" /> <!-- default -->
</selector>
Then set the button background to that state list drawable XML.
Solution 2:
While you haven't posted your raw nine patch PNG, I suspect that you can remove the right and bottom markings denoting the "padding box", while keeping the left and top markings, denoting the "Stretchable area" as documented in the 2D Graphics Doc. This will retain the image's current stretching behavior, but remove any padding that is intrinsic to the image. You can then add or remove padding as desired to the inner views to get the desired display.
精彩评论