Android canvas not drawing - java

I have found very few tutorials on how to use the android canvas. However, after looking at the official documentation I was able to produce this code. I simply would like to draw a green rectangle.
public class LevelActivity extends AppCompatActivity {
Object shape;
Target target;
Rect r;
Paint paint;
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_level);
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.FILL);
r = new Rect();
r.set(0,0,c.getWidth(),c.getHeight()/2);
c.drawRect(r,paint);
shape = new Shape(30,10); //Object setup
//shape.pick(); //Pick random point for target
}
}
I do not want to use a customView to draw, I just want to use pure android canvas. When I run the code I get no errors or any warnings.
Any help is greatly appreciated.
-Kelton

Well, you have a Canvas c with an underlying Bitmap b. Now anything that you draw on the Canvas c will actually be drawn on the Bitmap b. And you have done the drawing part in the above code. The Bitmap b has your drawn rectangle. But now to view that you need to load this Bitmap b in some view, like an ImageView. You can have an ImageView in your activity_level layout and then use ImageView.setImageBitmap(b) to load the Bitmap onto the view.

you need to add the following 2 lines at the end:
ImageView iv= (ImageView) findViewById(R.id.imageview_where_i_will_put_my_awesome_green_rectangle);
iv.setImageBitmap(b);

You have to create a ImageView and set the Bitmap as the one you created with canvas.
ImageView imageView = new ImageView(this);
imageView.setBitmap(bitmap);
addContentView(imageView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));

Related

canvas.drawBitmap() doesn't scale images

I'm a newbe when it comes to Android coding. As a first project I decided to port ZX Spectrum game. I have prepared bitmap frames of the car and buttons that are about to be scaled depending on screen size. I've put the images both in "drawable" and "drawable-hdpi" folders and it seems to work on my LG Leon phone, but not working on other phones. It looks like the image isn't scaled.
https://mechanism.fr/misc/not_scaled.png
here's my code:
declaration of the variables:
Rect source1;
Rect dest1;
Rect arrow_box;
Rect left_box;
Rect right_box;
Rect up_box1;
Rect down_box1;
Rect up_box2;
Rect down_box2;
example of calculating one pair of the Rect dimensions:
private void calculateDimensions(){
(...)
source1=new Rect(0,0,128,100);
arrow_box=new Rect(0,0,300,300);
left_box=new Rect((int)(0+boxxy/2),(int)(wysokosc*0.7),(int)(0+boxxy/2+boxxy),(int)(wysokosc*0.7+boxxy));
and the code responsible for drawing:
Paint drawPaint2 = new Paint();
canvas.drawBitmap(arrow_left_off,arrow_box,left_box,drawPaint2);
canvas.drawBitmap(arrow_right_off,arrow_box,right_box,drawPaint2);
canvas.drawBitmap(arrow_up_off,arrow_box,up_box1,drawPaint2);
canvas.drawBitmap(arrow_down_off,arrow_box,down_box1,drawPaint2);
canvas.drawBitmap(arrow_up_off,arrow_box,up_box2,drawPaint2);
canvas.drawBitmap(arrow_down_off,arrow_box,down_box2,drawPaint2);
the dimensions are calculated after creating the canvas view:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
kanwas=new CanvasV(this);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(kanwas);
getDimensions();
calculateDimensions();
updateScreen();
}
I don't quite understand what seems to be the problem.
I tried to copy the images both to "drawable" and "drawable-hdpi" folders
Also, scale the bitmaps. Scaling the canvas doesn't do anything.
Example of scaling bitmap
bitmap=Bitmap.createScaledBitmap(src, dstWidth, dstHeight, filter)

Animate rotation of an image in Android

