Android Tutorial Nine patch images tutorial

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:

SS-2012-01-22_12.19.33.png


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

  • NinePatchExample.zip
    7.4 KB · Views: 2,674

kamalkishor

Member
Licensed User
Longtime User
Problem downloaded multiple images using httpjob

Hi,

i want to download multiple images from url with httpjob so my problem is that ,i downloaded only one image , Any help or suggestion is appriciated My code is

Sub ListviewSub_cat_ItemClick (Position As Int, Value As Object)
Dim SubCat_id As Int
Dim jsonstring As String

'Cat_id = listview_category.GetItem(Position)
SubCat_id = listSub_cat_id.Get(Position)
job3.Initialize("job3",Me)

'jsonstring = "http://siliconsoftwares.in/store/storeproductjson.php?cat_id=223&format=json"
jsonstring = "http://siliconsoftwares.in/store/storeproductjson.php?cat_id=97&format='json'"
job3.PostString(jsonstring, " ")



End Sub

Sub JobDone (job As HttpJob)
If job.Success = True Then

If job.JobName = "Job1" Then
FillSimpleData(job.GetString)
LblAllCatogry.Visible = True
LblsubCatagory.Visible = False
svProduct.Visible = False
PnlLogo.Visible = False
LblProducts.Visible = False

End If

If job.JobName = "job2" Then

listview_category.visible=False
FillSub_catid(job.GetString)
LblAllCatogry.Visible = False
LblsubCatagory.Visible = True
ListviewSub_cat.Visible = True
svProduct.Visible = False
PnlLogo.Visible = False
LblProducts.Visible = False

End If

If job.JobName = "job3" Then
LblAllCatogry.Visible = False
ListviewSub_cat.Visible = False
FillstoreProduct(job.GetString)
LblsubCatagory.Visible = False
btnaddtocart.Visible = True
svProduct.Visible = True
PnlLogo.Visible = True
LblProducts.Visible = True
BtnSearch.Visible = False
EdttxtSearchbox.Visible = False


End If

If job.JobName = "job5" Then
' job.Download(bmImageUrl)
Dim imageProduct As ImageView
imageProduct.Initialize("imageProduct")
Dim imagetop As Int
imagetop = 1

svProduct.Panel.AddView(imageProduct,5%x,imagetop*1.5%y,30%x,21.3%y)
imageProduct.SetBackgroundImage(job.GetBitmap)

imagetop = imagetop +1
End If
End If
job.Release

End Sub

Sub FillstoreProduct(TempResultstringP)
Dim Productid_start As Int
Dim Productname_start As Int
Dim productPrice_start As Int
Dim productModel_start As Int
Dim ProductQuantity_start As Int
Dim tempstringP As String
Dim index_counterP As Int
Dim Complete_endP As Int
Dim Pic_start As Int

Dim Productid_db As String
Dim ProductName_db As String
Dim ProductPrice_db As String
Dim ProductModel_db As String
Dim ProductQuantity_db As String

Dim ImageStrng As String
Dim StringUtl As StringUtils
Dim bmImage As Bitmap
Dim ProductInputstream As InputStream

myJson.Initialize(TempResultstringP)
first_map.Initialize
first_map = myJson.NextObject
tempstringP = first_map.Get("items")
index_counterP=0
Dim i As Int
i = 0
Log("tempstring")
Log(tempstringP)

Do While index_counterP<>(-1)

Dim labelProductname As Label
labelProductname.Initialize("labelProductname")
labelProductname.TextColor = Colors.RGB(0,104,139)

Dim job5 As HttpJob


Dim lblProdctPrice As Label
lblProdctPrice.Initialize("lblProdctPrice")
lblProdctPrice.TextColor = Colors.Black

Dim lblProdctModel As Label
lblProdctModel.Initialize("lblProdctModel")
lblProdctModel.TextColor = Colors.Black


Dim bgImageview As String
Dim bmImageview As Bitmap

Dim pnlProduct As Panel
pnlProduct.Initialize("pnlProduct")
pnlProduct.Color = Colors.Transparent

Dim imageProduct As ImageView
imageProduct.Initialize("imageProduct")

Dim LblPrice As Label
LblPrice.Initialize("LblPrice")
LblPrice.TextColor = Colors.Black

Dim LblModel As Label
LblModel.Initialize("LblModel")
LblModel.TextColor = Colors.Black

Dim PnlLine As Panel
PnlLine.Initialize("PnlLine")
Dim bgPnlLine As String
Dim bmPnlLine As Bitmap

