Android canvas.translate() - android

I am using canvas.translate to change the coodinates of my canvas so that the viewport changes
my code :
public class draw extends View {
View v;
Paint paint;
int width;
int height;
int view_x;
int view_y;
static final int MAX_GAME_SPEED = 25;
static int fps;
public draw(Context context) {
super(context);
// tb.loadimg();
Thread myThread = new Thread(new UpdateThread());
myThread.start();
}
#Override
protected void onDraw(Canvas c) {
super.onDraw(c);
paint = new Paint(); // Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
// get screen size
WindowManager wm = (WindowManager) this.getContext().getSystemService(
Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
width = display.getWidth(); // deprecated
height = display.getHeight(); // deprecated
// make the entire canvas white
paint.setColor(Color.WHITE);
c.drawPaint(paint);
if (tb.dirt != null && tb.stone != null && tb.sand != null
&& tb.gold != null && tb.iron != null && tb.coal != null
&& tb.kies != null && tb.diamond != null && tb.redstone != null
&& tb.lava != null && tb.azur != null && tb.water != null) {
c.drawBitmap(tb.dirt, 0, 0, paint);
c.drawBitmap(tb.stone, 0, 50, paint);
c.drawBitmap(tb.sand, 0, 100, paint);
c.drawBitmap(tb.gold, 0, 150, paint);
c.drawBitmap(tb.iron, 50, 0, paint);
c.drawBitmap(tb.coal, 50, 50, paint);
c.drawBitmap(tb.kies, 50, 100, paint);
c.drawBitmap(tb.diamond, 50, 150, paint);
c.drawBitmap(tb.redstone, 100, 0, paint);
c.drawBitmap(tb.lava, 100, 50, paint);
c.drawBitmap(tb.azur, 100, 100, paint);
c.drawBitmap(tb.water, 100, 150, paint);
}
if (tb.map == null) {
}
view_x = 100;
view_y = 100;
c.translate(0, -4);
}
public Handler updateHandler = new Handler() {
/** Gets called on every message that is received */
// #Override
public void handleMessage(Message msg) {
invalidate();
super.handleMessage(msg);
}
};
public class UpdateThread implements Runnable {
#Override
public void run() {
while (true) { // Game Loop
long startTime = System.currentTimeMillis();
draw.this.updateHandler.sendEmptyMessage(0); // veranlassen,
// dass paint()
// erneut
// aufgerufen
// werden soll
// for (int i=0; i<999999; i++); //Bremse
Thread.yield();
long executionTime = System.currentTimeMillis() - startTime;
if (executionTime < MAX_GAME_SPEED) {
try {
Thread.sleep(MAX_GAME_SPEED - (int) executionTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fps = 1000 / MAX_GAME_SPEED;
} else
fps = (int) (1000 / executionTime);
}
}
}
}
but simply nothing happens
I have seen many online examples but I just don't get throug my problem -.-

I think you have to call translate() first, then draw your objects

I think if you do translate(0,-4),
now your c coodinates is out of the screeen

Related

Android - How to increase application performance with animated backgrounds?

I have and animated background for all screens in the application. I have
created that animated background using surface view and thread.
Here
is my SurfaceView class.
public class FloatingCirclesPanel extends SurfaceView implements
SurfaceHolder.Callback {
private static final String TAG = FloatingCirclesPanel.class
.getSimpleName();
public MainThread thread;
private Circle circle_white1;
private Circle circle_blue1;
private Circle circle_white2;
private Circle circle_blue2;
private Bitmap scaled;
Canvas canvas = null;
private Display mdisp;
private Context context;
private RotateAnimation anim;
private Matrix matrix;
#SuppressLint("NewApi")
public FloatingCirclesPanel(Context context) {
super(context);
this.context = context;
matrix = new Matrix();
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// create droid and load bitmap
circle_white1 = new Circle(BitmapFactory.decodeResource(getResources(),
R.drawable.circle), 50, 50);
mdisp = ((Activity) getContext()).getWindowManager()
.getDefaultDisplay();
Point mdispSize = new Point();
mdisp.getSize(mdispSize);
int maxX = mdispSize.x;
int maxY = mdispSize.y;
Bitmap b = scaleDown(BitmapFactory.decodeResource(getResources(),
R.drawable.circle_img), 300, true);
Bitmap b1 = scaleDown(BitmapFactory.decodeResource(getResources(),
R.drawable.circle_img), 130, true);
Bitmap b2 = scaleDown(
BitmapFactory.decodeResource(getResources(), R.drawable.circle),
100, true);
// droid1 = new Droid(BitmapFactory.decodeResource(getResources(),
// R.drawable.icon), 150, 150);
if (b != null) {
circle_blue1 = new Circle(b, 500, 500);
} else {
circle_blue1 = new Circle(BitmapFactory.decodeResource(
getResources(), R.drawable.circle_img), 500, 500);
}
if (b1 != null) {
circle_white2 = new Circle(b1, 800, 800);
} else {
circle_white2 = new Circle(BitmapFactory.decodeResource(
getResources(), R.drawable.circle), 800, 800);
}
if (b2 != null) {
circle_blue2 = new Circle(b2, 1500, 1500);
} else {
circle_blue2 = new Circle(BitmapFactory.decodeResource(
getResources(), R.drawable.circle_img), 1500, 1500);
}
// droid3 = new Droid(BitmapFactory.decodeResource(getResources(),
// R.drawable.icon), 150, 150);
// create the game loop thread
thread = new MainThread(getHolder(), this, context);
// make the GamePanel focusable so it can handle events
setFocusable(true);
}
public FloatingCirclesPanel(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public FloatingCirclesPanel(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
public static Bitmap scaleDown(Bitmap realImage, float maxImageSize,
boolean filter) {
float ratio = Math.min((float) maxImageSize / realImage.getWidth(),
(float) maxImageSize / realImage.getHeight());
int width = Math.round((float) ratio * realImage.getWidth());
int height = Math.round((float) ratio * realImage.getHeight());
Bitmap newBitmap = Bitmap.createScaledBitmap(realImage, width, height,
filter);
return newBitmap;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and
// we can safely start the game loop
if (thread != null && (thread.getState() == Thread.State.NEW)) {
thread.setRunning(true);// riga originale
thread.start();// riga originale
}
// after a pause it starts the thread again
else if (thread != null && thread.getState() == Thread.State.TERMINATED) {
thread = new MainThread(getHolder(), this, context);
;
thread.setRunning(true);
thread.start(); // Start a new thread
}
// thread.setRunning(true);
// thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.interrupt();
retry = false;
thread = null;
} catch (Exception e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
public void render(Canvas canvas) {
if (canvas != null) {
Rect dest = new Rect(0, 0, getWidth(), getHeight());
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
paint.setFilterBitmap(true);
paint.setDither(true);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.splashscreen);
canvas.drawBitmap(bitmap, null, dest, paint);
// canvas.drawColor(Color.BLUE);
circle_white1.draw(canvas);
circle_blue1.draw(canvas);
circle_white2.draw(canvas);
circle_blue2.draw(canvas);
}
}
private void createAnimation(Canvas canvas) {
anim = new RotateAnimation(0, 360, getWidth() / 2, getHeight() / 2);
anim.setRepeatMode(Animation.RESTART);
anim.setRepeatCount(Animation.INFINITE);
anim.setDuration(10000L);
((Activity) context).runOnUiThread(new Runnable() {
#Override
public void run() {
startAnimation(anim);
}
});
}
/**
* This is the game update method. It iterates through all the objects and
* calls their update method if they have one or calls specific engine's
* update method.
*/
public void update() {
// check collision with right wall if heading right
if (circle_white1.getSpeed().getxDirection() == Speed.DIRECTION_RIGHT
&& circle_white1.getX() + circle_white1.getBitmap().getWidth()
/ 2 >= getWidth()) {
circle_white1.getSpeed().toggleXDirection();
}
// check collision with left wall if heading left
if (circle_white1.getSpeed().getxDirection() == Speed.DIRECTION_LEFT
&& circle_white1.getX() - circle_white1.getBitmap().getWidth()
/ 2 <= 0) {
circle_white1.getSpeed().toggleXDirection();
}
// check collision with bottom wall if heading down
if (circle_white1.getSpeed().getyDirection() == Speed.DIRECTION_DOWN
&& circle_white1.getY() + circle_white1.getBitmap().getHeight()
/ 2 >= getHeight()) {
circle_white1.getSpeed().toggleYDirection();
}
// check collision with top wall if heading up
if (circle_white1.getSpeed().getyDirection() == Speed.DIRECTION_UP
&& circle_white1.getY() - circle_white1.getBitmap().getHeight()
/ 2 <= 0) {
circle_white1.getSpeed().toggleYDirection();
}
// Update the lone droid
circle_white1.update();
if (circle_blue1.getSpeed().getxDirection() == Speed.DIRECTION_RIGHT
&& circle_blue1.getX() + circle_blue1.getBitmap().getWidth()
/ 2 >= getWidth()) {
circle_blue1.getSpeed().toggleXDirection();
}
// check collision with left wall if heading left
if (circle_blue1.getSpeed().getxDirection() == Speed.DIRECTION_LEFT
&& circle_blue1.getX() - circle_blue1.getBitmap().getWidth()
/ 2 <= 0) {
circle_blue1.getSpeed().toggleXDirection();
}
// check collision with bottom wall if heading down
if (circle_blue1.getSpeed().getyDirection() == Speed.DIRECTION_DOWN
&& circle_blue1.getY() + circle_blue1.getBitmap().getHeight()
/ 2 >= getHeight()) {
circle_blue1.getSpeed().toggleYDirection();
}
// check collision with top wall if heading up
if (circle_blue1.getSpeed().getyDirection() == Speed.DIRECTION_UP
&& circle_blue1.getY() - circle_blue1.getBitmap().getHeight()
/ 2 <= 0) {
circle_blue1.getSpeed().toggleYDirection();
}
// Update the lone droid1
circle_blue1.update();
if (circle_white2.getSpeed().getxDirection() == Speed.DIRECTION_RIGHT
&& circle_white2.getX() + circle_white2.getBitmap().getWidth()
/ 2 >= getWidth()) {
circle_white2.getSpeed().toggleXDirection();
}
// check collision with left wall if heading left
if (circle_white2.getSpeed().getxDirection() == Speed.DIRECTION_LEFT
&& circle_white2.getX() - circle_white2.getBitmap().getWidth()
/ 2 <= 0) {
circle_white2.getSpeed().toggleXDirection();
}
// check collision with bottom wall if heading down
if (circle_white2.getSpeed().getyDirection() == Speed.DIRECTION_DOWN
&& circle_white2.getY() + circle_white2.getBitmap().getHeight()
/ 2 >= getHeight()) {
circle_white2.getSpeed().toggleYDirection();
}
// check collision with top wall if heading up
if (circle_white2.getSpeed().getyDirection() == Speed.DIRECTION_UP
&& circle_white2.getY() - circle_white2.getBitmap().getHeight()
/ 2 <= 0) {
circle_white2.getSpeed().toggleYDirection();
}
// Update the lone droid2
circle_white2.update();
if (circle_blue2.getSpeed().getxDirection() == Speed.DIRECTION_RIGHT
&& circle_blue2.getX() + circle_blue2.getBitmap().getWidth()
/ 2 >= getWidth()) {
circle_blue2.getSpeed().toggleXDirection();
}
// check collision with left wall if heading left
if (circle_blue2.getSpeed().getxDirection() == Speed.DIRECTION_LEFT
&& circle_blue2.getX() - circle_blue2.getBitmap().getWidth()
/ 2 <= 0) {
circle_blue2.getSpeed().toggleXDirection();
}
// check collision with bottom wall if heading down
if (circle_blue2.getSpeed().getyDirection() == Speed.DIRECTION_DOWN
&& circle_blue2.getY() + circle_blue2.getBitmap().getHeight()
/ 2 >= getHeight()) {
circle_blue2.getSpeed().toggleYDirection();
}
// check collision with top wall if heading up
if (circle_blue2.getSpeed().getyDirection() == Speed.DIRECTION_UP
&& circle_blue2.getY() - circle_blue2.getBitmap().getHeight()
/ 2 <= 0) {
circle_blue2.getSpeed().toggleYDirection();
}
// Update the lone droid3
circle_blue2.update();
}
}
I am adding that surfaceView into my activity like below
#Override
protected void onStart() {
super.onStart();
View childView = getLayoutInflater().inflate(R.layout.main, null);
circleAnimation = new FloatingCirclesPanel(this);
setContentView(circleAnimation);
final ImageView image = (ImageView) childView.findViewById(R.id.logout);
Now the problem is the app is running slow while navigating through screens in the app.
If I add that SurfaceView code in onStart/onResume entire views are redesigning twice and causing performance issues.
How can I improve performance?

Adding a timer to surfaceview for control the game end

How can i create a textPaint as a count down timer to control the game end?
I have tried to use countDownTimer with drawText to do something like that,it can show in surfaceview but it doesn't update for each onTick.
public class CfView extends SurfaceView {
/** The activity object. */
private final Activity activity;
/** The answer object. */
private static Answer answer;
private static int score;
/** The InputPanel object. */
private static InputPanel inputPanel;
/** The AnswerPanel object. */
private static AnswerPanel answerPanel;
/** Keyboard panel height. */
private static final float INPUT_PANEL_HEIGHT_WEIGHT = 0.7f;
/** Answer panel height. */
private static final float ANSWER_PANEL_HEIGHT_WEIGHT = 0.3f;
private Timer timer;
private String countDownTime;
private boolean gameOver;
private UserInput userInput = new UserInput();
private CountDownTimer cdTimer;
/** Saving and handling of user input of touch events. */
private class UserInput {
/** Whether there is a user input present. */
boolean present = false;
/** Action of the user input {#link MotionEvent}. */
int action;
/** x, y positions of the user input {#link MotionEvent}. */
int x, y;
synchronized void save(MotionEvent event) {
present = true;
action = event.getAction();
x = (int) event.getX();
y = (int) event.getY();
handle();
}
synchronized void handle() {
int pos = -1;
if (present) {
cdTimer.start();
if (action == MotionEvent.ACTION_UP) {
int[] clickedBallData = inputPanel.checkballStroked(x, y);
if (clickedBallData != null) {
pos = clickedBallData[0];
int value = clickedBallData[1];
if (answer.isCorrect(value)) {
if (answer.isHCF(value)) {
score += 500;
} else {
score += 100;
}
} else {
score -= 100;
if (score < 0)
score = 0;
}
inputPanel.updateInputPanel(pos, answer);
}
}
drawGameComponent();
present = false;
}
}
}
private class AnimationTask extends TimerTask {
#Override
public void run() {
// TODO Auto-generated method stub
Log.d("thread", "run");
userInput.handle();
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
answerPanel.drawOn(canvas);
inputPanel.drawOn(canvas);
Paint textPaint = new Paint();
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(50);
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("Score : " + score, getWidth() * 0.7f, 50,
textPaint);
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(50);
textPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("Time : " + countDownTime + "s", 0, 50,
textPaint);
if (gameOver) {
textPaint.setTextSize(2 * 30);
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("Times up", getWidth() / 2,
getHeight() / 2, textPaint);
canvas.drawText("Score : " + score, getWidth() / 2,
getHeight() / 2 + (2 * 30), textPaint);
}
getHolder().unlockCanvasAndPost(canvas);
}
}
}
public CfView(Activity activity) {
super(activity);
setFocusableInTouchMode(true); // For getting key events
cdTimer = new CountDownTimer(60000, 1000) {
public void onTick(long millisUntilFinished) {
countDownTime = String.valueOf(millisUntilFinished / 1000);
invalidate();
}
public void onFinish() {
countDownTime = 0 + "";
gameOver = true;
cancel();
}
};
setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
userInput.save(event);
return true;
}
});
this.activity = activity;
timer = new Timer();
timer.schedule(new AnimationTask() {
#Override
public void run() {
while (getWidth() == 0)
; // Wait for layout
newGame(true);
}
}, 0);
}
public void resume() {
if (timer == null)
timer = new Timer();
timer.schedule(new AnimationTask(), 0, 0);
}
public void pause() {
timer.cancel();
timer = null;
}
private void drawGameComponent() {
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
answerPanel.drawOn(canvas);
inputPanel.drawOn(canvas);
Paint textPaint = new Paint();
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(50);
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("Score : " + score, getWidth() * 0.7f, 50,
textPaint);
getHolder().unlockCanvasAndPost(canvas);
}
}
public void drawTimeText() {
Canvas canvas = getHolder().lockCanvas();
Paint textPaint = new Paint();
if (canvas != null) {
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(50);
textPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("Time : " + countDownTime + "s", 0, 50, textPaint);
if (gameOver) {
textPaint.setTextSize(2 * 30);
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("Times up", getWidth() / 2, getHeight() / 2,
textPaint);
canvas.drawText("Score : " + score, getWidth() / 2, getHeight()
/ 2 + (2 * 30), textPaint);
}
getHolder().unlockCanvasAndPost(canvas);
}
}
public void newGame(boolean newGame) {
score = 0;
gameOver = false;
if (newGame) {
answer = new Answer(activity);
Log.d("CF", getHeight() + ";" + getWidth());
answerPanel = new AnswerPanel(activity, 0, 0, getWidth(),
(int) (getHeight() * ANSWER_PANEL_HEIGHT_WEIGHT), answer);
inputPanel = new InputPanel(activity, 0,
(int) (getHeight() * (ANSWER_PANEL_HEIGHT_WEIGHT)),
getWidth(), (int) (getHeight()), answer);
} else {
answer.newNumber();
inputPanel.resetInputPanel();
answerPanel.resetAnswerPanel(answer);
}
drawGameComponent();
}
public void updateCategory(Answer.Level level) {
answer.updateCategory(level);
newGame(false);
}
#Override
protected boolean verifyDrawable(Drawable who) {
return true;
}
#Override
public void invalidateDrawable(Drawable drawable) {
}
}

Android: Order of layers in result drawable is different each time when i call function

i have following code:
public Drawable getMergedIcon(Drawable origIcon) {
Bitmap underlayBitmap = null, overlayBitmap = null;
if (isSupportUnderlays()) {
int overlayId = r.nextInt(Underlays.size());
underlayBitmap = Underlays.get(overlayId);
if(Overlays.size() == Underlays.size()) {
overlayBitmap = Overlays.get(overlayId);
}
if(underlayBitmap == null) {
return origIcon;
}
boolean needResize = false;
int targetWidth, targetHeight;
Bitmap iconBitmap = ((BitmapDrawable) origIcon).getBitmap();
if(iconBitmap.getHeight() > underlayBitmap.getHeight()) {
targetWidth = iconBitmap.getWidth();
targetHeight = iconBitmap.getHeight();
needResize = true;
} else {
targetWidth = underlayBitmap.getWidth();
targetHeight = underlayBitmap.getHeight();
}
Paint paint = new Paint();
Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, iconBitmap.getConfig());
Canvas canvas = new Canvas(result);
if(!needResize) {
canvas.drawBitmap(underlayBitmap, 0, 0, paint);
} else {
canvas.drawBitmap(Bitmap.createScaledBitmap(underlayBitmap, targetWidth,targetHeight, false), 0, 0, paint);
}
int left = (targetHeight - (int) (targetHeight * iconScale)) / 2;
int top = (targetWidth - (int) (targetWidth * iconScale)) / 2;
iconBitmap = Bitmap.createScaledBitmap(iconBitmap, (int) (targetWidth * iconScale),(int) (targetHeight * iconScale), false);
canvas.drawBitmap(iconBitmap, left, top, paint);
if(overlayBitmap != null) {
if(!needResize) {
canvas.drawBitmap(overlayBitmap, 0, 0, paint);
} else {
canvas.drawBitmap(Bitmap.createScaledBitmap(overlayBitmap, targetWidth,targetHeight, false), 0, 0, paint);
}
}
canvas.save();
BitmapDrawable icon = new BitmapDrawable(mContext.getResources(), result);
return icon;
}
return origIcon;
}
it takes random bitmap from overlays and underlays and places icon between it
but when i call this function layers is missed randomly.
f.e. on first call i have underlay and icon, on second overlay and icon and etc
i always have icon, but with only one layer(underlay or overlay) and never with bot of them.
Code was workable, was my stupid mistake...
I just placed wrong drawables in arrays.

