Updating a Layout class - android

My problem is, that I have a Layout class called by an Inflater and I want to run a method from this class to update a picture every few seconds.
I wanted to do this with handlers and the run() method, but my problem is, the picture only updates itself when I am interacting with the screen (clicking my only button).
Do you know what I did wrong?
Here is my GameLayout class:
package de.undeadleech.frogjump;
public class GameLayout extends View implements Runnable
{
private Sprite sprite;
private Bitmap bmp;
private Handler handler = new Handler();
public GameLayout(Context context)
{
super(context);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.froschanimation);
sprite = new Sprite(bmp, 0, 0, 400, 100, 5, 4);
handler.postDelayed(this, 0);
}
#Override
protected void onDraw(Canvas canvas)
{
sprite.draw(canvas);
}
public void update(long currentTimeMillis)
{
sprite.update(currentTimeMillis);
}
#Override
public void run()
{
sprite.update(System.currentTimeMillis());
handler.postDelayed(this, 0);
}
}
EDIT:
Here is my Sprite class because you wanted to see it:
package de.undeadleech.frogjump;
public class Sprite
{
//private static final String TAG = Sprite.class.getSimpleName();
private Bitmap bitmap; // the animation sequence
private Rect sourceRect; // the rectangle to be drawn from the animation bitmap
private int frameNr; // number of frames in animation
private int currentFrame; // the current frame
private long frameTicker; // the time of the last frame update
private int framePeriod; // milliseconds between each frame (1000/fps)
private int spriteWidth; // the width of the sprite to calculate the cut out rectangle
private int spriteHeight; // the height of the sprite
private int x; // the X coordinate of the object (top left of the image)
private int y; // the Y coordinate of the object (top left of the image)
public Sprite(Bitmap bitmap, int x, int y, int width, int height, int fps, int frameCount)
{
this.bitmap = bitmap;
this.x = x;
this.y = y;
currentFrame = 0;
frameNr = frameCount;
spriteWidth = bitmap.getWidth() / frameCount;
spriteHeight = bitmap.getHeight();
sourceRect = new Rect( 0, 0, spriteWidth, spriteHeight);
framePeriod = 1000 / fps;
frameTicker = 0l;
}
public void update(long gameTime)
{
if (gameTime > frameTicker + framePeriod)
{
frameTicker = gameTime;
// increment the frame
currentFrame++;
if (currentFrame >= frameNr)
{
currentFrame = 0;
}
}
// define the rectangle to cut out sprite
this.sourceRect.left = currentFrame * spriteWidth;
this.sourceRect.right = this.sourceRect.left + spriteWidth;
}
public void draw(Canvas canvas)
{
// where to draw the sprite
Rect destRect = new Rect( x, y, x + spriteWidth, y + spriteHeight);
canvas.drawBitmap(bitmap, sourceRect, destRect, null);
}
}

Instead of posting Runnable you should call invalidate() when you need to redraw your layout. Also you should update state of your Sprite in onDraw method as well.

Related

How to generate bitmap one after another from y axis in android game

