Android Question Canvas and Out of Memory

tdocs2

Well-Known Member
Licensed User
Longtime User
Greetings, all, and thank you in advance for answering my question.

I have read a number of postings on this subject, but none of them seem to hit the mark. My apologies if this post is redundant.

I am getting an Out of Memory error in an Asus Transformer 700 V4.2.1 - 1GB RAM, 32 GB storage. I do not get the error in any of my other tablets ranging from V4.0 to 4.4.

As pointed out in other posts on this subject, the error comes from Canvas or misuse of it.

Log
B4X:
java.lang.OutOfMemoryError
    at android.graphics.Bitmap.nativeCreate(Native Method)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:689)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:666)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:633)
    at anywheresoftware.b4a.objects.drawable.CanvasWrapper.Initialize(CanvasWrapper.java:76)
    at swi.myapp.CodeModule._rect_draw(td.java:735)

The sub in the code module:

B4X:
Sub rect_draw (p1 As Panel,n1 As Int,colorc As Int)
    Dim canvas2 As Canvas
    Dim rect2 As Rect
    canvas2.Initialize(p1)
    rect2.Initialize(0dip,0dip, p1.width-n1+1, p1.height-n1+1)
    canvas2.DrawRect(rect2, colorc, False, n1*1dip) 
    p1.Invalidate 'force to redraw
End Sub

I use the Canvas rectangle as a separator for the different panels in my activities.
I use Activity.Finish upon exiting each activity.

What can I do?

Any and all help is welcomed.

Sandy
 

sorex

Expert
Licensed User
Longtime User
can you upload a screen shot of what you are trying to do? or the project itself (file> export to ZIP)
 
Upvote 0

tdocs2

Well-Known Member
Licensed User
Longtime User
can you upload a screen shot of what you are trying to do? or the project itself (file> export to ZIP)
Thank you, Sorex.

What I am trying to do is simulate MS VB in their panels - give it a border.

I wrote a small app - my app is much too large...

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

    Private Button1 As Button
    Private Panel1 As Panel
    Private Panel2 As Panel
    Private Panel3 As Panel
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("Layout1")

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
Sub Button1_Click
    rect_draw(Panel1,3,Colors.White)
    rect_draw(Panel2,3,Colors.White)
    rect_draw(Panel3,3,Colors.White)

End Sub
Sub rect_draw (p1 As Panel,n1 As Int,colorc As Int)
    Dim canvas2 As Canvas
    Dim rect2 As Rect
    canvas2.Initialize(p1)
    rect2.Initialize(0dip,0dip, p1.width-n1+1, p1.height-n1+1)
    canvas2.DrawRect(rect2, colorc, False, n1*1dip)
    p1.Invalidate 'force to redraw
End Sub

The first image is 3 panels without a border. The second image is 3 panels with a border.

3panelsnoborder.png 3panelswithborder.png

Thank you.

Sandy
 
Last edited:
Upvote 0

sorex

Expert
Licensed User
Longtime User
why don't you draw in 1 canvas that's on top of the 3 panels? that will only consume 1/3th of what you have now.
 
Upvote 0

eps

Expert
Licensed User
Longtime User
Upvote 0

tdocs2

Well-Known Member
Licensed User
Longtime User
@eps
I am drawing a rectangle on a panel. How do I get the size of the bitmap?
I took a look at initializesample - did not find it in canvas or rect

@eps
I put canvas and rect in globals.

Same issue - I recreated it as follows:

B4X:
Sub Button1_Click
For i = 0 To 29
    Log (i)
    Activity.LoadLayout("Layout1")
    rect_draw(Panel1,3,Colors.White)
    rect_draw(Panel2,3,Colors.White)
    rect_draw(Panel3,3,Colors.White)
    DoEvents
Next   
End Sub
Sub rect_draw (p1 As Panel,n1 As Int,colorc As Int)
    canvas2.Initialize(p1)
    rect2.Initialize(0dip,0dip, p1.width-n1+1, p1.height-n1+1)
    canvas2.DrawRect(rect2, colorc, False, n1*1dip)
    p1.Invalidate 'force to redraw
End Sub

Whether the dim for rect and canvas is in globals or inside the sub, the app stops on the 25th loop.
 
Last edited:
Upvote 0

sorex

Expert
Licensed User
Longtime User
not sure how bitmaps are stored in memory but with the dimension of 445x326 that the panel appears to be it's probably something like

445x326x4=580280 bytes, that 0.5Mb * 25 = 12MB.

how many panels are you planning to have on screen since you do more than 25 loops?

and what's the actual purpose of all this?
 
Upvote 0

eps