How to optimize drawing text on canvas

I have window with 3 circles, they are rotating simultaneously. Everything is good until a Add text to the circles, then the rotation starts to lagging.
How can i optimize drawing on canvas ?
This is my code:
#Override
protected void onDraw(final Canvas canvas) {
if (mPaint == null) {
mPaint = new Paint();
mPaint.setTextSize(20f);
}
drawUpperCircle(canvas);
drawBottomCircle(canvas);
drawMainCircle(canvas);
try {
Thread.sleep(1, 1);
invalidate();
mRotation += 0.9;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
super.onDraw(canvas);
}
private void drawUpperCircle(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, 0, mUpperCircleCentr);
mPaint.setColor(Color.CYAN);
canvas.drawCircle(0, mUpperCircleCentr, mUpperCirclRadius, mPaint);
mPaint.setColor(Color.BLACK);
for (int i = 0; i < SEG_COUNT; i++) {
canvas.rotate(SEG_IN_GRAD, 0, mUpperCircleCentr);
canvas.drawLine(0, mUpperCircleCentr, mUpperCirclRadius, mUpperCircleCentr, mPaint);
// canvas.drawText("my text" + String.valueOf(i), mUpperCirclRadius * 2 / 3, mUpperCircleCentr - 4, mPaint);
}
canvas.restore();
}
private void drawBottomCircle(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, 0, mBottomCircleCentr);
mPaint.setColor(Color.RED);
canvas.drawCircle(0, mBottomCircleCentr, mBottomCirclRadius, mPaint);
mPaint.setColor(Color.BLACK);
for (int i = 0; i < SEG_COUNT; i++) {
canvas.rotate(SEG_IN_GRAD, 0, mBottomCircleCentr);
canvas.drawLine(0, mBottomCircleCentr, mBottomCirclRadius, mBottomCircleCentr, mPaint);
// canvas.drawText("my text" + String.valueOf(i), mBottomCirclRadius * 2 / 3, mBottomCircleCentr - 4, mPaint);
}
canvas.restore();
}
private void drawMainCircle(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, 0, mMainCircleCentr);
mPaint.setColor(Color.argb(100, 100, 100, 100));
canvas.drawCircle(0, mMainCircleCentr, mMainCirclRadius, mPaint);
mPaint.setColor(Color.BLACK);
for (int i = 0; i < SEG_COUNT; i++) {
canvas.rotate(SEG_IN_GRAD, 0, mMainCircleCentr);
canvas.drawLine(0, mMainCircleCentr, mMainCirclRadius, mMainCircleCentr, mPaint);
canvas.drawText("my text" + String.valueOf(i), mMainCirclRadius * 2 / 3, mMainCircleCentr - 4, mPaint);
}
canvas.restore();
}
EDIT
To improve performance and remove drawing from UI thread I have Used Double Buffering With SurfaceView and implement #Morgans optimizations. That is how it realized.
DrawView.java
public class DrawView extends SurfaceView implements SurfaceHolder.Callback {
...............................................................
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float currentX = event.getX();
float currentY = event.getY();
float deltaX, deltaY;
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
// Modify rotational angles according to movement
deltaX = currentX - previousX;
deltaY = currentY - previousY;
mDrawThread.mRotation += deltaY * 180 / getHeight();
}
// Save current x, y
previousX = currentX;
previousY = currentY;
return true; // Event handled
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mDrawThread = new DrawThread(getHolder(), this);
mDrawThread.setRunning(true);
mDrawThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
boolean retry = true;
mDrawThread.setRunning(false);
while (retry) {
try {
mDrawThread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
And the main work is done in the DrawThread.java
public class DrawThread extends Thread {
private ArrayList<Path> mMainCirclePaths = new ArrayList<Path>(SEG_COUNT);
private ArrayList<Path> mUpperCirclePaths = new ArrayList<Path>(SEG_COUNT);
private ArrayList<Path> mCenterCirclePaths = new ArrayList<Path>(SEG_COUNT);
private ArrayList<Path> mBottomCirclePaths = new ArrayList<Path>(SEG_COUNT);
private boolean mRun = false;
private SurfaceHolder mSurfaceHolder;
private DrawView mDrawView;
private Paint mPaint;
private CirclesModel mCirclesModel;
public float mRotation = 0;
public DrawThread(SurfaceHolder surfaceHolder, DrawView drawView) {
mSurfaceHolder = surfaceHolder;
mDrawView = drawView;
mCirclesModel = new CirclesModel(mDrawView.getHeight());
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(18f);
initPaths();
}
public void setRunning(boolean b) {
mRun = b;
}
#Override
public void run() {
while (mRun) {
Canvas canvas = null;
try {
canvas = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
drawMainCircle(canvas);
mPaint.setColor(Color.WHITE);
canvas.drawCircle(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y],
mCirclesModel.mSmallCirclesRadius, mPaint);
drawCenterCircle(canvas);
drawUpperCircle(canvas);
drawBottomCircle(canvas);
//mRotation += 0.5f;
}
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
private void drawMainCircle(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y]);
float rot = mRotation;
mPaint.setColor(Color.LTGRAY/* argb(100, 255, 255, 255) */);
canvas.drawCircle(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y],
mCirclesModel.mBigCirclesRadius, mPaint);
mPaint.setColor(Color.BLACK);
for (int i = 0; i < SEG_COUNT; i++) {
canvas.rotate(SEG_IN_GRAD, mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y]);
rot += SEG_IN_GRAD;
float absRot = Math.abs(rot % 360);
if (absRot > mCirclesModel.mMainCircleSegment[0] && absRot < mCirclesModel.mMainCircleSegment[1]) {
continue;
}
canvas.drawLine(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y],
mCirclesModel.mBigCirclesRadius, mCirclesModel.mMainCircleCentr[CirclesModel.Y], mPaint);
canvas.drawPath(mMainCirclePaths.get(i), mPaint);
// canvas.drawText("my text" + String.valueOf(i),
// mMainCirclRadius * 2 / 3, mMainCircleCentr - 4, mPaint);
}
canvas.restore();
}
.................................................................
}
Double Buffering is implemented in the two lines of code
canvas = mSurfaceHolder.lockCanvas(null); here I take from surface view canvas in which i will draw next frame.
mSurfaceHolder.unlockCanvasAndPost(canvas); here I am overlaping current image on SurfaceView with new canwas, this is the moment where image changes. Be aware if you have transparent elements then the previous image will be still visible, Image is not replaced, but overlaped.
Below is a version of your code that contains a few optimizations.
First, I try not to draw the lines and text that currently offscreen. I do this by tracking the rotation angle, and skipping the drawing for net rotations between 90 and 270 degrees. On my 2.3 simulator this improved performance overall by 25%.
Second, I "cache" the strings I am going to draw by initializing an array (ArrayList<Path>) with one Path for each string I need to draw. I do this in the same place you were one-time initializing the mPaint. Then I draw the strings using canvas.drawPath(...). On my 2.3 simulator this improved performance by another 33%. The net effect was to about double the rotation speed. Also, it stopped the text from "wiggling around".
A few other notes:
I removed the Thread.sleep(1,1). Not sure exactly what you were trying to accomplish with that.
I changed rotation delta to 1.0 from 0.9. Not sure why you were using 0.9. Note that if you change to back, my "log time it takes to rotate 10 degrees" will not quite work since mRotation % 10 may seldom be 0.
On a 4.1 simulator, the rotation was generally much faster (about 4x) than on my 2.3 simulator. And a 4.1 device was faster yet.
public class AnimView extends View {
Paint mPaint;
ArrayList<Path> mTextPaths;
float mRotation = 0f;
float mUpperCircleCentr = 150f;
float mUpperCirclRadius = 150f;
private static final int SEG_COUNT = 60;
private static final float SEG_IN_GRAD = 360.0f / SEG_COUNT;
float mBottomCircleCentr = 450f;
float mBottomCirclRadius = 150f;
float mMainCircleCentr = 300f;
float mMainCirclRadius = 300f;
long mLastMillis = 0L;
// ctors removed
#Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
if (mPaint == null) {
mPaint = new Paint();
mPaint.setTextSize(20f);
// init text paths
mTextPaths = new ArrayList<Path>(SEG_COUNT);
for (int i = 0; i < SEG_COUNT; i++) {
Path path = new Path();
String s = "my text" + String.valueOf(i);
mPaint.getTextPath(s, 0, s.length(), mMainCirclRadius * 2 / 3, mMainCircleCentr - 4, path);
path.close(); // not required on 2.2/2.3 devices
mTextPaths.add(path);
}
}
if (mLastMillis == 0L) {
mLastMillis = System.currentTimeMillis();
}
drawUpperCircle(canvas);
drawBottomCircle(canvas);
drawMainCircle(canvas);
invalidate();
if (((int) mRotation) % 10 == 0) {
long millis = System.currentTimeMillis();
Log.w("AnimateCanvas", "OnDraw called with mRotation == " + mRotation);
Log.w("AnimateCanvas", "Last 10 degrees took millis: " + (millis - mLastMillis));
mLastMillis = millis;
}
}
private void drawUpperCircle(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, 0, mUpperCircleCentr);
float rot = mRotation;
mPaint.setColor(Color.CYAN);
canvas.drawCircle(0, mUpperCircleCentr, mUpperCirclRadius, mPaint);
mPaint.setColor(Color.BLACK);
for (int i = 0; i < SEG_COUNT; i++) {
canvas.rotate(SEG_IN_GRAD, 0, mUpperCircleCentr);
rot += SEG_IN_GRAD;
if (rot % 360 > 90 && rot % 360 < 270)
continue;
canvas.drawLine(0, mUpperCircleCentr, mUpperCirclRadius, mUpperCircleCentr, mPaint);
}
canvas.restore();
}
private void drawBottomCircle(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, 0, mBottomCircleCentr);
float rot = mRotation;
mPaint.setColor(Color.RED);
canvas.drawCircle(0, mBottomCircleCentr, mBottomCirclRadius, mPaint);
mPaint.setColor(Color.BLACK);
for (int i = 0; i < SEG_COUNT; i++) {
canvas.rotate(SEG_IN_GRAD, 0, mBottomCircleCentr);
rot += SEG_IN_GRAD;
if (rot % 360 > 90 && rot % 360 < 270)
continue;
canvas.drawLine(0, mBottomCircleCentr, mBottomCirclRadius, mBottomCircleCentr, mPaint);
}
canvas.restore();
}
private void drawMainCircle(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, 0, mMainCircleCentr);
float rot = mRotation;
mPaint.setColor(Color.argb(100, 100, 100, 100));
canvas.drawCircle(0, mMainCircleCentr, mMainCirclRadius, mPaint);
mPaint.setColor(Color.BLACK);
for (int i = 0; i < SEG_COUNT; i++) {
canvas.rotate(SEG_IN_GRAD, 0, mMainCircleCentr);
rot += SEG_IN_GRAD;
if (rot % 360 > 90 && rot % 360 < 270)
continue;
canvas.drawLine(0, mMainCircleCentr, mMainCirclRadius, mMainCircleCentr, mPaint);
canvas.drawPath(mTextPaths.get(i), mPaint);
// canvas.drawText("my text" + String.valueOf(i), mMainCirclRadius * 2 / 3, mMainCircleCentr - 4, mPaint);
}
canvas.restore();
}
}
Your code is pretty nice and simple. You can optimize it by using less loops for instance, drawing things all together or combining variables, but this would quickly get messy.
I would recommend you to keep your drawing code more or less equal. You actually don't do the worst thing : instanciating objects, and it's clear and easy to maintain.
But you could maybe try to use a double buffer : drawing in a buffer in ram and flipping the buffer one shot on the screen. This generally performs quite well to get a constant animation pace. Use locking and unlocking of your canvas : Double buffering in Java on Android with canvas and surfaceview

