Android Tutorial Nine patch images tutorial

Erel

Administrator
Staff member
Licensed User
Android supports a special format of PNG images that can be resized by replicating specific parts of the image.
This images also include padding information.
These images are named nine-patch images.

You can read more about this format here: Canvas and Drawables | Android Developers

For example the following three labels use the same background nine-patch image:



Android SDK includes a tool named draw9patch.bat that can help you with building and modifying such images. This tool is available under: <android-sdk>\Tools
You can read more about it here: Draw 9-patch | Android Developers

The following steps are required to use a nine patch image as a view background:
- Copy the image to <project folder>\Objects\res\drawable
- Set the image to be read-only (otherwise it will be deleted during compilation).
- Add the following sub to your code (requires Reflection library):
B4X:
Sub SetNinePatchDrawable(Control As View, ImageName As String)
   Dim r As Reflector
   Dim package As String
   Dim id As Int
   package = r.GetStaticField("anywheresoftware.b4a.BA", "packageName")
   id = r.GetStaticField(package & ".R$drawable", ImageName)
   r.Target = r.GetContext
   r.Target = r.RunMethod("getResources")
   Control.Background = r.RunMethod2("getDrawable", id, "java.lang.int")
End Sub
For buttons you can use this sub which creates a StateListDrawable from two nine-patch images:
B4X:
Sub SetNinePatchButton(Btn As Button, DefaultImage As String, PressedImage As String)
   Dim r As Reflector
   Dim package As String
   Dim idDefault, idPressed As Int
   package = r.GetStaticField("anywheresoftware.b4a.BA", "packageName")
   idDefault = r.GetStaticField(package & ".R$drawable", DefaultImage)
   idPressed = r.GetStaticField(package & ".R$drawable", PressedImage)
   r.Target = r.GetContext
   r.Target = r.RunMethod("getResources")
   Dim sd As StateListDrawable
   sd.Initialize
   sd.AddState(sd.State_Pressed, r.RunMethod2("getDrawable", idPressed, "java.lang.int"))
   sd.AddCatchAllState( r.RunMethod2("getDrawable", idDefault, "java.lang.int"))
   Btn.Background = sd
End Sub
Now you should use this sub to set the views backgrounds:
B4X:
Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("1")
   SetNinePatchDrawable(Label1, "label_bg")
   SetNinePatchDrawable(Label2, "label_bg")
   SetNinePatchDrawable(Label3, "label_bg")
End Sub
Tips
- Don't modify the image files located under res\drawable directly with the draw9patch tool. It removes the read-only attribute and then the image will be deleted.
- The image name is case sensitive.
- After adding a new image you should clean the project by choosing Tools - Clean Project. This causes a generated file (R.java) to be recreated and include the new resources.

An example is attached.
 

Attachments

joseluis

Active Member
Licensed User
This is great, many many thaks. :icon_clap:

FYI, here is a great & vast collection of free 9patch images.
I have decided to start a blog to share some great 9 patch pngs used for the android platform. I have had trouble to find good quality 9 patch pngs in the past so thought this would be a good place to share some of my work.

Enjoy them all free of use, including in commercial android products.
Credits by offering a link to this blog would be greatly appreciated.

Do not use for your own website or blog without permission.
Not for resale/redistribution in packages without permission.
 

reeZZer

Member
Licensed User
Hello,
I'm keep getting an Error after putting the Ninepatch Images into the /res/drawable and try to Clean Project or Debug:


EDIT:
I named the image "nine-pressed.png" instead of "nine_pressed.9.png".
Solved.


Thanks for the Tutorial ! :)
 
Last edited:

joseluis

Active Member
Licensed User
Delete that Thumbs.db file from there, and/or close the program (probably the explorer window) that is using it.
 

joseluis

Active Member
Licensed User
I've created a new sub for setting up buttons supporting different images for Default, Pressed, Disabled, Focused and DisabledFocused states. You can pass an empty string for any button state you don't have an image for.

B4X:
Sub SetNinePatchButton2(Btn As Button, DefaultImage As String, PressedImage As String, _
   DisabledImage As String, FocusedImage As String, FocusedDisabledImage As String)

   Dim r As Reflector
   Dim package As String
   package = r.GetStaticField("anywheresoftware.b4a.BA", "packageName")
   
   r.Target = r.GetContext
   r.Target = r.RunMethod("getResources")
   Dim sd As StateListDrawable
   sd.Initialize
   
   Dim idDefault, idPressed, idFocused, idDisabled, idFocusedDisabled As Int
   
   If FocusedDisabledImage.Length > 0 Then
      idFocusedDisabled = r.GetStaticField(package & ".R$drawable", FocusedDisabledImage)
      sd.AddState2(Array As Int (sd.State_Disabled, 0x0101009c), r.RunMethod2("getDrawable", idFocusedDisabled, "java.lang.int"))
   End If
   If DisabledImage.Length > 0 Then
      idDisabled = r.GetStaticField(package & ".R$drawable", DisabledImage)
      sd.AddState(sd.State_Disabled, r.RunMethod2("getDrawable", idDisabled, "java.lang.int"))
   End If
   If PressedImage.Length > 0 Then
      idPressed = r.GetStaticField(package & ".R$drawable", PressedImage)
      sd.AddState(sd.State_Pressed, r.RunMethod2("getDrawable", idPressed, "java.lang.int"))
   End If
   If FocusedImage.Length > 0 Then
      idFocused = r.GetStaticField(package & ".R$drawable", FocusedImage)
      sd.AddState(0x0101009c, r.RunMethod2("getDrawable", idFocused, "java.lang.int"))
   End If
   If DefaultImage.Length > 0 Then
      idDefault = r.GetStaticField(package & ".R$drawable", DefaultImage)
      sd.AddCatchAllState( r.RunMethod2("getDrawable", idDefault, "java.lang.int"))
   End If
   
   Btn.Background = sd
End Sub
 

COBRASoft

Active Member
Licensed User
Erel, how can we 'remove' the nine patch image background from e.g. a panel? Let's say, I want the background to be visible for some time and at a certain point, I need to remove it again (not replacing by another one or a transparent one). Any ideas?
 

COBRASoft

Active Member
Licensed User
Hey,

I want no drawable at all :). Kinda like 'reset' the background to original state. Perhaps panel.background = null will do the trick, have to try :).

Greetings,
Sigurd
 

joneden

Active Member
Licensed User
Hi Erel,

Just been trying to work on this for an icon button on a panel and couldn't get it to find the file. Anyway then I finally noticed the line that reads:

After adding a new image you should clean the project by choosing Tools - Clean Project. This causes a generated file (R.java) to be recreated and include the new resources.
mmm. Methinks that line should be in big font and hightlighted red as it works perfectly now :)

Regards,

Jon
 

TheJinJ

Active Member
Licensed User
Thought I'd just post this in case anyone else makes the same schoolboy error as myself! Had me scratching my head for a few minutes.....

When referencing the 9patch image (eg. myimage.9.png), you don't pass '.9.png'. I know it's well documented in this thread that .png is not included but I was using 'myimage.9' and I couldn't see the mistake for looking at it until I went for a coffee and came back....obvious but worth mentioning!

:sign0104:
 
Top