Draw background of custom View from .png file on Android - android

I created a custom View by extending from View. In onDraw() I managed to draw some circles and other stuff. But now I want to add a background from a resource (sd card or a stream) which is actually a map I download from our server and than draw on it. It's for Android 8+
#Override
protected void onDraw(Canvas canvas) {
Canvas g = canvas;
String file = "/mnt/sdcard/download/tux.png";
Bitmap bg = null;
try {
bg = BitmapFactory.decodeFile(file);
g.setBitmap(bg);
} catch (Exception e) {
Log.d("MyGraphics", "setBitmap() failed according to debug");
}
}
Somehow g.setBitmap(bg) keeps failing, I haven't looked at the image specs, but actually it's just a tux image (no 24 bits colors) of PNG format.
Can someone give me some tips how to add a background image so I can draw on it?
Thank you.

You actually don't want to draw on to the bitmap you load, you just want to draw it on the Canvas, so you should use Canvas.drawBitmap(). You also really should not load a Bitmap in each onDraw(), do it in the constructor instead. Try this class:
package com.example.android;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
public class CustomView extends View {
private final Bitmap mBitmapFromSdcard;
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mBitmapFromSdcard = BitmapFactory.decodeFile("/mnt/sdcard/download/tux.png");
}
#Override
protected void onDraw(Canvas canvas) {
Canvas g = canvas;
if (mBitmapFromSdcard != null) {
g.drawBitmap(mBitmapFromSdcard, 0, 0, null);
}
}
}
You can also let Android draw the bitmap in the background:
package com.example.android;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.View;
public class CustomView extends View {
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
Bitmap bm = BitmapFactory.decodeFile("/mnt/sdcard/download/tux.png");
if (bm != null) {
setBackgroundDrawable(new BitmapDrawable(bm));
}
}
}

I'm afraid that you get OutOfMemoryError, cause onDraw is called many times during view lifecycle, and every time you are allocating memory for new bitmap. Just make bg member (maybe - static) of your class and load it only once - in your view's constructor. And don't forget to recycle bitmap on view's detaching.

Related

custom made arc shaped seekbar