I am working on a small simple game in which the hurdles coming out from top and there is an static ball which can only move on x-axis.When the hurdles coming out from top the user have to move the ball to avoid the collision.
I am placing 3 moving hurdles at a time.but my problem is they are coming out together i.e all three hurdles have the same y-axis values.I want it to come out one by one with some specific distance.
How can i achieve this.
Here is my GamePanel Class:
public class GamePanel extends SurfaceView implements Runnable {
private Thread thread = null;
private Ball ball;
private SurfaceHolder surfaceHolder;
private Paint paint;
private Canvas canvas;
volatile boolean playing = true;
private int hurdleCount = 3;
private Hurdles[] hurdles;
private int screenX, screenY;
private Rect ball_detectCollision;
public GamePanel(Context context, final int screenX, final int screenY) {
super(context);
ball = new Ball(context, screenX, screenY);
surfaceHolder = getHolder();
this.screenX = screenX;
this.screenY = screenY;
paint = new Paint();
canvas = new Canvas();
hurdles = new Hurdles[hurdleCount];
for (int i = 0; i < hurdleCount; i++) {
hurdles[i] = new Hurdles(context, screenX, screenY);
}
ball_detectCollision = new Rect(ball.getBall_x(), ball.getBall_y(), ball.getBitmap().getWidth(), ball.getBitmap().getHeight());
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
System.out.println("Surface Created");
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
System.out.println("Surface Changed");
thread = new Thread(GamePanel.this);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
System.out.println("Surface Destroyed");
}
});
}
private void draw() {
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.RED);
canvas.drawBitmap(ball.getBitmap(), updatedValue, ball.getBall_y(), paint);
ball.setBall_x(updatedValue);
ball_detectCollision.left = ball.getBall_x();
ball_detectCollision.top = screenY - ball.getBitmap().getHeight() - 260;
ball_detectCollision.right = ball.getBall_x() + ball.getBitmap().getWidth();
ball_detectCollision.bottom = screenY - ball.getBitmap().getHeight() - 260 + ball.getBitmap().getHeight();
for (int i = 0; i < hurdleCount; i++) {
canvas.drawBitmap(hurdles[i].getBitmap(), hurdles[i].getX(), hurdles[i].getY(), paint);
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
#Override
public void run() {
while (playing) {
update();
draw();
control();
}
}
private void update() {
for (int i = 0; i < hurdleCount; i++) {
hurdles[i].update();
if (Rect.intersects(getBall_detectCollision(), hurdles[i].getDetectCollision())) {
System.out.println("Collision Detected");
playing = false;
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
showGameOverMessage();
}
});
}
}
}
public void pause() {
playing = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
And this is my Hurdle Class
public class Hurdles {
private Bitmap bitmap;
private int x;
private int y;
private int speed = 20;
private int maxX;
private int minX;
private int maxY;
private int minY;
private Rect detectCollision;
public Hurdles(Context context, int screenX, int screenY) {
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.hurdle);
maxX = screenX - bitmap.getWidth();
maxY = screenY;
minX = 0;
minY = 0;
Random generator = new Random();
detectCollision = new Rect(x, y, bitmap.getWidth(), bitmap.getHeight());
x = generator.nextInt(maxX);
y = minY;
}
public void update() {
y += speed;
if (y > maxY - getBitmap().getHeight()) {
Random generator = new Random();
y = minY;
x = generator.nextInt(maxX);
}
detectCollision.left = x;
detectCollision.right = x + bitmap.getWidth();
detectCollision.top = y;
detectCollision.bottom = y + bitmap.getHeight();
}
If you want to add delay/gap between hurdles, you can do it in your GamePanel constructor like :
public GamePanel(Context context, final int screenX, final int screenY) {
super(context);
int gapBetweenHurdles = 100;
int gap = 0;
for (int i = 0; i < hurdleCount; i++) {
//(screenY - gap) will move your hurdle above the screen
hurdles[i] = new Hurdles(context, screenX, screenY - gap);
//increment the gap
gap += gapBetweenHurdles;
}
......
}
So the gap between the hurdles is 100 pixels as i have written randomly. If you want specific gap, you can set gapBetweenHurdles to some percentage of the screen height.
EDIT:
You have to pass the initial X and Y position to the hurdle constructor and then in update method of the hurdle class increment the Y value and in Hurdle class, getY() should return Y.
Try changing the code like this:
handler.postDelayed(new Runnable() {
#Override
public void run() {
showGameOverMessage();
}
},timeOffsetInMillis);
If the gaps can be the same you can set timeOffsetInMillis = i * gapInMillis

How to draw an audio waveform on Android

I have a custom view that I want to use to display the amplitude of audio coming in through the microphone in a line graph.
Getting the amplitude and all that I have no problem with, and drawing the lines is not really a problem either.
What I want to do is show the amplitude starting at the far right edge, moving left. So with each new sample I want to translate the bitmap to the left, then draw a line from the last point to the new point. I'm not sure what the easiest way to achieve this is. I originally was able to do it by drawing Paths and just adding a new point to the path with each sample, the problem was that after like a minute the path was too big to be drawn. So I thought about it and wanted to switch to using a cached bitmap, translate that on each iteration, and draw from the last point to the new point. However this is tricky to do as (after experimentation). When I translate the bitmap it doesn't move the far left pixels off the bitmap, it just moves the entire bitmap in the canvas and I have no way to write pixels to the right side.
Below is a description of what I'm trying to do:
Given this:
I want to translate that to the left:
Then draw a line to a new point the space space on the right
Of course, step 2 and 3 should happen at essentially the same time.
How can I achieve this? I'm open to new ideas altogether, like perhaps saving all the points for up to 1 screen worth and drawing them out on each onDraw call. I'd prefer to just save them in a bitmap and do some kind of translation/clipping etc to achieve the same thing with perhaps less overhead.
private static final int MAX_AMPLITUDE = 32767;
float lx, ly;
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.Black);
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
height = h;
width = w;
ly = height;
lx = width;
amplitudeDivisor = ((float) MAX_AMPLITUDE / (float) height);
}
#Override
public void onDraw(Canvas canvas) {
mAmplitude = (float)(MAX_AMPLITUDE * Math.random());
float dx = width - delta;
float dy = height - (mAmplitude / amplitudeDivisor);
mCanvas.drawLine(lx, ly, dx, dy, mPaint);
mCanvas.translate(-delta, 0);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
lx = dx;
ly = dy;
delta+=10;
postInvalidateDelayed(200);
}
The above is just a sample, I'm just using a random value for the amplitude to simplify for now. I've tried a bunch of things with no luck. Any help would be greatly appreciated.
I ended up getting this working by saving the points to an array. I draw a white line before the recording starts. Note that I use an EvictingQueue from the Guava library as a circular buffer of points to render on a line. To use this, once a recording starts call start() and when it ends call stop. From your activity you will need to send MediaRecorder getMaxAmplitude() values to the updateAmplitude() method of this class, and do so at an interval of say 50 ms. The view also supports rotation.
public class AmplitudeWaveFormView extends View {
private static final String TAG = AmplitudeWaveFormView.class.getSimpleName();
private static final int MAX_AMPLITUDE = 32767;
private static final int SAMPLES_PER_SCREEN = 100;
private float mAmplitude = 0;
private Paint mRecordingPaint, mNotRecordingPaint;
private int height = -1;
private int width = -1;
private boolean mIsStarted;
private float[] lastPoints;
private int oldWidth = -1, oldHeight = -1;
private int mCurrentSample;
private float amplitudeDivisor = 1;
private float lx,ly, deltaX;
private EvictingQueue<Float> mPointQueue;
private int recordColor;
private int notRecordingColor;
public AmplitudeWaveFormView(Context context) {
super(context);
init();
}
public AmplitudeWaveFormView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AmplitudeWaveFormView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void start() {
mIsStarted = true;
}
public void stop() {
mIsStarted = false;
}
public void updateAmplitude(float amplitude) {
mAmplitude = amplitude;
postInvalidate();
}
private void init() {
recordColor = getResources().getColor(R.color.mint);
notRecordingColor = getResources().getColor(R.color.alpine);
mRecordingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRecordingPaint.setStyle(Paint.Style.STROKE);
mRecordingPaint.setStrokeWidth(5);
mRecordingPaint.setColor(recordColor);
mNotRecordingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mNotRecordingPaint.setStyle(Paint.Style.STROKE);
mNotRecordingPaint.setStrokeWidth(5);
mNotRecordingPaint.setColor(notRecordingColor);
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
height = h;
width = w;
ly = height;
lx = width;
deltaX = (float)width / (float)SAMPLES_PER_SCREEN;
amplitudeDivisor = ((float) MAX_AMPLITUDE / (float) height);
mPointQueue = EvictingQueue.create(SAMPLES_PER_SCREEN * 4);
if (lastPoints != null && lastPoints.length > 0) {
float xScale = (float) width/oldWidth;
float yScale = (float) height/oldHeight;
Matrix matrix = new Matrix();
matrix.setScale(xScale, yScale);
matrix.mapPoints(lastPoints);
mPointQueue.addAll(Floats.asList(lastPoints));
ly = lastPoints[lastPoints.length-1];
lx= lastPoints[lastPoints.length -2];
lastPoints = null;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mCurrentSample = bundle.getInt("sample");
lastPoints = bundle.getFloatArray("lines");
oldWidth = bundle.getInt("oldWidth");
oldHeight = bundle.getInt("oldHeight");
state = ((Bundle) state).getParcelable("parent");
}
super.onRestoreInstanceState(state);
}
#Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putFloatArray("lines", Floats.toArray(mPointQueue));
bundle.putInt("sample", mCurrentSample);
bundle.putParcelable("parent", super.onSaveInstanceState());
bundle.putInt("oldWidth", width);
bundle.putInt("oldHeight", height);
return bundle;
}
#Override
public void onDraw(Canvas canvas) {
if (mIsStarted) {
float x = lx + deltaX;
float y = height - (mAmplitude / amplitudeDivisor);
mPointQueue.add(lx);
mPointQueue.add(ly);
mPointQueue.add(x);
mPointQueue.add(y);
lastPoints = Floats.toArray(mPointQueue);
lx = x;
ly = y;
}
if (lastPoints != null && lastPoints.length > 0) {
int len = mPointQueue.size() / 4 >= SAMPLES_PER_SCREEN ? SAMPLES_PER_SCREEN * 4 : mPointQueue.size();
float translateX = width - lastPoints[lastPoints.length - 2];
canvas.translate(translateX, 0);
canvas.drawLines(lastPoints, 0, len, mRecordingPaint);
}
if (mCurrentSample <= SAMPLES_PER_SCREEN) {
drawNotRecordingLine(canvas);
}
mCurrentSample++;
}
private void drawNotRecordingLine(Canvas canvas) {
canvas.drawLine(0,height, width, height, mNotRecordingPaint);
}
}