I have a gear image which I want to continuously rotate about a fixed point.
Earlier I was accomplishing this by including the image in my Android class as an ImageView and applying a RotateAnimation to it.
#InjectView(R.id.gear00) ImageView gear00;
RotateAnimation ra07 = new RotateAnimation(0, 359, 129, 186);
ra07.setDuration(10000);
ra07.setRepeatCount(RotateAnimation.INFINITE);
ra07.setInterpolator(new LinearInterpolator());
gear00.setAnimation(ra07);
Basically, I was injecting the ImageView into the class and applying a rotation animation.
However, I dont have the luxury of using an ImageView anymore. I have to use a Bitmap and rotate it on the canvas.
How can I go about accomplishing what I was doing earlier in the onDraw() method with a bitmap rotating about a fixed point continiously on the canvas?
Edit1:
I tried one of the suggestions mentioned below my code looks a little like the following
in onCreate():
Matrix matrix = new Matrix();
matrix.setRotate(10, 100, 200);
Then in onDraw() (where gear00Scaled is a bitmap to be rotated on the canvas):
canvas.drawBitmap(gear00Scaled, matrix, new Paint());
Another method I tried involved saving the canvas, rotating it, then restoring it:
canvas.save();
canvas.rotate(10);
canvas.drawBitmap(gear00Scaled, 100, 200, null);
canvas.restore();
Neither seem to be working though!
Make an XML class (suppose: rotate.xml) and place it in res/anim folder and then write the following code in it:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="360" />
Then in your java class, do the following in OnCreate:
final Animation a = AnimationUtils.loadAnimation(CustomActivity.this,
R.anim.rotate);
a.setDuration(3000);
gear00.startAnimation(a);
OR
To do it using bitmap, I hope the following sequence of code helps you:
Bitmap targetBitmap = Bitmap.createBitmap(targetWidth, targetHeight, config);
Canvas canvas = new Canvas(targetBitmap);
Matrix matrix = new Matrix();
matrix.setRotate(mRotation,source.getWidth()/2,source.getHeight()/2);
canvas.drawBitmap(source, matrix, new Paint());
If you check the following method from:
~frameworks\base\graphics\java\android\graphics\Bitmap.java
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,
Matrix m, boolean filter)
this would explain what it does with rotation and translate.
I want to rotate a custom image as progress dialog in my application. You can use the code below to rotate an image:
RotateAnimation anim = new RotateAnimation(0.0f, 360.0f ,
Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f);
anim.setInterpolator(new LinearInterpolator());
anim.setRepeatCount(Animation.INFINITE);
anim.setDuration(1000);
imageView.setAnimation(anim);
imageView.startAnimation(anim);
In your onCreate() do
Matrix matrix = new Matrix();
And in onDraw
float angle = (float) (System.currentTimeMillis() % ROTATE_TIME_MILLIS)
/ ROTATE_TIME_MILLIS * 360;
matrix.reset();
matrix.postTranslate(-source.getWidth() / 2, -source.getHeight() / 2);
matrix.postRotate(angle);
matrix.postTranslate(centerX, centerY)
canvas.drawBitmap(source, matrix, null);
invalidate(); // Cause a re-draw
ROTATE_TIME_MILLIS is the full circle time, e.g. 2000 is 2 seconds.
Two things before the code:
You can't use imageview at all at all? Cause you could link it to a canvas and use a bitmap, but it is still an imageview.
I believe the main issue you are having is the other answer is not animating it and you are missing the call to invalidate() the view and redraw it as rotated.
So there are two approaches:
1st is with the imageview, personally I think it is easier and nicer. Below is a method in my activity class and mLittleChef is an ImageView.
public void doCanvas(){
//Create our resources
Bitmap bitmap = Bitmap.createBitmap(mLittleChef.getWidth(), mLittleChef.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
final Bitmap chefBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.dish_special);
//Link the canvas to our ImageView
mLittleChef.setImageBitmap(bitmap);
ValueAnimator animation= ValueAnimator.ofFloat(0, 359, 129, 186);
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
//Clear the canvas
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.save();
canvas.rotate(value, canvas.getWidth()/2, canvas.getHeight()/2);
canvas.drawBitmap(chefBitmap, 0, 0, null);
canvas.restore();
mLittleChef.invalidate();
}
});
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(1000);
animation.start();
}
The other way to do it is with a custom canvas class. Instead of an ImageView, I create my own custom view class for ExampleDrawView mLittleChefDraw; in my layout. You will probably have to monkey with this one a bit to get exactly what you are looking for in terms of rotation, I made it just do a 360 degree turn using the middle of the canvas as the pivot point.
public class ExampleDrawView extends View {
Bitmap bitmap;
Float mRotate= 0f;
Handler h;
//State variables
final int STATE_PAUSE = 2;
final int STATE_ROTATE = 3;
int STATE_CURRENT;
public ExampleDrawView(Context context, AttributeSet attrs) {
super(context, attrs);
h = new Handler();
bitmap= BitmapFactory.decodeResource(getResources(), R.drawable.dish_special);
STATE_CURRENT= STATE_PAUSE;
}
Runnable move = new Runnable() {
#Override
public void run() {
switch (STATE_CURRENT){
case STATE_ROTATE:
if (mRotate<360){
mRotate++;
invalidate();
}else{
STATE_CURRENT= STATE_PAUSE;
}
h.postDelayed(move, 20);
break;
}
}
};
public void startDrawing(){
if(STATE_CURRENT == STATE_PAUSE){
STATE_CURRENT= STATE_ROTATE;
mRotate=(float) 0;
h.postDelayed(move, 20);
}
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//change your rotate point here, i just made it the middle of the canvas
canvas.rotate(mRotate,getWidth()/2,getHeight()/2);
canvas.drawBitmap(bitmap, 0, 0, null);
}
}
And then back in the activity call this to start it:
public void doCanvasCustomView(){
mLittleChefDraw.startDrawing();
}