What I am trying to achieve is to make an arc shaped seekbar. I know there are plenty of libraries I could use to achieve this, but I am just trying my hands on custom made views.
I have encountered couple of problems:
I have a class which extends SeekBar, and I have implemented onDraw and onMeasure methods as well, but I am not able to view that in layout editor in eclipse, here is the code for the custom view class:
package com.custom.android.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.SeekBar;
import android.widget.Toast;
public class CustomSeekBar extends SeekBar {
public CustomSeekBar(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public CustomSeekBar(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
super.draw(canvas);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Here is my layout xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<com.custom.android.views.CustomSeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/seekBar"/>
</RelativeLayout>
If I use canvas class to draw an arc or any shape, would that be a good starting point?
What exactly is wrong with the eclipse adt and how could I use the onDraw method to give shape to that seekbar?
Drawing a ProgressBar with any shape, is pretty easy. With the SeekBar you have some complexity, since you have to achieve 3 diferent things:
Draw the line
Draw the draggable thumb, if you want.
Handle the user interaction
You have to think of it as an arc that is draw inside a rectangle. So point 3 could be easy: just let the user move the finger in a horizontal line, or exactly over the arc, but considering only the x coordinate of the touch event. What does this mean, in short? ok, good news: you dont have to do anything, since thats the normal behavior of the base SeekBar.
For the second point, you can choose an image for the handler, and write it in the corresponding position with a little maths. Or you can forget the handler for know, and just draw the seek bar as a line representing the full track, and another line over it representing the progress. When you have this working, if you want you can add the handler.
And for the first point, this is the main one, but its not hard to achieve. You can use this code:
UPDATE: I made some improvements in the code
public class ArcSeekBar extends SeekBar {
public ArcSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ArcSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private Paint mBasePaint;
private Paint mProgressPaint;
private RectF mOval = new RectF(5, 5, 550, 550);
private int defaultmax = 180;
private int startAngle=180;
private int strokeWidth=10;
private int trackColor=0xFF000000;
private int progressColor=0xFFFF0000;
public void setOval(RectF mOval) {
this.mOval = mOval;
}
public void setStartAngle(int startAngle) {
this.startAngle = startAngle;
}
public void setStrokeWidth(int strokeWidth) {
this.strokeWidth = strokeWidth;
}
public void setTrackColor(int trackColor) {
this.trackColor = trackColor;
}
public void setProgressColor(int progressColor) {
this.progressColor = progressColor;
}
public ArcSeekBar(Context context) {
super(context);
mBasePaint = new Paint();
mBasePaint.setAntiAlias(true);
mBasePaint.setColor(trackColor);
mBasePaint.setStrokeWidth(strokeWidth);
mBasePaint.setStyle(Paint.Style.STROKE);
mProgressPaint = new Paint();
mProgressPaint.setAntiAlias(true);
mProgressPaint.setColor(progressColor);
mProgressPaint.setStrokeWidth(strokeWidth);
mProgressPaint.setStyle(Paint.Style.STROKE);
setMax(defaultmax);// degrees
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawArc(mOval, startAngle, getMax(), false, mBasePaint);
canvas.drawArc(mOval, startAngle, getProgress(), false, mProgressPaint);
invalidate();
//Log.i("ARC", getProgress()+"/"+getMax());
}
}
Of course, you can and you should make everything configurable, be means of the contructor, or with some setters for the start and end angles, dimensions of the containing rectangle, stroke widths, colors, etc.
Also, note that the arc is drawn from 0 to getProgress, being this number an angle relative to the x axis, growing clocwise, so, if it go from 0 to 90 degrees, it will be something like:
Of course you can change this: canvas.drawArc get any number as an angle, and it is NOT treated as module 360, but you can do the maths and have it starting and ending in any point you want.
In my example the beggining is in the 9 of a clock, and it takes 180 degrees, to the 3 in the clock.
UPDATE
I uploaded a running example to github

Drawing using canvas

Hi Im new to Android Development and I wish to get my head around drawing with canvas.
I have several questions which the develop.android.com website doesn't make clear:
-Can you use a simple canvas for different views or do you have to create a new canvas each time you wish to add a new view?
-Are bitmaps the only image type "things" that can be drawn onto a canvas? for example in my code below I have a drawable called swirls however I cannot use this, do I have to convert it to a bitmap? And how do I do that?
package ple.kon15;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.ImageView;
public class DrawBG extends View {
Bitmap swirls = BitmapFactory.decodeResource(getResources(),R.drawable.swirls);
public DrawBG(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawBitmap(swirls, swirls.getWidth(), swirls.getHeight(),null);
}
public DrawBG(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public DrawBG(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
Yes, each View has its own Canvas.
You can convert drawable resources to Bitmaps using:
BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_resource);
1- yes, every view has its own canvas but you can also have complex views that has one canvas,
2- you should decode it with BitmapFactory.decodeResource(your_context.getResources(), R.drawable.swirls); to use. And no not only bitmap, you can draw lines, vertex, rectangles, rounded rectangles, paths with canvas, and if you want to do more complex things like a game or a complex animation, you can also use SurfaceView but you should think really carefully since it's a bit more advanced and give all the handling of surcace to you

Zoom effect on Android SurfaceView

I am developing a Car Race Game. I am able to move, stop, accelerate. But i want that when i press screen then car(bitmap of car image) should look like, it had jumped on its place.I am using surfaceviewto draw view
I do not want to use 3D openGL. Any help is much appreciated
finally i used.On touch event, i create a bigger bitmap and draw it on same place. So it will give zoom effect
Bitmap bitmapToZoom; // Create a Bitmap
// Now zoom it
bitmapToZoom=Bitmap.createScaledBitmap(bitmapToZoom, bitmapToZoom.getWidth()+30,bitmapToZoom.getHeight()+30, null);
//now draw it again
canvas.drawBitmap(bitmapToZoom, 0,0,null);
So now finally, Zooming actor on touch code will be like this. Class Name ZoomonTouc.java
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.SurfaceView;
public class ZoomonTouc extends SurfaceView {
public Bitmap mMyChracter;
public ZoomonTouc(Context context) {
super(context);
mMyChracter = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_launcher);
setWillNotDraw(false);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mMyChracter, mMyChracter.getWidth()/2, mMyChracter.getHeight()/2, null);
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
RectF rectF = new RectF(mMyChracter.getWidth()/2, mMyChracter.getHeight()/2, mMyChracter.getWidth() + 100,
mMyChracter.getHeight() + 100);
if (rectF.contains(event.getX(), event.getY())) {
mMyChracter = Bitmap.createScaledBitmap(mMyChracter,
mMyChracter.getWidth() + 10, mMyChracter.getHeight() + 10,
false);
}
return true;
}
}
To test it properly create one activity and set above surface to activity background
setContentView(new ZoomonTouc(this));

Android LAYOUT xml / java error

I am trying to get a panel which has a canvas containing an image which i will place over another image and when i touch the screen the top(overlaying) image will be erased by means of a PoerterDuffXfermode(PorterDuff.Mode) etc ,, anyway I have the functionality done and dusted thanks to the help of a guy on this forum who provided some code which basically carried out exactly what I needed, but I'm having one slight problem,, the guys implementation of the code, will not allow me to reference the Panel class properly in XML to place the Panel on a pre defined XML (main.xml) file. its giving me an error stating
Custom view Panel is not using the 2- or 3-argument View constructors; XML attributes will not work
This is what my xml looks like on a basic scale (just the view in place within the outer linearlayout).
<com.easyscratch.full.Panel
xmlns:android="http://schemas.android.com/apk/res/android"
android:id ="#+id/easyCustView"
android:layout_width="300dp"
android:layout_height="300dp"
android:visibility="visible"
android:focusableInTouchMode="true"/>
The java is as followed .( PANEL CLASS)
package com.easyscratch.full;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
class Panel extends View
{
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
Bitmap bitmap;
Canvas pcanvas ;
int x = 0;
int y =0;
int r =0;
public Panel(Context context) {
super(context);
Log.v("Panel", ">>>>>>");
setFocusable(true);
setBackgroundColor(Color.GREEN);
// setting paint
mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setAntiAlias(true);
// getting image from resources
Resources r = this.getContext().getResources();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.foreground_image);
// converting image bitmap into mutable bitmap
bitmap = Bitmap.createBitmap(295, 260, Config.ARGB_8888);
pcanvas = new Canvas();
pcanvas.setBitmap(bitmap); // drawXY will result on that Bitmap
pcanvas.drawBitmap(bm, 0, 0, null);
}
#Override
protected void onDraw(Canvas canvas) {
// draw a circle that is erasing bitmap
pcanvas.drawCircle(x, y, r, 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;
}
}
BASIC MAIN CLASS CALLING MAIN.XML
package com.easyscratch.full;
import android.app.Activity;
import android.os.Bundle;
public class easyscratch extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
,, If only Someone cud tell me what im doing wrong , or maybe an alternative implementation of the
public Panel(Context context) {
super(context);
anyway thanks alot in advanced really would appriciate some help as soon as possible :)
CHEERS GUYS!
Your constructor for Panel must also, at least, have an AttributeSet field.
public Panel(Context context, AttributeSet attr){
super.(context, attr);

Drawing onClick in Android

I am trying to make a program the makes a graph using user input values from 2 EditText Fields. The program should use this input to draw a line from the center of an axis to a point specified by the information. I have a button that each time it is clicked should make a new line to the point specified (so there can be more than one line) I have created a custom view to hold the axis, but that utilizes its onDraw method, obviously, so I cant use it also to draw the new line.
Here is the code for my custom view:
package android.physicsengine;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class AxisDrawing extends View{
public AxisDrawing(Context context){
super(context);
}
public AxisDrawing(Context context, AttributeSet attrs){
super(context, attrs);
}
public AxisDrawing(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(Color.RED);
canvas.drawLine(canvas.getWidth()/2, canvas.getHeight()/2-200, canvas.getWidth()/2 ,canvas.getHeight()/2+100, linePaint);
canvas.drawLine(canvas.getWidth()/2-150, canvas.getHeight()/2-75, canvas.getWidth()/2+150 ,canvas.getHeight()/2-75, linePaint);
}
}
If your custom view class is defined in the activity, then it is an inner class of that activity and has access to variables and arrays which are defined on the activity level.
Every time the user clicks the button, you should process and store the information into these common variables or arrays which onDraw can access and from them calculate the next line or the entire graph. If your custom view is a separate class, then you need to pass the data, one way to do it is to use static varibales..
to make onDraw() method draw again your graph you need to state:
myCustomView.invalidate();
in the button click event, just after you set the new data for the graph.
You simply need to set the data in the custum view (Globally ) and call invalidate which will redraw the view.
importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.util.AttributeSet;
importandroid.view.View;
#Override
protectedvoidonDraw(Canvascanvas
{
canvas.drawColor(Color.BLACK);
PaintlinePaint=newPaint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(Color.RED);
canvas.drawLine(data,data,getWidtt()-data,getHeight()-data,linePaint);
//you can also pplaceinvalidate() here which will recursively redraw the canvas in aloop
}
publicvoidsetData(intdata)
{
this.data=data;
invalidate();
}
}

Categories

Resources