Okay so Im trying to set the background of a SurfaceView to a JPG file. But it doesn't seem to want to draw the image, and all I get is a black screen.
Here:s my code:
public class FloorplanActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MapView mapView = new MapView(getApplicationContext());
setContentView(mapView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.floorplan, menu);
return true;
}
class MapView extends SurfaceView{
Rect testRectangle1 = new Rect(0, 0, 50, 50);
Bitmap scaled;
int x;
int y;
public MapView(Context context) {
super(context);
}
public void surfaceCreated(SurfaceHolder arg0){
Bitmap background = BitmapFactory.decodeResource(getResources(), R.drawable.floorplan);
float scale = (float)background.getHeight()/(float)getHeight();
int newWidth = Math.round(background.getWidth()/scale);
int newHeight = Math.round(background.getHeight()/scale);
scaled = Bitmap.createScaledBitmap(background, newWidth, newHeight, true);
}
public void onDraw(Canvas canvas) {
canvas.drawBitmap(scaled, 0, 0, null); // draw the background
}
Not sure why it won't draw the "floorplan" image I have saved in the drawable-mdpi folder.
Anyone got any suggestions?
Thanks.
EDIT: After doing some debugging with breakpoints, it seems like the "scaled" variable becomes "Infinity" for some reason and as such the newWidth and newHeight variables become less than 0 and the application crashes.
This is only if I move the entire surfaceCreated into the contstructor, if I leave the code as is here then it doesn't do anything beyind displaying a black screen.
No idea what's causing it to do that though...
First, you should implement SurfaceHolder.Callback interface with your MapView class and set it as a callback for its SurfaceHolder to make the app call your onSurfaceCreated() metod.
Second, if you want your onDraw() method to be called, then call setWillNotDraw(false) in your MapView's constructor.
I've done that as following:
public class MapView extends SurfaceView implements SurfaceHolder.Callback {
private Bitmap scaled;
public MapView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
getHolder().addCallback(this);
}
public void onDraw(Canvas canvas) {
canvas.drawBitmap(scaled, 0, 0, null); // draw the background
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
Bitmap background = BitmapFactory.decodeResource(getResources(), R.drawable.dr);
float scale = (float) background.getHeight() / (float) getHeight();
int newWidth = Math.round(background.getWidth() / scale);
int newHeight = Math.round(background.getHeight() / scale);
scaled = Bitmap.createScaledBitmap(background, newWidth, newHeight, true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Callback method contents
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Callback method contents
}
}
And it works well.
NOTE: Moved MapView class to a separate *.java file.
Update Watch Duplicate Copy Move
Related
I'm trying to draw a bitmap into surfaceView I can successfully draw but I need to move that bitmap around the screen based on some other user movements but when I set
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(bitmap, left, top, null);
It draws same bitmap multiple times in screen.
But when I do this way
canvas.drawColor(Color.GREEN);
canvas.drawBitmap(bitmap, left, top, null);
It works correctly draws just one bitmap and moves it , but I need transparent background not colored.
CODE
public class DotsSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private boolean created;
Bitmap bitmap;
#Override
public void surfaceCreated(SurfaceHolder holder) {
// draw();
created = true;
}
#Override
// This is always called at least once, after surfaceCreated
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// draw();
created = true;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
public DotsSurfaceView(Context context) {
super(context);
holder = getHolder();
holder.addCallback(this);
holder.setFormat(PixelFormat.TRANSPARENT);
Drawable drawable = ARTrackingActivity.contexti.getDrawable(R.drawable.ic_tune_black_24px);
bitmap =Utils.drawableToBitmap(drawable);
}
public void draw(float left, float top) {
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
synchronized (holder) {
draw2(canvas, left, top);
}
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
public void draw2(Canvas canvas, float left, float top) {
if (created) {
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(bitmap, left, top, null);
}
}
}
I wrote code with guide to move my background in game and it's working, but the size of my background is original and I want to make the size of my moving background in full screen, please, help me))
Code:
public class Background extends SurfaceView implements
SurfaceHolder.Callback {
private Bitmap backGround;
public Background(Context context) {
super(context);
backGround = BitmapFactory.decodeResource(context.getResources(),
R.drawable.cold_planet);
setWillNotDraw(false);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
doDrawRunning(canvas);
invalidate();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
/**
* Draws current state of the game Canvas.
*/
private int mBGFarMoveX = 0;
private int mBGNearMoveX = 0;
private void doDrawRunning(Canvas canvas) {
// decrement the far background
mBGFarMoveX = mBGFarMoveX - 1;
// decrement the near background
mBGNearMoveX = mBGNearMoveX - 4;
// calculate the wrap factor for matching image draw
int newFarX = backGround.getWidth() - (-mBGFarMoveX);
// if we have scrolled all the way, reset to start
if (newFarX <= 0) {
mBGFarMoveX = 0;
// only need one draw
canvas.drawBitmap(backGround, mBGFarMoveX, 5000, null);
} else {
// need to draw original and wrap
canvas.drawBitmap(backGround, mBGFarMoveX, 0, null);
canvas.drawBitmap(backGround, newFarX, 0, null);
}
}
}
Example:
You should try using a transformation matrix built for ScaleToFit.CENTER. For example:
Matrix m = new Matrix();
m.setRectToRect(new RectF(0, 0, b.getWidth(), b.getHeight()), new RectF(0, 0, reqWidth, reqHeight), Matrix.ScaleToFit.CENTER);
return Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);
I have a view and I want to draw a shape on it (circle for example) after click.
I've tried to do this but there are two problems -
onDraw is never called.
Not sure the setLayoutParams(v.getLayoutParams) will give me the result I want.
#Override
public void onClick(View v) {
CircleView circle = new CircleView(GameXoActivity.this, v.getWidth(), v.getHeight());
circle.setLayoutParams(v.getLayoutParams());
circle.startDrawing();
}
CircleView:
public CircleView(Context context, int width, int height) {
super(context);
this.width = width;
this.height = height;
}
protected void startDrawing() {
this.postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
Log.d("TAG", "onDraw");
// draw circle
}
}
}
UPDATE:
The shape is not an image and I want to draw it with animation (I didn't write the entire code).
Also, the shape is not always a circle, so using a drawable-state is not an option.
Because there is not just one view, but 9, I don't think the making 9 more on top of them would be right.
As I'm sure you'll need to customize this quite a bit, I've left things rather generic. The following example will animate a blue circle being drawn clockwise, starting from the east (0 degrees), on top of the View's content when the View is clicked.
public class CircleView extends View
{
private static final int MARGIN = 50;
Handler handler = new Handler();
Paint paint = new Paint();
RectF rect = new RectF();
boolean drawing = false;
float sweep = 0;
public CircleView(Context context)
{
this(context, null);
}
public CircleView(Context context, AttributeSet attrs)
{
super(context, attrs);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(15);
paint.setColor(Color.BLUE);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawArc(rect, 0, sweep, false, paint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
rect.set(MARGIN, MARGIN, w - MARGIN, h - MARGIN);
}
public void startAnimation()
{
drawing = true;
handler.post(runnable);
}
Runnable runnable = new Runnable()
{
#Override
public void run()
{
sweep += 10;
if (!(sweep > 360))
{
invalidate();
handler.postDelayed(this, 20);
}
else
{
drawing = false;
sweep = 0;
}
}
};
}
In this Activity example, I used an image that most developers would already have in their project, but it can obviously be changed to your custom image. Also, for the sake of simplicity and brevity, the CircleView is set as the entire content of the Activity, but it can easily be listed in an xml layout, as well.
public class MainActivity extends Activity
{
CircleView circle;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
circle = new CircleView(this);
circle.setBackgroundResource(R.drawable.ic_launcher);
circle.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
circle.startAnimation();
}
}
);
setContentView(circle);
}
}
I suggest, create two imageView with same dimensions, set the image you want to display on image view and then make the second image invisible .
For example :
circleimage.setVisibility(View.INVISIBLE);//now its hidden(do it OnCreate)
and then show 2ndimage when 1stimage is clicked(do it in onclick of 1st image)
circleimage.setVisibility(View.VISIBLE);
If you want to mess with drawing of the object you should overridepublic void draw(Canvas canvas) and not protected void onDraw(Canvas canvas)
EDIT:Please read comments, this first statement of my answer is probably wrong
but I would use a FrameLayout or a RelativeLayout and put the images one on top of another.
Then you can play with the visibility of the overlaying image in order to hide/show it.
EDIT:
In case your circle is not an image and needs to be drawn, make your own circle class extending View and use it as a component in the FrameLayout or RelativeLayout as you would do if it were an image
i used to paint the background but i want to make it drawable /picture background and the wrong in drawbackground what shall i do ?
public void surfaceCreated(SurfaceHolder arg0) {
Bitmap background = BitmapFactory.decodeResource(context.getResources(), R.drawable.download);
float scale = (float)background.getHeight()/(float)GetHeight();
int newWidth = Math.round(background.getWidth()/scale);
int newHeight = Math.round(background.getHeight()/scale);
scaled = Bitmap.createScaledBitmap(background, newWidth, newHeight, true);
}
public void onDraw(Canvas canvas) {
canvas.drawPicture(mbgdDrawable);// draw the background
}
public void DrawBackground(Canvas canvas) {
canvas.drawBitmap(scaled, mScreenHeight, mScreenWidth,null);
}
I want to zoom in the canvas board partially as the letter tray button will be fixed and the board will be zoom,
The partially coding is as mention below and the screen is fully draw in canvas which can be viewed as mention at the bottom.. please help and thanks for you concern.
public class BoardView extends SurfaceView implements SurfaceHolder.Callback
{
class DrawingThread extends Thread implements OnTouchListener {
public DrawingThread(SurfaceHolder holder, Handler handler) {
mSurfaceHolder = holder;
public void setRunning(boolean b) {
mRun = b;
}
#Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
// System.gc();
// c.scale(canvasScaleX, canvasScaleY);
// c.save();
// c.translate(canvasTranslateX, canvasTranslateY);
doDraw(c);
}
updateGame();
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
private void doDraw(Canvas canvas) {
if (ge == null)
return;
Rect bRect = new Rect(0, 0, dims.getTotalWidth(),
dims.getScoreHeight() + dims.getBoardheight());
Drawable drawable = getResources().getDrawable(R.drawable.board);
drawable.setBounds(bRect);
drawable.draw(canvas);
Rect tRect = new Rect(0, dims.getScoreHeight()
+ dims.getBoardheight(), dims.getTotalWidth(),
dims.getTotalHeight());
canvas.drawRect(tRect, fillTrayPaint);
int topHeight = dims.getScoreHeight() + dims.getBoardheight();
int bottom = (dims.getTotalHeight() + 5)
- (dims.getTotalWidth() / Tray.TRAY_SIZE);
Rect rect = new Rect(0, topHeight, dims.getTotalWidth(), bottom - 7);
Drawable drawableTray = getResources()
.getDrawable(R.drawable.strip);
drawableTray.setBounds(rect);
drawableTray.draw(canvas);
drawTray(canvas);
drawBoard(canvas);
// drawScore(canvas);
drawMovingTile(canvas);
}
public BoardView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// endTurn=(ImageButton)findViewById(R.id.endturn_button_horizontal);
thread = new DrawingThread(holder, handler);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
final float scale1 = getResources().getDisplayMetrics().density;
defaultFontSize = (int) (MIN_FONT_DIPS * scale1 + 0.5f);
thread.setDefaultFontSize(defaultFontSize);
dimensions = calculateDimensions(getWidth(), getHeight());
thread.setDimensions(dimensions);
thread.setRunning(true);
// if (thread != null && !thread.isAlive())
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
We can simply do it by canvas.clipRect(rectangle), at this point what we are suppose to do is save the canvas canvas.save() and after canvas.clipRect(rectangle) call canvas.restore().
Consider trying something like this:
Create a bitmap from the canvas you wish to zoom into.
When rendering that bitmap to the screen, you can change the scr rect
(which will "zoom" into the bitmap).
I hope this helps.
I think you need to render your game board into offscreen bitmap and then draw this bitmap at canvas using Matrix, so you will be able to change its position and scale.
I believe the easiest way to do this would be to use Canvas#scale(float scale, int pivotx, int pivoty) method. It essentially grows the entire canvas uniformally which really is what zooming is from a 2D perspective.
canvas.scale(2.0f, getWidth()/2, getHeight()/2) will scale the image by 2 from the center. canvas.scale(1.0f, getWidth()/2, getHeight()/2) will shrink it back.
I have never tried this in this context, so if you decide to try it report back the results please! I'd like to know for future reference.