Drawing WebView into set position on Canvas

I have created a custom WebView, which will be used to play a gif file. Now I need to draw this to my canvas in a specific position and using a set size.
However, I am not entirly sure how I can convert this webView containing Gif into a Bitmap so that I can insert it into the correct location on my Canvas.
My WebView Class:
public class finishWebView extends WebView
{
public finishWebView(Context context, String path)
{
super(context);
loadUrl(path);
}
}
Getting the raw resource for main Activity:
finishWebView view = new finishWebView(context, "file:///android_asset/test.gif");
The orginal code to convert my image to the correct size and insert into canvas:
finish = BitmapFactory.decodeResource(getResources(), R.drawable.finish);
finish = Bitmap.createScaledBitmap(finish, (PhoneWidth / 5), (PhoneWidth / 5), true);
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(finish, (j * totalCellWidth), (i * totalCellHeight), null);
}
So essentially, I need to work out a way to draw the webView with gif image displaying onto the specified locations within my canvas, similar to what I am currently doing with 'canvas.drawBitmap(...)'.
----------------------------------- EDIT ----------------------------------------
Using the code snippet mentioned provided by mikejonesguy, I have got to here, however this now just displays a blank screen.
public static Bitmap createBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565);
Canvas c = new Canvas(b);
v.draw(c);
return b;
}
// Within onDraw
GIFView gifView = new GIFView(context);
canvas.drawBitmap(createBitmapFromView(gifView), (j * totalCellWidth), (i * totalCellHeight), null);
I have tested my GIFView on normal xml layout pages and it works correctly, just cannot get it inserted into my canvas.
There are several ways to convert a view into a bitmap. This method is one way:
public static Bitmap createBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(b);
v.draw(c);
return b;
}
You'll have to wait until your webView has finished loading the GIF, of course, before you call this to get the bitmap.
If you're just trying to play an animated gif, this other SO post might help: Showing gif in android

How to overlay list item text color in specific region?

