Moving Circle on Live Wallpaper - android

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);
}
}
}

Related

How to return a bitmap that has been drawn on top of?

I have class allows you draw on an image from the filesystem. It has methods that I've shared below. The idea is that, as the image is touched, circles will be drawn on top of that image at each of the points that have been touched.
#Override
protected void onDraw(Canvas canvas) {
try{
if (mTextPaint == null)
{
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(Color.CYAN);
}
if (cachedBitmap == null)
{
Bitmap immutableBitmap = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), Uri.fromFile(CameraActivity.getFile()));
cachedBitmap = immutableBitmap.copy(Bitmap.Config.ARGB_8888, true);
}
if (cachedCanvas == null)
{
cachedCanvas = canvas;
cachedCanvas.setBitmap(cachedBitmap);
}
if (! mPoints.isEmpty())
{
for (Point point : mPoints)
{
cachedCanvas.drawCircle(point.x, point.y, 25, mTextPaint);
}
Log.i(TAG, "Drawing points...");
}
}
catch (Exception e)
{
Log.e(TAG, "Error on draw: " + e.toString());
}
}
/**
* populates a list of points that have been touched
*
* #param event
* #return
*/
#Override
public boolean onTouchEvent(MotionEvent event)
{
Point point = new Point((int)event.getX(), (int)event.getY());
mPoints.add(point);
invalidate();
return true;
}
After this is done, I'd like to include a static method that returns the now drawn on bitmap, something like this:
public static Bitmap getCachedBitmap()
{
return cachedBitmap;
}
The problem is, the cachedBitmap variable is not being updated as it is being drawn on, so when I inspect the return value of cachedBitmap, I just get the Bitmap from the file URI specified in the initial immutableBitmap variable.
Any idea how I can return the altered bitmap?
You can take an image of outer layout which contains both the bitmap and the canvas on which circles are drawn:
View v = yourlayout.getRootView();
v.setDrawingCacheEnabled(true);
Bitmap bitmap = v.getDrawingCache();
BitmapDrawable drawable=new BitmapDrawable(bitmap);
By this way, you will get your initial image + edits.

Failed to allocate a 1814412 byte allocation with 59912 free bytes in Xamarin Android

I tried to extend ImageView class and in OnDraw method, I am creating a circle and trying to display image only in that circle in order to get a rounded ImageView.
I am using this RoundedImageView in ListView.
This piece of code is running fine initially, but after having 4-5 scrolls of ListView, its throwing an exception with message "Failed to allocate a 1814412 byte allocation with 59912 free bytes".
the same piece of code is running perfect in eclipse even with more than 300 list items but in xamarin its throwing this exception
Can anyone help me out?
Below is the code:
public class RoundedImageView : ImageView {
public RoundedImageView(Context context) : base(context) {
}
public RoundedImageView(Context context, IAttributeSet attrs) : base(context,attrs) {
}
public RoundedImageView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) {
}
protected override void OnDraw(Canvas canvas) {
Drawable drawable = Drawable;
if (drawable == null){
return;
}
Bitmap bitmap = null;
Bitmap bit = ((BitmapDrawable)drawable).Bitmap;
try {
bitmap = bit.Copy(Bitmap.Config.Argb8888, true);
} catch (Exception exp) {
string temp = exp.Message;
base.OnDraw(canvas);
return;
}
int _width = Width;
int _height = Height;
Bitmap roundBitmap = getCroppedBitmap(bitmap, _width <= _height ? _width : _height);
canvas.DrawBitmap(roundBitmap, 0,0, null);
}
public static Bitmap getCroppedBitmap(Bitmap bitmap, int radius) {
Bitmap _bitmap;
if(bitmap.Width != radius || bitmap.Height != radius) {
try {
_bitmap = Bitmap.CreateScaledBitmap(bitmap, radius, radius, false);
} catch (Exception exp) {
string temp = exp.Message;
_bitmap = bitmap;
return _bitmap;
}
} else {
_bitmap = bitmap;
}
Bitmap output = Bitmap.CreateBitmap(_bitmap.Width,_bitmap.Height, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(output);
Paint paint = new Paint();
Rect rect = new Rect(0, 0, _bitmap.Width, _bitmap.Height);
paint.AntiAlias = true;
paint.FilterBitmap = true;
paint.Dither = true;
canvas.DrawARGB(0, 0, 0, 0);
paint.Color = Color.ParseColor("#7f00ff");
canvas.DrawCircle(_bitmap.Width / 2 ,
_bitmap.Height / 2 ,
_bitmap.Width / 2 - 0.2f,
paint);
paint.SetXfermode(new PorterDuffXfermode(Android.Graphics.PorterDuff.Mode.SrcIn));
canvas.DrawBitmap(_bitmap, rect, rect, paint);
return output;
}
}
You really need to recycle all the Bitmap instances you are creating.
This could help:
protected override void OnDraw(Canvas canvas)
{
Drawable drawable = Drawable;
if (drawable == null)
{
return;
}
Bitmap bitmap = null;
Bitmap bit = ((BitmapDrawable)drawable).Bitmap;
try
{
bitmap = bit.Copy(Bitmap.Config.Argb8888, true);
bit.Recycle();
}
catch (Exception exp)
{
string temp = exp.Message;
base.OnDraw(canvas);
return;
}
int _width = Width;
int _height = Height;
using (Bitmap roundBitmap = getCroppedBitmap(bitmap, _width <= _height ? _width : _height))
{
canvas.DrawBitmap(roundBitmap, 0,0, null);
roundBitmap.Recycle();
bitmap.Recycle();
}
}
public static Bitmap getCroppedBitmap(Bitmap bitmap, int radius)
{
Bitmap _bitmap;
if(bitmap.Width != radius || bitmap.Height != radius)
{
try
{
_bitmap = Bitmap.CreateScaledBitmap(bitmap, radius, radius, false);
}
catch (Exception exp)
{
string temp = exp.Message;
_bitmap = bitmap;
return _bitmap;
}
}
else
{
_bitmap = bitmap;
}
Bitmap output = Bitmap.CreateBitmap(_bitmap.Width,_bitmap.Height, Bitmap.Config.Argb8888);
using(Canvas canvas = new Canvas(output))
using(Paint paint = new Paint())
{
Rect rect = new Rect(0, 0, _bitmap.Width, _bitmap.Height);
paint.AntiAlias = true;
paint.FilterBitmap = true;
paint.Dither = true;
canvas.DrawARGB(0, 0, 0, 0);
paint.Color = Color.ParseColor("#7f00ff");
canvas.DrawCircle(_bitmap.Width / 2 ,
_bitmap.Height / 2 ,
_bitmap.Width / 2 - 0.2f,
paint);
paint.SetXfermode(new PorterDuffXfermode(Android.Graphics.PorterDuff.Mode.SrcIn));
canvas.DrawBitmap(_bitmap, rect, rect, paint);
_bitmap.Recycle();
}
return output;
}
Also it is not very efficient to create so many instances of objects in the OnDraw method, so consider if you can move something out of it.
Also consider if you can just use a Drawable like in the Xamarin Store sample: https://github.com/xamarin/xamarin-store-app/blob/master/XamarinStore.Droid/Views/CircleDrawable.cs

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 draw outside canvas and make bitmap of it?

