I created two class - one is a class which extends SurfaceView and has a method onDraw(), inside which I'm drawing an image on my canvas:
public class Board extends SurfaceView{
public BitmapFactory myBitmapFactory = new BitmapFactory();
public Bitmap myBitmap = new Bitmap;
public Board(Context context) {
super(context);
setFocusable(true);
}
//here i implement all the abstract classes, which are not important now
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
myBitmap = Bitmap.createScaledBitmap(myBitmapFactory.decodeResource(getResources(), R.drawable.image), 90, 90, false);
Paint paint = new Paint();
canvas.drawBitmap(myBitmap, 20, 20, paint);
}
public void setBitmap(Bitmap b){
this.myBitmap = b;
}
}
And I have also my activity class, where after a button click which runs the method changeImage. I want two change this image which i put on my canvas
public class MyActivity extends Activity {
public Context context = this;
public Board board;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_game);
}
public void changeImage(View view){
Bitmap tmpBitmap = new Bitmap();
BitmapFactory tmpBitmapFactory = new BitmapFactory();
tmpBitmap = Bitmap.createScaledBitmap(tmpBitmapFactory.decodeResource(getResources(), R.drawable.image2), 130, 130, false);
board.setBitmap(tmpBitmap);
board.invalidate();
}
}
But after calling method changeImage nothing is changing - the image is the same as at the beginning. I tried to use invalidate() also inside setBitmap() method, but it also doesn't work. Is this because every time when I call invalidate() I'm creating a new Bitmap or I'm doing something else wrong?
---EDIT---
So ofc, wrong was that:
myBitmap = Bitmap.createScaledBitmap(myBitmapFactory.decodeResource(getResources(), R.drawable.image), 90, 90, false);
was setting all the time the same image. I changed it, but still invalidate() thosent work. I mean the image is not changing. I was looking for the result in other topics, but I did't find any solution, now my classes looks like this:
public class Board extends SurfaceView{
public BitmapFactory myBitmapFactory = new BitmapFactory();
public Bitmap myBitmap = Bitmap.createScaledBitmap(myBitmapFactory.decodeResource(getResources(), R.drawable.image), size, size, false);
public int tmp = 0;
public Paint paint = new Paint();
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
canvas.drawBitmap(myBitmap, 50, 50, paint);
}
public void setBitmaps(Bitmap one){
this.paint = new Paint();
this.myBitmap = one;
this.tmp=4;
this.invalidate();
}
And the method changeImage() inside my activity:
public void changeImage(View view){
Bitmap tmpBitmap1 = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.blue), 230, 230, false);
myGameBoard.setBitmaps(tmpBitmap1);
}
I tried to use: invalidate(), postInvalidate(), but it doesn't work and i hae now idea what am I doing wrong
You update myBitmap in onDraw():
myBitmap = Bitmap.createScaledBitmap(myBitmapFactory.decodeResource(getResources(), R.drawable.image), 90, 90, false);
So your updated image is still not used as you directly override it with another image...
(old answer, which is now obsolete due to fixed question)
There seems something else off in your example code. In setBitmap() you store the new bitmap in pawnBitmap. In the onDraw() method, you do not use pawnBitmap. So it is expected that nothing changes, as you do not use the changed values...
(old answer, which is now obsolete due to change in question)
You are trying to call a static class method with:
Board.setBitmap(tmpBitmap);
Board.invalidate();
(I am wondering why you did not get compile errors... Or did you forgot to mention them?)
As Board is the name of your class. Instead you need to call the instance method.
Board board = ...
board.setBitmap(tmpBitmap);
board.invalidate();
This obviously requires the board instance, which you need to available in your activity class.
Note: if you are calling invalidate() from a non-UI thread, you need to use postInvalidate().
Related
seems I have a problem to understand how offset works. When I use the code below it create me 5 arrowheads, but why? I create one and the use twice offset. So i thought i get 3 arrow heads.
public class legende extends View {
private Paint paint;
private Path arrowPath;
public legende(Context context) {
super(context);
init();
}
public void init(){
paint = new Paint();
arrowPath = new Path();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 1500;
int height = 5000;
setMeasuredDimension(width, height);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
arrowPath.moveTo(420, 300);
arrowPath.lineTo(430, 300);
arrowPath.lineTo(420, 310);
arrowPath.lineTo(410, 300);
arrowPath.close();
canvas.drawPath(arrowPath, paint);
arrowPath.offset(0,200);
canvas.drawPath(arrowPath, paint);
arrowPath.offset(0,380);
canvas.drawPath(arrowPath, paint);
}
}
EDIT
Looks like, the problem is, that onDraw is called twice, but i don't know why.
This is how I use it in my fragment activity:
hScrollView.addView(new legende(getActivity()));
scrollView.addView(hScrollView);
relativeLayout.addView(scrollView);
Looks like you´ve got a non thread safe implementation of the onDraw method.
This guy worked it out by using a thread safe implementation: Custom View not drawing properly
Maybe you should copy the path to a local object inside the onDraw and then alter and paint.
I have a LinearLayout whose background color is set to black. In this LinearLayout, I have a View in which I draw using Canvas. Because the onDraw() method will be called multiple times, I want to clear what I drew previously when I call onDraw() method, thus I use Canvas.drawColor(Color.BLACK) to clear the canvas.
But what I get is a black screen without anything even when I draw something new. I can already draw something before I add Canvas.drawColor(Color.BLACK) within onDraw() method.
EDIT: codes of my onDraw() method
String value = "";
static Bitmap bitmap;
static Canvas canvas;
public void init(){// this is called by constructor method
this.setWillNotDraw(false);
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas = new Canvas();
canvas.setBitmap(bitmap);
}
public void onDraw(Canvas canvas){
canvas.drawBitmap(bitmap, 0, 0, null);
drawGrid();
}
public void drawGrid(){
Paint paint = new Paint();
paint.setColor(Color.GRAY);
paint.setStrokeWidth(1);
canvas.drawText(value, somex, somey, paint);
}
public void changeData(String value){
this.value = value;
this.postInvalidate();
}
Another question, where is the right place I call Canvas.drawColor(Color.BLACK)?
I used the below code and it works for me. Clearing the draw on canvas.
public void resetBitmapCanvasAndPath() {
// TODO Auto-generated method stub
mDrawingUtilities.mBitmap = Bitmap.createBitmap(Constants.SCREEN_WIDTH,Constants.SCREEN_HEIGHT ,
Bitmap.Config.ARGB_8888);
mDrawingUtilities.mCanvas = new Canvas(mDrawingUtilities.mBitmap);
mDrawingUtilities.mPath = new Path();
}
Here's a link similar to your question and have a look at the accepted answer.
How to clear finger paint?
use Color.TRANSPARENT instead of using Color.BLACK
after one year of using SO to solve my questions, I came to a problem for which I cannot find solution.
I am using multiple threads to calculate bitmap. When new row of bitmap is calculated, I update custom view and call invalidate(). But when I rotate my phone, bitmap stops updating. After another screen rotation View is updated only once, and part of bitmap that was calculated in the meantime is also visible.
part of onCreate method:
view = (MyView) findViewById(R.id.view1);
if (savedInstanceState != null)
{
bitmap = savedInstanceState.getParcelable("bitmap");
view.bitmap = bitmap;
}
else
{
bitmap = Bitmap.createBitmap(400, 500, Bitmap.Config.ARGB_8888);
bitmap.eraseColor(Color.GRAY);
view.bitmap = bitmap;
}
I use handler to update my view:
public void handleMessage(final Message msg) {
Log.i("Recieved", "redraw signal");
view.bitmap = bitmap;
view.invalidate();
}
In my view, i use:
Bitmap bitmap;
Rect rect;
//...constructors
#Override
protected void onSizeChanged (int w, int h, int oldw, int oldh)
{
rect = new Rect(0, 0, getWidth(), getHeight());
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if (bitmap!=null)
{
canvas.drawBitmap(bitmap, null, rect, null);
}
Log.i("onDraw", "draw");
}
Solution
So far, I have found one solution. If I add invalidate() to the end of onDraw, bitmap is corectly updated(row by row) after screen orientation change. I don't think this is very "clean" solution, so I would like to understand why is invalidate() called from handler ignored after config change?
Thanks!
I found a solution.
Problem was in Handler, which was NOT static. I ignored "Leaks might occur" warning because I wasn't posting long delayed messages. Big mistake, because after configuration change Handler was somehow unable to reach View.
Here is solution:
static class MyHandler extends Handler {
private final WeakReference<MainActivity> mTarget;
public MyHandler(MainActivity target) {
mTarget = new WeakReference<MainActivity>(target);
}
#Override
public void handleMessage(Message msg) {
MainActivity target = mTarget.get();
...other stuff...
target.view.invalidate(); // now this works after configuration change
}
}
Bottom line: use static handler, as explained here
I have this class that extends View and draws a line:
public class MyDraw extends View
{
Paint paint = new Paint();
public MyDraw(Context context)
{
super(context);
paint.setColor(Color.BLUE);
}
#Override
public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawLine(1, 1, 100, 100, paint);
}
}
I would like to use the existing view from the Context to draw on top of it. Is it possible?
If you are just trying to get the view as a bitmap, you can get it from the drawing cache. This should work.
view.buildDrawingCache;
Bitmap bm = view.getDrawingCache
You cannot use the exisitng view instance and add it again as it already has a parent assigned to it and it wil cause an exception.
My project is about image processing in android.I have a bitmap that I have loaded from a resource file (an PNG image). I want to draw it. But I couldn't.
Here my code snippet:
mB = BitmapFactory.decodeResource(getResources(), R.drawable.picture);
Canvas c = new Canvas(mB);
Paint p = new Paint();
c.drawBitmap(mB,0,0,p);
it didn't work. Is the code true? .Is there any thing more that I must do?
You should use an ImageView instead and load it by
imageView.setImageResource(R.drawable.picture);
If you want to manually draw it with a Canvas, you have to use a canvas that is passed into a draw() method and implement a Custom View.
Update to add example CustomView:
public class CustomView extends View {
private Paint mPaint;
private Drawable mDrawable;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mDrawable = context.getResources().getDrawable(R.drawable.some_drawable);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawable.draw(canvas);
}
}
There are a couple of things you are missing.
First, I think you're misunderstanding the Canvas(Bitmap b) constructor. The Bitmap passed in there is one that the Canvas will draw into. This could be just a new Bitmap that you constructed.
Second, it is recommended that you use the Canvas that is passed to you in your View's onDraw method. Presumably that View is one from your Activity, either fetched from your XML layout via findViewById or constructed and passed to setContentView in the Activity's onCreate() method.
So, you are going to have to subclass View and override the onDraw method to get your drawing done. Something like:
public class MyView extends View {
#Override
public void onDraw (Canvas c) {
Bitmap mB = BitmapFactory.decodeResource(this.getContext().getResources(), R.drawable.picture);
c.drawBitmap(mB, 0, 0, null);
}
}
Then, in your Activity, you'll need to create an instance of your new View and pass it to the Activity via setContentView:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mv = new MyView(this);
setContentView(mv);
}
You can instead call the setContentView(View v, ViewGroup.LayoutParameters lp) overload if you want to set up the LayoutParameters.
I haven't tested any of this, but it should at least get you on the right path.