Let suppose we have the custom ListView which extends onDraw() method by drawing some rectangle in it.
class Listpicker extends ListView {
//..Some other methods here..//
#Override
protected void onDraw(android.graphics.Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(glassPickerBitmap, null, glassRect, semiTransparrentPaint);
}
}
The bitmap drawn represent some sort of glass which is rectangle with width equal to the width of listview and some height. What I want is when list item scrolls into this rectangle the color used for drawing text (not all item with background and etc.) would be changed into some another color. So, for example, when list item fits so that only half height of text fits into glassPickerBitmap, the outside part of list item should remain in its ordinal colors, and the part that is inside glassPickerBitmap should change its text color. List items are simple TextViews without any background.
How can be this achieved? Maybe any ColorFilters assigned to the Paint? But where? onDraw() method of Listview is not even called when ListView is scrolled... Can be this done inside customized views, for example, that will be ListView's items? Or may be some Color/Shader/do not know what else overlay can be used here, but where?
EDIT (added image examples):
Here is example of some crop from real-life app. There are 2 ListViews: one on the left side, other on the right. Glass is the grayed rectangle. Now, left list has "United States Dollar" currency selected. And I am scrolling right ListView in that way, that selection is somewhere between USD and Afghan Afghani. Now, what I want, is that USD currency in the left ListView would be drawn in red (exact color doesn't matter now, I will change it later to something meaningful) AND, in the same time, bottom part of "United States Dollar" and top part of "Afghan Afghani" in the right ListView would be drawn also in the same red color.
This color changing should be done in dynamic way - color should be changed only for the part of text that is under glass rectangle during scrolling of the list.
*OK, EUR and USD here are special currencies drawn with not standard cyan color. The question is about at least text with white color. But if it will be possible to change also cyan color it would be great.
As you can tell in their implementation they use and update a fixed position in their ListAdapter so then they update the View that matches accordingly, which is to me the easiest and simpler way to do it, but you don't get the half-half colored text, which I think you still want :)
So here is probably one of the worst answers I've given, sorry about it, I'll probably revisit this or hope someone can point you to a better solution
You can see a blurry demo at:
http://telly.com/Y98DS5
The dirty way:
Create a Paint with a proper ColorMatrixColorFilter, for the purposes of answering this I'm just desaturating, you'll have to figure out your 5x4 matrix for ColorMatrix to get the cyan your looking for. Also you will need some Rects to define source and dest when drawing below.
Create an off screen Bitmap and Canvas which you'll use to draw your list, that's done in onSizeChanged so we have proper view layout values, here you can start noticing the dirtiness as you will have to allocate a Bitmap as big as your ListView so you may get OutOfMemoryException.
Override draw to use your off screen Canvas and let ListView draw to it, then you can draw the Bitmap to the given Canvas that will draw your list as usual, then draw just the part of that Bitmap which will be drawn differently using the Paint we defined before, you will get overdraw for those pixels.
Finally draw your pre-fabricated glass Bitmap, I would recommend a n-patch (9-patch) so it scales and looks sharp across screens and densities, then you get more overdraw for those pixels.
The whole cake looks like:
public class OverlayView extends ListView {
private RectF mRect;
private Rect mSrcRect;
private Paint mPaint;
private Canvas mCanvas;
private Bitmap mBitmap;
private float mY;
private float mItemHeight;
public OverlayView(Context context) {
super(context);
initOverlay();
}
public OverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
initOverlay();
}
public OverlayView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initOverlay();
}
private void initOverlay() {
// DO NOT DO THIS use attrs
final Resources res = getResources();
mY = res.getDimension(R.dimen.overlay_y);
mItemHeight = res.getDimension(R.dimen.item_height);
//
mRect = new RectF();
mSrcRect = new Rect();
mPaint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
mPaint.setColorFilter( new ColorMatrixColorFilter(colorMatrix) );
}
#Override
public void draw(Canvas canvas) {
super.draw(mCanvas);
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawBitmap(mBitmap, mSrcRect, mRect, mPaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRect.set(0, mY, w, mY + mItemHeight);
mRect.roundOut(mSrcRect);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
}
So that said, I hope you end up with a cleaner solution, even if you don't get that half-half sexiness :)
First, keep a spare bitmap and canvas:
class Listpicker extends ListView {
Bitmap bitmapForOnDraw = null;
Canvas canvasForOnDraw = null;
Make sure it's the right size:
#override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
if (bitmapForOnDraw != null) bitmapForOnDraw.recycle();
bitmapForOnDraw = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvasForOnDraw = new Canvas(bitmapForOnDraw);
}
And when we come to drawing the list:
#override
protected void onDraw(android.graphics.Canvas realCanvas) {
// Put the list in place
super.onDraw(realCanvas);
// Now get the same again
canvasForOnDraw.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
super.onDraw(canvasForOnDraw);
// But now change the colour of the white text
canvasForOnDraw.drawColor(0xffff00ff, PorterDuff.Mode.MULTIPLY);
// Copy the different colour text into the right place
canvas.drawBitmap(bitmapForOnDraw, glassRect, glassRect overwritePaint);
// finally, put the box on.
canvas.drawBitmap(glassPickerBitmap, null, glassRect, semiTransparrentPaint);
}

Android Scratch card app

I am doing a small project for college and wondering if someone can tell me how I would go about doing a scratch card app. This app should have one image overlaying another. The one on top should allow the user to remove the image depending on where they rub on the image and thus part of the image that was removed revealing the image underneath. Pretty much like a scratch card. Any help would be great!
This is the code im using at the moment .
public class workinggraphics extends Activity
{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
setContentView(new Panel(this));
LinearLayout l1 = (LinearLayout) findViewById(R.id.LAYOUTTEST1);
Panel p1 = new Panel(null);
}
class Panel extends View{
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
// private Paint nPaint;
Bitmap bitmap;
Canvas pcanvas ;
int x = 0;
int y =0;
int r =0;
public Panel(Context context) {
super(context);
Log.v("Panel", "STROKE");
setFocusable(true);
setBackgroundColor(Color.TRANSPARENT);
/*
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
//WallpaperDrawable wallpaperDrawable=wallpaperManager.getDrawable();
try {
wallpaperManager.setBitmap(bmp);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
//setBackgroundDrawable(bmp);
// setting paint
/* nPaint = new Paint();
nPaint.setStyle(Paint.Style.FILL);*/
mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//.Mode.DST_IN));
mPaint.setAntiAlias(false);
// getting image from resources
Resources r = this.getContext().getResources();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.mainscreen_background);
Bitmap bm1 = BitmapFactory.decodeResource(getResources(),R.drawable.foreground_image);
// converting image bitmap into mutable bitmap
bitmap = bm.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_8888);
//bitmap = bm1.createBitmap(bm1.getWidth(),bm1.getHeight(),Config.ARGB_8888);
pcanvas = new Canvas();
pcanvas.setBitmap(bitmap); // drawXY will result on that Bitmap
pcanvas.drawBitmap(bm, 0, 0, null);
pcanvas.drawBitmap(bm1,0,0,null);
}
#Override
protected void onDraw(Canvas canvas) {
Rect cBK = new Rect();
//canvas.set
cBK.set(0,0,canvas.getWidth(),canvas.getHeight());
//pcanvas.drawRect(cBK, nPaint);
// draw a circle that is erasing bitmap
pcanvas.drawCircle(x, y, r, mPaint);
//pcanvas.drawLine(x, y, 0, 0, mPaint);
canvas.drawBitmap(bitmap, 0, 0,null);
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// set paramete to draw circle on touch event
x = (int) event.getX();
y = (int) event.getY();
r =20;
// Atlast invalidate canvas
invalidate();
return true;
}
}
}
Now as you can see above there is a lot of comments for things we have tried. Ideally I would like to be able to create an instance of the Panel class, set the workinggraphics class contextview to a XML LinearLayout (OR SurfaceView w/e would be used I dont really know), and then just set a background image on the defined LinearLayout in XML to be revealed when the canvas erases the bitmap image we have set.
Anyway any suggestions would be appricated thanks alot in advanced!
I created a library call WScrarchView where you can implement scratch view just few lines in layout xml. Hope this can help those who still looking for the solution https://github.com/winsontan520/Android-WScratchView

Categories