Making new images on a timer

Hey I am trying to make a new icon every 800 milliseconds, and position their x values randomly, and their y below the android screen. I want to get these icon to move across the screen vertically, but instead getting to work I keep getting multiple icons stacked on top of each other. Any Help is appreciated.
public class GameView extends SurfaceView{
Bitmap icon;
LinkedList<Sprite> balloonList;
Timer spawnTimer;
TimerTask spawnTask;
Random rand;
SurfaceHolder holder;
int spawnTime;
final int SPAWN_DIFFERENCE = 10;
public GameView(Context context){
super(context);
icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
holder = getHolder();
balloonList = new LinkedList<Sprite>();
rand = new Random();
spawnTime = 1;
spawnTimer = new Timer();
spawnTask = new TimerTask() {
#Override
public void run() {
spawnNewBalloon();
}
};
spawnTimer.scheduleAtFixedRate(spawnTask, 800, 800);
}
private void spawnNewBalloon(){
Canvas canvas = holder.lockCanvas();
if(spawnTime == 0){
balloonList.insertTail(new Sprite(icon, rand.nextInt(getWidth() - icon.getWidth() / 2), getHeight()));
balloonList.getTailData().draw(canvas);
spawnTime = SPAWN_DIFFERENCE;
}
else spawnTime--;
if(balloonList.length() == 0){
for(int i = 0; i < balloonList.length(); i++) {
balloonList.getData(i).update(canvas);
}
}
holder.unlockCanvasAndPost(canvas);
}
}
class Sprite{
public Bitmap image;
public int x, y;
public Sprite(Bitmap image, int x, int y){
this.image = image;
this.x = x;
this.y = y;
}
public void draw(Canvas canvas){
canvas.drawBitmap(image, x, y, null);
}
public void update(Canvas canvas){
if(y > 0) y -= 15;
draw(canvas);
}
}