bgPnlLine = "line.jpg"
bmPnlLine.Initialize(File.DirAssets,bgPnlLine)

index_counterP = Complete_endP
Complete_endP =tempstringP.IndexOf2("}",index_counterP+1)

Productid_start = tempstringP.IndexOf2("products_id",index_counterP)
Productname_start = tempstringP.IndexOf2("products_name",index_counterP)
productPrice_start = tempstringP.IndexOf2("products_price",index_counterP)
productModel_start = tempstringP.IndexOf2("products_model",index_counterP)
ProductQuantity_start = tempstringP.IndexOf2("products_quantity",index_counterP)
Pic_start = tempstringP.IndexOf2("products_image",index_counterP)

Productid_db = tempstringP.SubString2(Productid_start+12,Complete_endP)
ProductName_db = tempstringP.SubString2(Productname_start+14,Productid_start-2)
ProductPrice_db = tempstringP.SubString2(productPrice_start+15,Productname_start-2)
ProductModel_db = tempstringP.SubString2(productModel_start+15,productPrice_start-2)

' ImageByte = StringUtl.DecodeBase64(tempstringP.SubString2(Pic_start+19,ProductQuantity_start-2))
' ProductInputstream.InitializeFromBytesArray(ImageByte,0,ImageByte.Length)
' bMImage.Initialize2(ProductInputstream)
' ProductInputstream.Close
bgImageview = tempstringP.SubString2(Pic_start +15,ProductQuantity_start-2)


' Dim InputStream1 As InputStream
' InputStream1 = job5.GetInputStream
' Dim OutputStream1 As OutputStream
' OutputStream1.InitializeToBytesArray(1000)
' File.Copy2(InputStream1, OutputStream1)
' Dim Buffer() As Byte 'declares an empty array
' Buffer = OutputStream1.ToBytesArray
'
'
' Dim input As InputStream
' input.InitializeFromBytesArray(Buffer,0,Buffer.Length)
' Dim BiMap As Bitmap
' BiMap.Initialize2(input)
' input.Close
'

'imageProduct.SetBackgroundImage(job5.GetBitmap)





'bgImageview = "sample_image-1.png"
'bmImageview.Initialize(File.DirAssets,bgImageview)

labelProductname.Text = ProductName_db
ProductName = labelProductname.Text
lblProdctPrice.Text = ProductPrice_db
lblProdctPrice.TextColor = Colors.Red
ProductPrice = lblProdctPrice.Text

LblPrice.Text = "Price:"

svProduct.Panel.AddView(pnlProduct,0%x,i*25%y,90%x,30%y)
pnlProduct.AddView(PnlLine,0%x,90%y,100%x,3%y)
PnlLine.SetBackgroundImage(bmPnlLine)

pnlProduct.AddView(imageProduct,5%x,1.5%y,30%x,21.3%y)

pnlProduct.AddView(labelProductname,50%x,5%y,70%x,7%y)
pnlProduct.AddView(LblPrice,50%x,13%y,15%x,5%y)
pnlProduct.AddView(lblProdctPrice,62%x,13%y,25%x,5%y)

i = i + 1

index_counterP = Complete_endP
index_counterP = tempstringP.IndexOf2("}",index_counterP+1)

job5.Initialize("job5",Me)
job5.Download(bgImageview)
'


Loop

End Sub
 

kamalkishor

Member
Licensed User
Longtime User
Problem While downloading Multiple images using httpjob

Hi,

i want to download multiple images using httpjob .But problem is that only one image is downloaded while using httpjob in loop , any help or suggestion is appriciated






Rgds
 
Last edited:

Inman

Well-Known Member
Licensed User
Longtime User
Does Nine patch work with Imageviews as well? I tried it on an Imageview just now. I get the 9 patch background correctly, but it overwrites the bitmap that was on the Imageview.
 

Inman

Well-Known Member
Licensed User
Longtime User
Oh ok. Thanks for the tip. Guess I have to put the ImageView on a Panel first and set the Panel's background to 9 patch.
 

moster67

Expert
Licensed User
Longtime User
Hi, I am trying to customize some different types of views using 9patch. So far edittexts, buttons and labels seem to be OK. I got a reply what regards the Spinner-view although I still need to test it.

What about Checkbox and Radiobutton views? Is it possible? Perhaps it is an overkill and it would be better just to change the drawable?

One other thing, I think it is better to start creating 9patch-images in smaller sizes since I believe they will never down-scale, only upwards. Is that right? In this regard, I have seen some online tools such as this one:

