Why cant I draw anything on my SurfaceView canvas? - android

I tried to match LunarLander's implementation of a SurfaceView style of drawing.
I am starting really simple, and just trying to draw circles underneath the user's touch input, but I can't seem to draw anything! All my app gets is a green screen (which is the background color of the view in the layout xml).
In an attempt to draw something, I have tried drawing a black rectangle on the entire canvas, a circle at 0,0 and a bitmap image. None of these draws succeed. They all get called, and none throw exceptions, but the screen never changes from green.
I am at wits end trying to figure this out so here is my code (minus the game engine, which is irrelevant at this point).
The layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.jknight.particletoy.view.GameEngineView
android:id="#+id/gameengineview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF66FF33"/>
</FrameLayout>
EDIT:
Figured it out:
You cannot have a background color property in the layout for a surface view. It was rendering overtop.

Related

Android Custom View inside Linear Layout with other views

I have a custom view to display a simple piechart.
My intention is to take some values like size,background color etc thru Layout xml file.
I derive the radius of the circle, strokewidth etc as a factor of the height of the view(width is equal to width of the parent/screen) and all dimensions are based on this radius.
Here is my layout xml for the fragment
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/bluegrey200"
tools:context="ash.dbtest.GraphPieChartFragment">
<ash.dbtest.GraphPieChartView
android:layout_width="match_parent"
android:id="#+id/pieChart1"
custom:backgroundColor="#color/bluegrey800"
custom:labelColor="#color/white"
android:layout_height="800px"
/>
This appears perfectly fine now.
See the image:
The black rectangle is the area used in the overridden onDraw method. Note the blue diagonal line showing the bounds of the rectangle.
I know the height is mentioned in pixels and it must be in dp; but the values are now for debugging purpose.
I changed my layout xml file to below to add a banner above the Custom view in another linear layout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/bluegrey200"
tools:context="ash.dbtest.GraphPieChartFragment">
<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:text="TestBanner"
android:textSize="20sp"
android:layout_height="wrap_content" />
</LinearLayout>
<ash.dbtest.GraphPieChartView
android:layout_width="match_parent"
android:id="#+id/pieChart1"
custom:backgroundColor="#color/bluegrey800"
custom:labelColor="#color/white"
android:layout_height="800px"
/>
See the screenshot now, it goes weird.
See image here:
Now my customview moves to the bottom, but is getting truncated. The onDraw method is getting the right dimensions and the circle is getting drawn correctly. But somehow the View is getting truncated. In the screenshot below, note that the black rectangle is incomplete and the blue diagonal line is getting truncated.
What could be the issue ?
I am a beginner around android and hope that I have provided all required information. I did not post the code because its just basic draws on canvas.
Thank you very much for your time on this.
Code on the GraphPieCharView.onDraw
#Override
public void onDraw(Canvas canvas) {
Log.d("onDraw","onDraw");
Log.d("onDraw1",Float.toString(this.getLeft()));
Log.d("onDraw2",Float.toString(this.getTop()));
Log.d("onDraw3",Float.toString(this.getRight()));
Log.d("onDraw4",Float.toString(this.getBottom()));
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setStrokeWidth(20);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(BACKGROUND_COLOR);
canvas.drawRect(this.getLeft(),this.getTop(),this.getRight(),this.getBottom(),paint);
because your GraphPieChartView is below TextView
Figured it out.
I was drawing the rectangle using the below code
canvas.drawRect(this.getLeft(),this.getTop(),this.getRight(),this.getBottom(),paint);
Lets say the page is 1500px high and 1000px wide.
Here, when the customview is the only view on the page, below are the results of the getters.
left - 0
top - 0
right - 1000
bottom - 800
I drew the rectangle in these bounds, and all the calculation based on these bounds; it worked fine.
Now, when the custom view is the second view when listed vertically, with the top TextView taking say 50px,
left - 0
top - 50
right - 1000
bottom - 850
But these values are relative to the whole screen (1000*1500).
But inside my custom view, the canvas I have is 1000*800, starting from 0,0 and ending at 1000,800 diagonally.
But I was drawing on the canvas as if it starts from 0,50 and ending at 1000,850.
Adjusted the calculation inside the onDraw method to always start from 0,0 and it solved the issue.
Thanks for all the responses.

Adding an overlay to the camera view rather than the map view

I am trying to add a 'target' which would be represented as a circle which must always remain at the center of the current map view. Looking through the API I see a lot about adding Markers, Shapes, Ground overlays and Tile overlays but none seem to be adaptable to my needs.
Basically they are all used to draw elements 'on the map' at specified locations. What I need is some sort of overlay for the actual camera view. A good example would be when looking through the lens of the camera where a circle denotes the center of the frame.
You can use a RelativeLayout, which contains the MapFragment plus an additional custom view:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<fragment android:id="#+id/map"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent" android:layout_height="match_parent" />
<mypackage.MyCustomView
android:id="#+id/mapOverlay" android:layout_width="match_parent"
android:layout_height="match_parent" android:background="#android:color/transparent" />
</RelativeLayout>
Whatever you draw into the custom view, e.g. a circle in the center, is drawn on top of the map, and will stay there, independent of the maps movement.
If you want to adapt the shape to the zoom, tilt, etc. you need to listen to camera changes and adapt the drawing accordingly. Unfortunately the CameraChangeListener is only called, after the change is finished. So you can not immediately follow the zoom or tilt gesture with your drawing.

Content area defined in 9 patch image doesn't work for custom view

Currently, I have a custom view BarChart. I wish to have some red shadowing effect on it. I'm using nine patch image technique to achieve so.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="240dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/card_background_red"
android:orientation="vertical"
android:padding="0dp" >
<org.yccheok.jstock.gui.charting.BarChart
android:id="#+id/bar_chart"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
In my custom view, there is line of code, where I will draw string at the end-y of the view.
// Drawing string at end-y of BarChart custom view.
canvas.drawText("2007", x0, getHeight(), textPaint);
canvas.drawText("2008", x1, getHeight(), textPaint);
I avoid my custom view from "touching" any of the red shadow, I define my content area of 9-patch, so that it doesn't touch the red shadow.
As you can see, the content area are pretty much stay away from red shadow.
I thought my drawn text will never touch the red shadow area, as I restrict my content area (entire custom view?) stay away from red shadow area. However, it doesn't work.
Am I having wrong expectation on the content area of 9 patch image? I thought Linear Layout's "content" is my custom view BarChart. Hence, BarChart shouldn't be touching the red shadow as specific in 9 patch image. (http://www.shubhayu.com/android/9-patch-image-designers-vs-developers)
Remove android:padding="0dp" from LinearLayout. It overrides padding from 9-patch.

9-patch drop shadow technique works for image view but not camera preview

I use to following 9-patch image to create drop shadow effect.
It works pretty well for the following image view.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#FFFFFF"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center" >
<ImageView
android:id="#+id/image_test"
android:background="#drawable/dropshadow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/pic1"
/>
</LinearLayout>
I am able to get a perfect shadow dropped effect. Note that, there is a 1 pixel wide red line at picture right/bottom. I purposely place the 1 pixel wide red line around picture right/ bottom, to proof there are no pixel from 9-patch image cover up my original picture.
However, when I try to apply the exact same technique on camera preview view, things getting complicated. I do a slight modification on API Demo example com.example.android.apis.graphics.CameraActivity
I modify the GUI view, by performing the following modification.
class Preview extends ViewGroup implements SurfaceHolder.Callback {
// ...
Preview(Context context) {
// ...
mSurfaceView.setBackgroundDrawable(getResources().getDrawable(drawable.dropshadow));
this.setBackgroundColor(Color.WHITE);
// ...
}
}
I get the following outcome. Note that, shadow effect begin to appear, before end of picture right edge and bottom edge. Its distance is 9 pixels before reaching end of edge.
I am pretty sure I am having correct image filename dropshadow.9.png for both project. However, I lack of idea why the latter doesn't work as expected?
This is because you are using a SurfaceView which provides you with a dedicated drawing surface that will not respect the content boundaries you have defined in your nine patch image.
A possible workaround would be to place your camera's SurfaceView on top of a slightly larger view that holds the drop shadow as a background.

Compositing Android MapView and SurfaceView

I have an application with a SurfaceView and a MapView. They are displayed in a single view, like so:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.google.android.maps.MapView
android:id="#+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="XXXXXXXXredactedXXXXXXXXXXXXx"
/>
<class.that.extends.SurfaceView
android:id="#+id/surfaceview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#00000000"
/>
</RelativeLayout>
Note the MapView comes before the SurfaceView. Because of this, the MapView is drawn "under" the SurfaceView, and all that I see is a black background, with the things that I draw on the SurfaceView showing. If I swap the order, and put SurfaceView first in the file and MapView second, the MapView is drawn, with none of the SurfaceView showing.
According to the SurfaceView Javadoc:
The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it.
To me, this means that I should be able to put the SurfaceView first in the file, and have it "punch through" the MapView and display what it has. But, as it is, they seem to be two views, and only one is visible at a time.
After thought: I tried setting the background to #00000000 so that the alpha of the background would be zero (i.e. transparent), but it doesn't have any effect.
The alpha setting alone won't really help in this case.
When you setup your views, set the PixelFormat of the view's SurfaceHolder:
glsView = (GLSurfaceView) findViewById(R.id.surfaceview);
glsView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
I ended up abandoning SurfaceView, despite it's wonderful features for drawing smoothly. Now I'm just using a regular old View, overriding its onDraw method. The MapView underneath is currently being refreshed every time this new View is drawn, but I'm looking in to using the drawing cache feature of Views to simplify that.

Categories

Resources