Expert
Licensed User
Longtime User
You probably don't need to always initialise the canvas either - you need to check to see if it's already initialised and then initialise if it isn't initialised.

It's a bit difficult to give help not knowing what you're actually attempting to achieve (as Sorex has asked as well). Are you going to place a bitmap on the canvas or is it just that you haven't got that far yet??
 
Upvote 0

tdocs2

Well-Known Member
Licensed User
Longtime User
Hello, Sorex and eps,

My whole intent is to put a border around the panels similar to panels in Visual Basic in MS. My wish would be that it would be a property in B4A. I have not tried it in B4J (no time), but I would venture to guess that it is a property for the panel container.

The purpose of the border around the panel was only to delineate different sections of the activity since it contains many sections, and the app contains many activities. Each panel contains other views labels, Imageview, buttons, listviews, etc.

I looped 30 times to see when I could recreate the error. In my app, I only draw the rectangle once when the activity is started. When I exit the activity, I issue Activity.Finish, but apparently that does not free the memory or leaves a little garbage behind which after staring the activity several times, creates the Out of memory situation. In my app, the number of panels range from 2 to 17 depending on the activity.

I know I could use a gradientDrawable appearance combined with different shade of the color palette I selected to separate the sections, but I thought the canvas was clearer. Although at this point, I am leaning in that direction of dropping the rectangles altogether.

Canvas has to be initialized each time since the target is a different panel - in the example, Panel1, 2, and 3.

Best regards.

Sandy
 
Last edited:
Upvote 0

tdocs2

Well-Known Member
Licensed User
Longtime User
actually you should only do it once for each panel.
You are absolutely correct, Sorex. And that is what the sample app I wrote shows - once for each panel.
Canvas has to be initialized each time since the target is a different panel - in the example, Panel1, 2, and 3.
Thank you, again.

Sandy
 
Upvote 0

tdocs2

Well-Known Member
Licensed User
Longtime User
I should have realized the price that graphics brings. I thought the canvas on panel would be an exception. That is why mega processors and mega memory are required for film editing and special effects.

I have opted for the use of shades and gradientdrawables within the selected palette of my app. If I need a border, I can always use a panel within panel - lots less overhead.

Thank you, Sorex and eps for your help.

Sandy
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
Here's a quick and easy solution that i picked up in the week, found it somewhere on Stackoverflow...
  • Create an XML file, save it with a path that matches res\drawable\view_border.xml:
    B4X:
    <?xml version="1.0" encoding="UTF-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
    	<solid android:color="#FFFFFF" />
    	<stroke android:width="1dp" android:color="#000000" />
    	<padding android:left="1dp" android:top="1dp" android:right="1dp" android:bottom="1dp" />
    </shape>
  • Add an AdditionalRes project attribute to your project attributes, use the path to the res folder above.
  • Include my Resources library in your project.
    (library files attached).
  • Get the resource id of the view_border drawable resource (resource ids are integer values).
  • Get the Drawable from your project's Resources with this resource id.
  • Set the Background of your Panel to this Drawable.

An example project:

B4X:
#Region  Project Attributes 
	#ApplicationLabel: B4A Example
	#VersionCode: 1
	#VersionName: 
	'SupportedOrientations possible values: unspecified, landscape or portrait.
	#SupportedOrientations: unspecified
	#CanInstallToExternalStorage: False
	#AdditionalRes: C:\Temp\ViewBorder\res
#End Region

#Region  Activity Attributes 
	#FullScreen: False
	#IncludeTitle: True
#End Region

Sub Process_Globals

End Sub

Sub Globals
	Private Panel1 As Panel
	Private Panel2 As Panel
	Private Panel3 As Panel
End Sub

Sub Activity_Create(FirstTime As Boolean)
	Activity.LoadLayout("Main")

	Dim Resources1 As Resources
	Resources1.Initialize(Resources1.RESOURCE_SOURCE_APPLICATION)
	
	Dim ResourceId As Int
	ResourceId=Resources1.GetIdentifier("view_border", "drawable", Resources1.PackageName)
	Panel1.Background=Resources1.GetDrawable(ResourceId)
	Panel2.Background=Resources1.GetDrawable(ResourceId)
	Panel3.Background=Resources1.GetDrawable(ResourceId)

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

And a screenshot from my Moto G is attached.

You should be able to use this technique on any android View - not just Panels.
So far a single pixel black border is all i've used it for, obviously if you understand the XML then you can change the style of the border.

Example project and my Resources library attached.

Martin.
 

Attachments

  • view_border.png
    view_border.png
    8.4 KB · Views: 244
  • Resources_library_files.zip
    8.1 KB · Views: 254
  • ViewBorder example.zip
    7.4 KB · Views: 252
Upvote 0
Top