I am drawing some rows of text. After a couple of rows, it goes offscreen. What i want to do is capture all the rows(also the rows outside the canvas) in a bitmap.
I have the code below which only works for within the canvas(screen).
private class DeciderView extends View {
private Paint paint;
String text = "";
String[] options = { "een", "twee", "drie", "vier", "vijf", "zes", "zeven", "acht",
"negen", "tien", "elf", "twaalf", "dertien", "vertien", "vijftien" };
public DeciderView(Context context) {
super(context);
// Keep screen on
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Remove title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Remove notification bar
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
this.setBackgroundColor(Color.WHITE);
paint = new Paint();
paint.setTextSize(75);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
setDrawingCacheEnabled(true);
setDrawingCacheQuality(DRAWING_CACHE_QUALITY_HIGH);
}
private void drawInput(Canvas canvas) {
Paint p = new Paint();
p.setTextAlign(Align.CENTER);
p.setTextSize(canvas.getWidth() / 10f);
p.setColor(Color.BLACK);
float xText = canvas.getWidth() / 2f;
float yText = (canvas.getHeight() / 4f);
for (int i = 0; i < options.length; i++) {
text += options[i] + "\n";
}
for (String line : text.split("\n")) {
canvas.drawText(line, xText, yText, p);
yText -= (p.ascent() + p.descent()) * 2.5f;
}
}
#Override
public void onDraw(Canvas canvas) {
drawInput(canvas);
this.setDrawingCacheEnabled(true);
Bitmap b = Bitmap.createBitmap(this.getDrawingCache());
b = Bitmap.createScaledBitmap(b, canvas.getWidth(), canvas.getHeight(), false);
try {
b.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(
new File(Environment.getExternalStorageDirectory().getPath()
+ "/Pictures/aaaaa.jpg")));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (b != null) {
b.recycle();
b = null;
}
}
}
So basically i want to make a bitmap from all the rows, even if the rows are drawn outside the canvas.
Why not just draw directly to the bitmap? You can create a new Canvas that has a bitmap backing it. You use the normal canvas draw commands, and the output will be written to the bitmap rather than the screen. If you also want to draw it to the screen, just draw the new bitmap to the screen at the end. To do this all you need is
Canvas myCanvas = new Canvas(myBitmap);

Android canvas.translate()

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

Categories

Resources