http://android-ui-utils.googlecode.com/hg/asset-studio/dist/nine-patches.html

I noted this tool generates 4 sizes, namely MDPI, HDPI, XHDPI and XXHDPI. Is it good to do so? Will the code posted in this tutorial take into consideration the different folders:
drawable-mdpi
drawable-hdpi
drawable-xhdpi
drawable-xxhdpi

and use the correct 9patch-images according to device?

Many thanks in advance to those who can clarify and shed some lights on these doubts of mine.
 

gawie007

Member
Licensed User
Longtime User
Hi,
I have placed my 9 patch images in the correct folder but keep getting the following compilation error:
Parsing code. 0.00
Compiling code. 0.05
Compiling layouts code. 0.01
Generating R file. Error
res\drawable\SimHR.9.png: Invalid file name: must contain only [a-z0-9_.]
res\drawable\SimHR.9.png: Invalid file name: must contain only [a-z0-9_.]
res\drawable\SimHR.9.png: Invalid file name: must contain only [a-z0-9_.]
res\drawable\SimHRP.9.png: Invalid file name: must contain only [a-z0-9_.]
res\drawable\SimHRP.9.png: Invalid file name: must contain only [a-z0-9_.]
res\drawable\SimHRP.9.png: Invalid file name: must contain only [a-z0-9_.]
res\drawable\SimHRP.9.png: Invalid file name: must contain only [a-z0-9_.]

My file sizes are 25 KB if that makes any difference?
 

gawie007

Member
Licensed User
Longtime User
Thanks NJ Dude,
I didn't see the trees for the woods: a-z and no A-Z.
This was also due partly to the documentation that stated that it was case sensitive so I ASSumed that it could use both lower and uppercase.
 

achtrade

Active Member
Licensed User
Longtime User
Hello,

I'm trying this with a button but I got this error:

java.lang.RuntimeException: Object should first be initialized (Button).
at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:46)
at anywheresoftware.b4a.objects.ViewWrapper.setBackground(ViewWrapper.java:91)
at b4a.gym.main._setninepatchbutton(main.java:526)
at b4a.gym.main._activity_create(main.java:452)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:636)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:305)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:238)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:121)
at b4a.gym.main.afterFirstLayout(main.java:98)
at b4a.gym.main.access$100(main.java:16)
at b4a.gym.main$WaitForLayout.run(main.java:76)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4950)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1004)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:771)
at dalvik.system.NativeStart.main(Native Method)
** Activity (main) Resume **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **


I created the button in the designer. This is my code:
B4X:
Sub Globals
    Dim btn1 As Button 
End Sub

Sub Activity_Create(FirstTime As Boolean)
  SetNinePatchButton(btn1, "label_bg", "label_bg")
End Sub

the image file is in the indicated folder.

what's wrong ?
 

achtrade

Active Member
Licensed User
Longtime User
Have you loaded the layout file?

Ok, that was a rookie mistake. It's working but it replaces my icon for the background. How can I keep this button with an icon and this background/border ?

thanks
 
D

Deleted member 30048

Guest
I have a question, if I have a nine-patch image with 100x100 pixels and I put it into a imageview with 1000x1000 pixels, How much memory RAM use the bitmap that containing the imageview, like the original nine-patch (100x100) or like an image of 1000x1000 pixels?

Best regards
 

Peter Simpson

Expert
Licensed User
Longtime User
Nine Patch, the best thing since sliced bread :)

Today I used 9 Patch for the first time ever. By looking at @Erel's example I found it extremely simple to implement into my old apps(it literally took just minutes). My updated apps are now looking clean and up to date.

Here is a quick video that I found on YouTube on how to use the Draw 9 Patch tool. Ignore the first 60 seconds or just jump forwards 60 seconds, it's up to you, you can then stop at 3.10 minutes. So the only useful part imo for B4A users is between 60 seconds and 3 minutes and 10 seconds. The reason why I say to ignore the beginning and the end is because if you follow the example in Erels post, then you really should create your 9 Patch file whilst coding your app and not after you've created the APK file.


Enjoy...
 
Last edited:

miguelconde

Member
Licensed User
Longtime User
Hi Erel , guys.

I have a problem using an image "nine path" on a "spinner".
The image appears correctly, but the content is displayed at the top and center of the "view".
How do I get the correct gravity of the "spinner" is respected?.
It seems as if the text or content avoid areas where i placed the "pixels" indicators.

thank you.
 

Attachments

  • spiner.9.png
    spiner.9.png
    464 bytes · Views: 415
Top