imageview not tracing the path android

I am using translate animation to make imageview trace a path.Initially I am just making my imageview to translate through a particular set of points but it does not.here is my code in ondraw method:
#Override
public void onDraw(Canvas canvas) {
Log.w(this.getClass().getName(),"onDraw of Balls called");
super.onDraw(canvas);
mCanvas = canvas;
canvas.drawLine(0, 240, 160, 0, mPaint);
canvas.drawLine(160, 0, 320, 240, mPaint);
mBal = (ImageView)findViewById(R.id.strike);
TranslateAnimation mTrans = new TranslateAnimation(0, 240, 160, 0);
mTrans.setDuration(6000);
mTrans.setFillEnabled(true);
mTrans.setFillAfter(true);
mTrans.start();
}
plz help.
=============================================================
Edit 1:-
This is my modified code but the translation is still not working.PLz help
#Override
public void onDraw(Canvas canvas) {
Log.w(this.getClass().getName(),"onDraw of Balls called");
BallsOnDraw(canvas);
}
void BallsOnDraw(Canvas canvas)
{
canvas.drawLine(0, 240, 160, 0, mPaint);
canvas.drawLine(160, 0, 320, 240, mPaint);
TranslateAnimation mTrans = new TranslateAnimation(0, 320, 0,240);
mTrans.setDuration(6000);
SitoliaActivity.mBal.startAnimation(mTrans);
}
You are mixing custom classes with ImageViews and animations. IMHO you should use only one of the methods. Also I don't see that you actually connect the TranslationAnimation with the ImageView itself.
So, one solution would be to use TranslateAnimation, but then you would need to call mBal.startAnimation(mTrans) instead of mTrans.start().
The other solution would be to use a custom view, override onDraw, and handle the animation yourself:
save the current time (System.curentTimeMillis)
draw the Bitmap directly to the Canvas using canvas.drawBitmap(bitmap, x, y, paint);
update the coordinates based on the elapsed time
if the animation should still run, call postInvalidateDelayed(40);
Here is an example custom view:
public class C extends View {
private static final float FROM_X = 400;
private static final float FROM_Y = 100;
private static final float TO_X = 10;
private static final float TO_Y = 400;
private static final int DURATION = 2*1000; // 2 seconds
private Bitmap mImage;
private long mStart = -1;
private Paint mPaint = new Paint();
public C(Context context) {
super(context);
mImage = ((BitmapDrawable)context.getResources().getDrawable(R.drawable.icon)).getBitmap();
}
#Override
public void onDraw(Canvas canvas) {
boolean finished = false;
if (mStart == -1) {
// Time to start the animation
mStart = SystemClock.uptimeMillis();
}
int elapsed = (int)(SystemClock.uptimeMillis() - mStart);
if (elapsed >= DURATION) {
elapsed = DURATION;
finished = true;
}
float x = FROM_X + (TO_X - FROM_X) * elapsed / DURATION;
float y = FROM_Y + (TO_Y - FROM_Y) * elapsed / DURATION;
canvas.drawBitmap(mImage, x, y, mPaint);
if (!finished) {
postInvalidateDelayed(10);
}
}
}

