ClassCastException on custom SurfaceView - android

i've got a big problem since a few days:
When I try to bind the surfaceview on the layout and my custom surfaceview, i get a ClassCastException from my custom surfaceview.
here is my code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
waveform =(WaveFormView) findViewById(R.id.surfaceView1);
}
class WaveFormView extends SurfaceView implements SurfaceHolder.Callback {
public WaveFormView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
_dthread = new DrawingThread(getHolder(), this);
setFocusable(true);
paintP.setStyle(Paint.Style.STROKE);
paintP.setStrokeWidth(1);
paintP.setColor(Color.WHITE);
paintT.setStyle(Paint.Style.STROKE);
paintT.setStrokeWidth(1);
paintT.setColor(Color.WHITE);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
for (int i=0;i<_athread.buffer.length-pas;i+=pas){
canvas.drawLine(i, 150-_athread.buffer[i], i+pas, 150-_athread.buffer[i+pas], paintP);
}
canvas.drawText("FPS: " + String.valueOf(FPS), 0, 10, paintT);
//canvas.drawText("tmp: " + tmp, 0, 20, paintT);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
_dthread.setRunning(true);
_dthread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// simply copied from sample application LunarLander:
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
_dthread.setRunning(false);
while (retry) {
try {
_dthread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
class DrawingThread extends Thread {
private SurfaceHolder _surfaceHolder;
private WaveFormView _waveformview;
private boolean _run = false;
public DrawingThread(SurfaceHolder surfaceHolder, WaveFormView waveformview) {
_surfaceHolder = surfaceHolder;
_waveformview = waveformview;
}
public void setRunning(boolean run) {
_run = run;
}
public SurfaceHolder getSurfaceHolder() {
return _surfaceHolder;
}
#Override
public void run() {
Canvas c;
long startMs=System.currentTimeMillis();
int frameCounter=0;
while (_run) {
c = null;
frameCounter++;
if (System.currentTimeMillis()-startMs>1000){
startMs = System.currentTimeMillis();
FPS = frameCounter;
frameCounter=0;
}
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_waveformview.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
When I replace:
setContentView(R.layout.main);
with this:
setContentView(new WaveFormView (this));
...it works perfectly !
But, I need to have buttons besides.
here is the layout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
<LinearLayout android:layout_weight="1" android:padding="0dip" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" android:id="#+id/content">
<TextView android:layout_height="wrap_content" android:id="#+id/tvdebug" android:text="Debug" android:layout_width="wrap_content"></TextView>
<SurfaceView android:id="#+id/surfaceView1" android:layout_height="fill_parent" android:layout_width="fill_parent"></SurfaceView>
</LinearLayout>
If someone have the solution it would me a lot !

Maybe R.id.surfaceView1 is not declared as WaveFormView but as SurfaceView? Try declare it specifically, otherwise this is illegal casting indeed.
WaveFormView is a SurfaceView, but a SurfaceView is not necessarily a WaveFormView.
You can use your own view in the layout xml, by specifying the class name (<path.to.WaveFormView instead of <SurfaceView...)
For example:
<view class = "com.mypath.WaveFormView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/surfaceView1"
android:layout_above="#id/auto_scrollview"
/>
OR
<com.mypath.WaveFormView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/surfaceView1"
android:layout_above="#id/auto_scrollview"
/>
If this class is an inner class, as seemed from your question, then use:
<com.mypath.OUTERCLASSNAME$WaveFormView
EDIT
Since this class is needed to be shown from outside your class, and be loaded not from instance but statically, you need to public static to its declaration, e.g.:
public static class WaveFormView extends SurfaceView implements SurfaceHolder.Callback

Related

RelativeLayout background always displayed black

I want to apply repeating pattern to my layout background, but when i start application, background is black.
I have implemented custom view from SurfaceView:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="#+id/playGroundLayout"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/backrepeat"
android:scaleType="centerCrop">
</ImageView>
</RelativeLayout>
background tile:
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/tile"
android:tileMode="repeat"
android:dither="true" />
surface creation:
public class PlayGroundActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_ground);
RelativeLayout surface = (RelativeLayout)findViewById(R.id.playGroundLayout);
surface.addView(new PlayGroundSurface(this));
}
}
surfaceView implementation:
public class PlayGroundSurface extends SurfaceView implements SurfaceHolder.Callback
{
private MainThread thread;
public PlayGroundSurface(Context context)
{
super(context);
getHolder().addCallback(this);
setFocusable(true);
setWillNotDraw(false);
}
private Bitmap scaled;
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new MainThread(getHolder(),this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry)
{
try {
thread.join();
retry = false;
}catch (InterruptedException ex){}
}
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
return true;
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
public void render(Canvas canvas) {
}
}
mainThread class:
public class MainThread extends Thread
{
private final static int MAX_FPS = 50;
private final static int MAX_FRAME_SKIPS = 5;
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
private SurfaceHolder holder;
private PlayGroundSurface surface;
private boolean running = false;
public MainThread(SurfaceHolder surfaceHolder, PlayGroundSurface playGroundSurface) {
surface = playGroundSurface;
holder = surfaceHolder;
}
public void setRunning(boolean run)
{
running = run;
}
#Override
public void run()
{
Canvas canvas;
long beginTime;
long timeDiff;
int sleepTime;
int framesSkipped;
sleepTime = 0;
while(running)
{
canvas = null;
try
{
canvas = holder.lockCanvas();
synchronized (holder)
{
beginTime = System.currentTimeMillis();
framesSkipped = 0;
surface.draw(canvas);
surface.render(canvas);
timeDiff = System.currentTimeMillis() - beginTime;
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if(sleepTime > 0)
{
try {
Thread.sleep(sleepTime);
}catch (InterruptedException e){ }
}
while(sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS)
{
surface.updateAllItems();
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
finally
{
if(canvas != null)
{
holder.unlockCanvasAndPost(canvas);
}
}
}
}
}
when i change layout root element from RelativeLayout to LinearLayout, it shows horizontal stripe of background. Seems like my RelativeLayout has zero dimensions.
What i am doing wrong?
I think you should try to remove the ImageView and set the background to the RelativeLayout directly
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="#+id/playGroundLayout"
android:layout_height="match_parent"
android:background="#drawable/backrepeat">
</RelativeLayout>

Canvas SurfaceView

I have custom SurfaceView, setting a background in onDraw method, then drawing canvas, but see only background. How can I draw with canvas on a background?
Tried to add a background with canvas, it works, but at top left 2-3dp was deleted, and redrawing every time background will eat a lot of memory.
So can it be done with 1st method or 2nd without empty spaces on top left?
Edit...
GameView.java
public class GameView extends SurfaceView {
private Bitmap background;
private SurfaceHolder holder;
private GameManager gameLoopThread;
public GameView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
setWillNotDraw(false);
gameLoopThread = new GameManager(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
background = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
}
public void onDraw(Canvas canvas) {
canvas.drawBitmap(background, 0, 0, null);
}
public void drawCanvas(Canvas canvas) {
if (x < getWidth() - 10) {
x += 10;
} else {
gameLoopThread.setRunning(false);
}
canvas.drawLine(0, 200, x, 200, paint);
}
}
GameManager.java
public class GameManager extends Thread {
private GameView view;
private boolean running = false;
public GameManager(GameView view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
#Override
public void run() {
while (running) {
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.drawCanvas(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
}
MainActivity.java
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout ll = (LinearLayout) findViewById(R.id.main);
ll.setLayoutParams(new LinearLayout.LayoutParams(640, 400));
}
}
Main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center_horizontal|top">
<LinearLayout
android:id="#+id/main"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<com.android.GameView
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
The result is http://oi57.tinypic.com/15znrpy.jpg
If I comment GameView onDraw method and add canvas.drawBitmap(background, 0, 0, null); to drawCanvas the result will be
http://oi60.tinypic.com/t0g802.jpg
There's not really a specific way to set a SurfaceView's background.
If you set a background in onDraw then draw to the canvas somewhere outside of onDraw, it's going to call onDraw and draw the background over whatever was just drawn.
Redrawing the background every time will not eat a significant amount of memory, assuming you're not trying to do it on the UI thread.
Also, I'm not quite sure what you mean by "at top left 2-3dp was deleted" and "empty spaces on top left". Can you post an image of this, along with the code you've tried?

Simple game bug

I write a simple game with the following structure. Main xml layout have user View:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/menuRL"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.sokolovlev.UFOtest03.MenuView
android:id="#+id/menuView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
This View have two files. First is user MenuView:
package com.sokolovlev.UFOtest03;
import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MenuView extends SurfaceView implements SurfaceHolder.Callback
{
private SurfaceHolder mSurfaceHolder; //Draw surface
public static MenuManager mMenuManager; //Draw manager
public MenuView(Context context, AttributeSet attrs)
{
super(context, attrs);
// Surface events registration
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
mMenuManager = new MenuManager(mSurfaceHolder, context);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
mMenuManager.initPositions(height, width);
}
#SuppressWarnings("static-access")
#Override
public void surfaceCreated(SurfaceHolder holder)
{
mMenuManager.setRunning(true);
try
{
mMenuManager.start();
}
catch (Exception e) { }
}
#SuppressWarnings("static-access")
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
mMenuManager.setRunning(false);
while (retry)
{
try
{
mMenuManager.join();
retry = false;
}
catch (InterruptedException e) { }
}
}
}
And second file is this draw manager:
package com.sokolovlev.UFOtest03;
import ...
public class MenuManager extends Thread
{
private SurfaceHolder mSurfaceHolder;
private static boolean mRunning;
private int _screenHeight;
private int _screenWidth;
private ...
Context c;
private ...
public MenuManager(SurfaceHolder surfaceHolder, Context context)
{
mSurfaceHolder = surfaceHolder;
mRunning = false;
c = context;
...
}
public static void setRunning(boolean running)
{
mRunning = running;
}
#Override
public void run()
{
while (mRunning)
{
Canvas canvas = null;
try
{
// подготовка Canvas-а
canvas = mSurfaceHolder.lockCanvas();
canvas.drawRGB(0, 0, 0);
synchronized (mSurfaceHolder)
{
//All drawing
}
}
catch (Exception e) { }
finally
{
if (canvas != null)
{
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
Everything works fine! (step №1) But if I press central button, or I have incoming call, or I call task manager (step №2) and go back to my app - I haven't my drawing, only black screen (step №3). But if I go next to step №2 and then in my app, I see that everything works.
I don't understand, where there is a reloading which influences such on my drawings! Help please!
To solve this problem, I need to save the settings and restart my surface. Then load the settings. (See LunarLander Example)

How to display the Canvas part of a view in android?

I am working on a code where we use canvas to detect the touch on the screen.As of now the canvas is been directly drawn.How to add it as part of a view which comprises of other elements in xml.Here is the code
public class Tutorial2D extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new Panel(this));
}
}
Here is the other part of it
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
private ViewThread mThread;
private ArrayList<Element> mElements = new ArrayList<Element>();
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}
public void doDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
synchronized (mElements) {
for (Element element : mElements) {
element.doDraw(canvas);
}
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!mThread.isAlive()) {
mThread = new ViewThread(this);
mThread.setRunning(true);
mThread.start();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread.isAlive()) {
mThread.setRunning(false);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (mElements) {
mElements.add(new Element(getResources(), (int) event.getX(), (int) event.getY()));
}
return super.onTouchEvent(event);
}
}
How to add this canvas to the main xml and been displayed over an image,any snippet on this or how should I change working on this code,anything will be greatful.Thanks
Try this constructor for the Panel class:
public Panel(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}
You can use the custom view in xml layout with its package name. For example, in main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello" />
<your.package.name.Panel
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Then, in onCreate of your activity:
setContentView(R.layout.main);

Android SurfaceView not showing onDraw

For simpleness of the question, I'm drawing an integer on a SurfaceView which increases by 1 every draw.
The increasing actually happens, as I can see on the System.out.
The text on the screen stays on '0'.
Who can tell me what I'm doing wrong?
SurfaceViewTest.java
package com.niek.surfaceviewtest;
import android.app.Activity;
import android.os.Bundle;
public class SurfaceViewTest extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
StatusView.java
package com.niek.surfaceviewtest;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class StatusView extends SurfaceView implements SurfaceHolder.Callback {
private int tmp;
private DrawThread drawThread;
public StatusView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
setFocusable(true);
drawThread = new DrawThread(getHolder());
}
#Override
public void onDraw(Canvas c) {
c.drawColor(Color.BLACK);
Paint p = new Paint();
p.setColor(Color.RED);
c.drawText(tmp + "", 10, 10, p);
tmp++;
System.out.println(tmp);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
drawThread.setRunning(true);
drawThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
drawThread.setRunning(false);
while (retry) {
try {
drawThread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
protected class DrawThread extends Thread {
private SurfaceHolder surfaceHolder;
private boolean isRunning;
public DrawThread(SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
isRunning = false;
}
public void setRunning(boolean run) {
isRunning = run;
}
public void run() {
Canvas c;
while (isRunning) {
c = null;
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF">
<com.niek.surfaceviewtest.StatusView
android:id="#+id/statusview1"
android:layout_width="fill_parent"
android:layout_height="30dip"
android:background="#000000"
/>
</LinearLayout>
My only guess is that painting isn't being done on the surface because the view isn't invalidated. You're supposed to call invalidate() in order to draw, and then let the framework call onDraw(). Maybe that's why tmp is being incremented, but the paint operation only reaches the surface the first time.
It might be worth experimenting: maybe make Canvas c a member of StatusView, and then replace
synchronized (surfaceHolder) {
onDraw(c);
}
with
synchronized (surfaceHolder) {
invalidate();
}
Does that work?
The current solution is not right. You are using the SurfaceView in order to update the content from a separate thread and not using the invalidate() method that will run onDraw() method when the system refreshes the content. The problem is that you have set a background for your StatusView, try deleting that line
android:background="#000000"
apparently, you need to control the whole information displayed in that view.
Looks like you are showing your main.xml with setContentView(R.layout.main); instead of creating the surface and displaying that. unless i am missing code somewhere I don't see that as being the case.

Categories

Resources