Moving Circle on Live Wallpaper

I have to draw a circle in live wallpaper when it touches the boundary the direction of drawing gets reversed (something like in zigzag format).
The problem is i am able to draw circle in this format. But:
How to remove the previously drawn circle so that only single circle (dot) is visible at a time.
When i redraw the bitmap it starts flickering why this happens?
Code is as follows:
Thread to draw circle:
{animRunnable = new Runnable() {
public void run() {
if (!isRightEndReached && moveCircleX < 320) {
moveCircleX++;
moveCircleY++;
} else if (isRightEndReached) {
moveCircleX--;
moveCircleY++;
}
if (moveCircleX >= 320) {
isRightEndReached = true;
} else if (moveCircleX <= 0) {
isRightEndReached = false;
}
moveCircle(moveCircleX, moveCircleY);
if (moveCircleY == 480) {
// end of screen -re-init x and y point to move circle.
moveCircleX = intialStartX-10;
moveCircleY = intialStartY+1;
isRightEndReached = false;
// show wallpaper.
showWallpaper();
moveCircle(moveCircleX, moveCircleY);
}
}
};
/**
* Method to move circle
*
* #param x
* #param y
*/
private void moveCircle(int x, int y) {
Log.d("x==" + x, "y==" + y);
Paint paint = new Paint();
SurfaceHolder surfaceHolder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
canvas.save();
paint.setColor(Color.RED);
canvas.drawCircle(x, y, 5, paint);
canvas.restore();
}
} catch (Exception e) {
e.printStackTrace();
}
finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
animHandler.removeCallbacks(animRunnable);
if (isVisible()) {
animHandler.postDelayed(animRunnable, 1000L / 500L);
}
}
//Show wallpaper method.
/**
* Method to show wallpaper.
*/
void showWallpaper() {
SurfaceHolder surfaceHolder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
System.out
.println("Drawing bitmap in show Wallpaper method.");
canvas.save();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.aquarium, options);
canvas.drawColor(0xff000000);
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.restore();
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
SOLVED: finally i got the solution by not concentrating on removing circle but drawing bitmap again and again with new point. The method is as follows:
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.aquarium, options);
Paint paint = new Paint();
/**
* Method to move circle i.e to draw bitmap with new circle position.
*
* #param x
* #param y
*/
private void renderBackground(int x, int y) {
Log.d("x==" + x, "y==" + y);
surfaceHolder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
paint.setColor(Color.RED);
canvas.save();
// set Back ground
canvas.drawBitmap(bitmap, 0, 0, null);
// write draw circle.
paint.setAntiAlias(true);
canvas.drawCircle(x, y, 15, paint);
canvas.restore();
bitmap.recycle();
}
} catch (Exception e) {
e.printStackTrace();
}
finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
// showWallpaper();
}
}
animHandler.removeCallbacks(animRunnable);
if (isVisible()) {
animHandler.postDelayed(animRunnable, 1000L / 25L);
}
}
}

Categories

Resources