In my Android app,how do I move the images from their position?

I have a class Panel which overrides onDraw() method as below.I have two images mentioned in canvas.drawBitmap(),as of now their position is fixed. Is it possible for me to move these two images from bottom to top on my emulator? Code is:
class Panel extends View {
public Panel(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
Bitmap image1 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
canvas.drawColor(Color.CYAN);
canvas.drawBitmap(Image1, 10, 10, null);
Bitmap Image2 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
canvas.drawBitmap(Image2, 100, 100, null);
}
}
Re-write your code like this canvas.drawBitmap(Image1, x, y, null);.
And write a thread to change the value of x or y over time.
Once you change the value of x or y, please call invalidate in your Panel.java to redraw the view.
use variables in onDraw call for values of x,y where you want to draw. I modified your code to redraw itself to new coordinates after 10 seconds of first draw call. It will give you an idea to use variables inside draw call to dynamically change where you draw. Hope I have answered your question
class Panel extends View {
public Panel(Context context) {
super(context);
}
Handler handler = new Handler() {
public void handleMessage(Message msg) {
x1 = 20;
y1 = 20;
x2 = 120;
y2 = 120;
invalidate();
}
}
private int x1 = 10;
private int y1 = 10;
private int x2 = 100;
private int y2 = 100;
private boolean drawAfterTenSecs = false;
#Override
public void onDraw(Canvas canvas) {
Bitmap image1 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
canvas.drawColor(Color.CYAN);
canvas.drawBitmap(Image1, x1, y1, null);
Bitmap Image2 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
canvas.drawBitmap(Image2, x2, y2, null);
if(!drawAfterTenSecs) {
handler.sendEmptyMessageDelayed (-1, 10000)
drawAfterTenSecs = true;
}
}
}

Categories

Resources