I have simple layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RelativeLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/backgroundtimer"
android:orientation="vertical" >
<TextView
android:id="#+id/TextView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="#string/hello" />
<com.fmech.zenclock.surface.ZenClockSurface
android:id="#+id/zenClockSurface1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
And a have ZenClockSurface class
package com.fmech.zenclock.surface;
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.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class ZenClockSurface extends SurfaceView implements SurfaceHolder.Callback{
private DrawClock drawClock;
public ZenClockSurface(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
getHolder().addCallback(this);
}
public ZenClockSurface(Context context) {
super(context);
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
getHolder().addCallback(this);
}
public ZenClockSurface(Context context, AttributeSet attrs) {
super(context, attrs);
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
//this.getHolder().setFormat(PixelFormat.TRANSPARENT);
drawClock = new DrawClock(getHolder(), getResources());
drawClock.setRunning(true);
drawClock.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
// завершаем работу потока
drawClock.setRunning(false);
while (retry) {
try {
drawClock.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class DrawClock extends Thread{
private boolean runFlag = false;
private SurfaceHolder surfaceHolder;
private Bitmap picture;
private Matrix matrix;
private long prevTime;
private Paint painter;
public DrawClock(SurfaceHolder surfaceHolder, Resources resources){
this.surfaceHolder = surfaceHolder;
this.surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
picture = BitmapFactory.decodeResource(resources, R.drawable.ic_launcher);
matrix = new Matrix();
this.painter=new Paint();
this.painter.setStyle(Paint.Style.FILL);
}
public void setRunning(boolean run) {
runFlag = run;
}
#Override
public void run() {
Canvas canvas;
while (runFlag) {
matrix.preRotate(1.0f, picture.getWidth() / 2, picture.getHeight() / 2);
canvas = null;
try {
//surfaceHolder.getSurface().setAlpha(0.5f);
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(picture, matrix, this.painter);
}
}
finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
}
And code of activity
package com.fmech.zenclock.surface;
import android.app.Activity;
import android.graphics.PixelFormat;
import android.os.Bundle;
public class ZenClockSurfaceActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
setContentView(R.layout.main);
}
}
I want what background color of android picture was tranparent but i get black background.
I have background on RelativeLayout with some picture bu SurfaceView rotate Android icon with no transparent.
How i can do transparent?
Yeah, i did it i solve problem
Activity code
package com.fmech.zenclock.surface;
import android.app.Activity;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class ZenClockSurfaceActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ZenClockSurface sfvTrack = (ZenClockSurface)findViewById(R.id.zenClockSurface1);
sfvTrack.setZOrderOnTop(true); // necessary
SurfaceHolder sfhTrack = sfvTrack.getHolder();
sfhTrack.setFormat(PixelFormat.TRANSLUCENT);
}
}
Surface Code
package com.fmech.zenclock.surface;
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.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PixelFormat;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class ZenClockSurface extends SurfaceView implements
SurfaceHolder.Callback {
private DrawClock drawClock;
public ZenClockSurface(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
getHolder().addCallback(this);
}
public ZenClockSurface(Context context) {
super(context);
getHolder().addCallback(this);
}
public ZenClockSurface(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
drawClock = new DrawClock(getHolder(), getResources());
drawClock.setRunning(true);
drawClock.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
drawClock.setRunning(false);
while (retry) {
try {
drawClock.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class DrawClock extends Thread {
private boolean runFlag = false;
private SurfaceHolder surfaceHolder;
private Bitmap picture;
private Matrix matrix;
private Paint painter;
public DrawClock(SurfaceHolder surfaceHolder, Resources resources) {
this.surfaceHolder = surfaceHolder;
picture = BitmapFactory.decodeResource(resources,
R.drawable.ic_launcher);
matrix = new Matrix();
this.painter = new Paint();
this.painter.setStyle(Paint.Style.FILL);
this.painter.setAntiAlias(true);
this.painter.setFilterBitmap(true);
}
public void setRunning(boolean run) {
runFlag = run;
}
#Override
public void run() {
Canvas canvas;
while (runFlag) {
matrix.preRotate(1.0f, picture.getWidth() / 2,
picture.getHeight() / 2);
canvas = null;
try {
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
canvas.drawBitmap(picture, matrix, this.painter);
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
}
It's work.
In API 3 and 4, you cannot put anything behind a SurfaceView. Quoting Dianne Hackborn:
The surface view is actually BEHIND your window, and a hole punched in the window for you to see it. You thus can put things on top of it in your window, but nothing in your window can appear behind it.
http://groups.google.com/group/android-developers/browse_thread/thread/8d88ef9bb22da574
From API 5 onwards you can use setZOrderOnTop. The trick is that you have to do it in your constructor so it gets called before the view is attached to the window:
public ZenClockSurface(Context context, AttributeSet attrs) {
super(context, attrs);
setZOrderOnTop(true);
SurfaceHolder holder = getHolder();
holder.setFormat(PixelFormat.TRANSLUCENT);
}
Related
I'm trying to draw a rectangle over camera2 textureview, when i run the code I see the usual camera screen with moving square , and when I Click (touch) it, app crashes with the error in topic. I also not sure I implemented the custom view correctly, Here are all the relevant parts, Would love some help (I'm not sure I have a good layout xml, I added ViewGroup code under OnCrearte, not sure i even need to touch xml)
-------CameraActivity.java:
package com.example.android.camera2video;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.ViewGroup;
public class CameraActivity extends Activity {
private Context context;
CustomView customview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
customview = new CustomView(this);
final ViewGroup viewGroup = (ViewGroup) ((ViewGroup) this.findViewById(android.R.id.content)).getChildAt(0);
viewGroup.addView(new CustomView(this));
if (null == savedInstanceState) {
getFragmentManager().beginTransaction()
.replace(R.id.container, Camera2VideoFragment.newInstance())
.commit();
}
}
}
-------CustomView.java
package com.example.android.camera2video;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class CustomView extends SurfaceView {
private Paint paint;
private SurfaceHolder mHolder;
private Context context;
public CustomView(Context context) {
super(context);
mHolder = getHolder();
mHolder.setFormat(PixelFormat.TRANSPARENT);
this.context = context;
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// real work here
}
private void doAdditionalConstructorWork() {
// init variables etc.
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
invalidate();
if (mHolder.getSurface().isValid()) {
final Canvas canvas = mHolder.lockCanvas();
Log.d("touch", "touchRecieved by camera");
System.err.println("EXIT 1");
if (canvas != null) {
Log.d("touch", "touchRecieved CANVAS STILL Not Null");
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.drawColor(Color.TRANSPARENT);
canvas.drawCircle(event.getX(), event.getY(), 100, paint);
mHolder.unlockCanvasAndPost(canvas);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Canvas canvas1 = mHolder.lockCanvas();
if(canvas1 !=null){
canvas1.drawColor(0, PorterDuff.Mode.CLEAR);
mHolder.unlockCanvasAndPost(canvas1);
}
}
}, 1000);
}
mHolder.unlockCanvasAndPost(canvas);
}
}
return false;
}
}
-----fragment_camera2_video.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.android.camera2video.AutoFitTextureView
android:id="#+id/texture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_below="#id/texture"
android:background="#4285f4">
<Button
android:id="#+id/video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/record" />
<ImageButton
android:id="#+id/info"
android:contentDescription="#string/description_info"
style="#android:style/Widget.Material.Light.Button.Borderless"
android:layout_width="4dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:padding="20dp"
android:src="#drawable/ic_action_info" />
</FrameLayout>
</RelativeLayout>
Only one thing can be drawing to a View at a time; once the SurfaceView is connected to the camera, you're not able to lock it for drawing yourself.
Your crash is probably because you're calling mHolder.unlockCanvasAndPost(canvas); outside of the null check.
If you want to draw over the cameara preview, you need a second View positioned on top of the SurfaceView.
i am new to android and stuck with getting screen size. Here is my code,
package com.piyush.droidz;
import android.app.Activity;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.piyush.droidz.model.boy;
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = MainGamePanel.class.getSimpleName();
private GameThread td;
private boy b1;
private int s_width;
private int s_height;
public MainGamePanel(Context context) {
super(context);
getHolder().addCallback(this);
Log.d(TAG, "Screen width=" + s_width);
b1 = new boy(BitmapFactory.decodeResource(getResources(), R.drawable.boy), 50, 50);
td = new GameThread(getHolder(), this);
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
s_width = getHolder().getSurfaceFrame().width();
Log.d(TAG, "Present Screen width=" + s_width);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
td.setRunning(true);
td.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
td.setRunning(false);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
b1.handleActionDown((int)event.getX(), (int)event.getY());
if (event.getY()>getHeight()-50) {
td.setRunning(false);
((Activity) getContext()).finish();
}
else {
Log.d(TAG, "Coordinates: X="+event.getX()+", Y="+event.getY());
}
}
if (event.getAction()==MotionEvent.ACTION_MOVE) {
if (b1.isTouched()) {
b1.setX((int)event.getX());
b1.setY((int)event.getY());
}
}
if (event.getAction()==MotionEvent.ACTION_UP) {
b1.setTouched(false);
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.parseColor("#ff0000"));
b1.draw(canvas);
}
}
in the first log-tag it shows 0 where as in the second log-tag it shows exact width of emulator. I have tried initializing the "s_width" with getHolder().getSurfaceFrame().width() even before the constructor but still "s_width" is 0. Also tried WindowManagerand getwindowManager() but it is not recognized by IDE in this class. Here is my Activity class,
package com.piyush.droidz;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class DroidzActivity extends Activity {
MainGamePanel mgp;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mgp = new MainGamePanel(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(mgp);
}
}
please help!
Have you tried?
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(metrics);
You need to use context for getting the WindowManager. please see the documentation
http://developer.android.com/reference/android/view/WindowManager.html
Preferably the above should be done inside the constructor
public MainGamePanel(Context context) {}
Hope it helps. :)
Update : I unfortunately can't get a solution for my question yet...
I am developing a recorder on android and I want show visualize while recording. I have bellow classes.
When I run project and press start record I cant see any visualizer while recording.I test bellow code on real device and add permissions to manifest.
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
I use from open source project visualizer for playing by "Felix Palmer" and own add new method for link recorder to visualizer. I think must be run perfectly.but really any showing for my recorder unfortunately. (code has not any error)
How i can resolve my problem and see visualizer?
This is VisualizerView.java class...
import java.util.HashSet;
import java.util.Set;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.media.audiofx.Visualizer;
import android.util.AttributeSet;
import android.view.View;
import com.pheelicks.visualizer.renderer.Renderer;
public class VisualizerView extends View {
private byte[] mBytes;
private byte[] mFFTBytes;
private Rect mRect = new Rect();
private Visualizer mVisualizer;
private Set<Renderer> mRenderers;
private Paint mFlashPaint = new Paint();
private Paint mFadePaint = new Paint();
public VisualizerView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs);
init();
}
public VisualizerView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public VisualizerView(Context context)
{
this(context, null, 0);
}
private void init() {
mBytes = null;
mFFTBytes = null;
mFlashPaint.setColor(Color.argb(122, 255, 255, 255));
mFadePaint.setColor(Color.argb(238, 255, 255, 255)); // Adjust alpha to change how quickly the image fades
mFadePaint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
mRenderers = new HashSet<Renderer>();
}
//-----------------------------------------------
public void link(MediaRecorder recorder)
{
if(recorder == null)
{
throw new NullPointerException("Cannot link to null MediaPlayer");
}
mVisualizer = new Visualizer(0);
mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
Visualizer.OnDataCaptureListener datacaptureListener1=new Visualizer.OnDataCaptureListener()
{
#Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate)
{
// TODO Auto-generated method stub
updateVisualizer(bytes);
}
#Override
public void onFftDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate)
{
// TODO Auto-generated method stub
updateVisualizerFFT(bytes);
}
};
mVisualizer.setDataCaptureListener(datacaptureListener1,Visualizer.getMaxCaptureRate() /2,false,true);
mVisualizer.setEnabled(true);
}
//-----------------------------------------------
This is AudioRecorder.java...
public class AudioRecorder
{
private String name="";
private static int id=0;
private MediaRecorder recorder = new MediaRecorder();
private String path=null;
VisualizerView mVisualizerView;
private Context context=null;
//-----------------------------------------------
public AudioRecorder(Context context)
{
this.context=context;
}
//-----------------------------------------------
public void Record() throws IOException
{
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(this.path);
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.recorder1, null);
mVisualizerView = (VisualizerView)view.findViewById(R.id.visualizer1);
mVisualizerView.link(recorder);
addLineRenderer();
try
{
recorder.prepare();
}
catch (IllegalStateException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
try
{
System.out.println("****");
recorder.start();
}
catch (Exception e)
{
}
}
//------------------------------------------
public void stopRecord() throws IOException
{
recorder.stop();
recorder.release();
}
And this is visualizer in my XML.
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="#drawable/highlight_shared"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#drawable/corner_liner" >
<com.pheelicks.visualizer.VisualizerView
android:id="#+id/visualizer1"
android:layout_width="match_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
</LinearLayout>
It is the issue with certain phones for different reasons. You can see here
https://code.google.com/p/android/issues/detail?id=64423
https://github.com/felixpalmer/android-visualizer/issues/5
for the list of unsupported devices.
I try to use my own SurfaceView in XML and I am unable to do it. I get NullPointerException.
According internet it should look like this:
Activity:
package editor;
import android.app.Activity;
import android.os.Bundle;
import com.example.balls_menu_v1.R;
public class EditorActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.editor);
EditorView ev = (EditorView) findViewById(R.id.editorView);
}
}
If I comment findViewById I get NullPointerException.
SurfaceView:
package editor;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class EditorView extends SurfaceView {
public EditorView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onFinishInflate() {
super.onFinishInflate();
SurfaceHolder holder = getHolder();
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.GREEN);
holder.unlockCanvasAndPost(canvas);
}
}
Layout: editor.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" >
<editor.EditorView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/editorView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
I found answer: implement SurfaceHolder.Callback, add all 3 constructors of SurfaceView and add getHolder().addCallback(this); to each constructor.
Code:
public class EditorView extends SurfaceView implements SurfaceHolder.Callback{
public EditorView(Context context) {
super(context);
getHolder().addCallback(this);
// TODO Auto-generated constructor stub
}
public EditorView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
// TODO Auto-generated constructor stub
}
public EditorView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
getHolder().addCallback(this);
// TODO Auto-generated constructor stub
}
public void doDraw() {
SurfaceHolder holder = getHolder();
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.GREEN);
holder.unlockCanvasAndPost(canvas);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {}
#Override
public void surfaceCreated(SurfaceHolder holder) {
doDraw();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
you can't call Canvas canvas = holder.lockCanvas();`
before the oncreate flow finish,
you should call it after the oncreate finish
I am developing an Android Digital signature app in which user can sign and i have to save this file as image.i am using SurfaceView for drawing. DigitalSignatureActivity has two Buttons Save,Clear.
1.Save Button to save file as image
2.Clear Button to clear surface.
But i am unable to clear the surface.i tried drawingSurface.setBackgroundColor(Color.BLACK); still previous sign is retained and canvas.drawColor(Color.BLACK); has no effect and
i am able to save file but its not storing signature perfectly(Some contents are missing) please help.
My code is:
DigitalSignatureActivity.java
package com.pop.digitalsign;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Bundle;
import android.os.Environment;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
public class DigitalSignatureActivity extends Activity implements
View.OnTouchListener {
private DrawingSurface drawingSurface;
private DrawingPath currentDrawingPath;
private Paint currentPaint;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setCurrentPaint();
drawingSurface = (DrawingSurface) findViewById(R.id.drawingSurface);
drawingSurface.setOnTouchListener(this);
}
private void setCurrentPaint() {
currentPaint = new Paint();
currentPaint.setDither(true);
currentPaint.setColor(Color.WHITE);
currentPaint.setStyle(Paint.Style.STROKE);
currentPaint.setStrokeJoin(Paint.Join.ROUND);
currentPaint.setStrokeCap(Paint.Cap.ROUND);
currentPaint.setStrokeWidth(2);
}
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
drawingSurface.setBackgroundColor(0);
currentDrawingPath = new DrawingPath();
currentDrawingPath.paint = currentPaint;
currentDrawingPath.path = new Path();
currentDrawingPath.path.moveTo(motionEvent.getX(),
motionEvent.getY());
currentDrawingPath.path.lineTo(motionEvent.getX(),
motionEvent.getY());
} else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
currentDrawingPath.path.lineTo(motionEvent.getX(),motionEvent.getY());
drawingSurface.addDrawingPath(currentDrawingPath);
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
currentDrawingPath.path.lineTo(motionEvent.getX(),motionEvent.getY());
}
return true;
}
//To save file as Image
public void saveDrawing(View v) throws IOException {
File mediaStorageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"MySignatures");
Bitmap nBitmap = drawingSurface.getBitmap();
try {
if (!mediaStorageDir.exists()) {
mediaStorageDir.mkdirs();
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
.format(new Date());
File mediaFile = new File(mediaStorageDir.getPath()
+ File.separator + "SIGN_" + timeStamp + ".png");
FileOutputStream out = new FileOutputStream(mediaFile);
nBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
Toast.makeText(this, "Signature saved to " + mediaFile,
Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Not saved",
Toast.LENGTH_SHORT).show();
}
}
//To clear Surface
public void clearScreen(View v) {
//drawingSurface.setBackgroundColor(Color.BLACK);
drawingSurface.clear();
}
}
DrawingSurface.java
package com.pop.digitalsign;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback {
private Boolean _run;
protected DrawThread thread;
private Bitmap mBitmap;
Canvas canvas;
private CommandManager commandManager;
public DrawingSurface(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
commandManager = new CommandManager();
thread = new DrawThread(getHolder());
}
class DrawThread extends Thread{
public SurfaceHolder mSurfaceHolder;
public DrawThread(SurfaceHolder surfaceHolder){
mSurfaceHolder = surfaceHolder;
}
public DrawThread() {
// TODO Auto-generated constructor stub
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run() {
canvas = null;
while (_run){
try{
canvas = mSurfaceHolder.lockCanvas(null);
//canvas.drawColor(Color.WHITE);
if(mBitmap == null){
mBitmap = Bitmap.createBitmap (1, 1, Bitmap.Config.ARGB_8888);;
}
final Canvas c = new Canvas (mBitmap);
c.drawColor(0, PorterDuff.Mode.CLEAR);
commandManager.executeAll(c);
canvas.drawBitmap (mBitmap, 0, 0,null);
} finally {
if(canvas!=null){
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
public void addDrawingPath (DrawingPath drawingPath){
commandManager.addCommand(drawingPath);
}
public void clear(){
//Here i want to clear surface
canvas.drawColor(Color.BLACK);//it has no effect
}
public boolean hasMoreUndo(){
return commandManager.hasMoreUndo();
}
public Bitmap getBitmap(){
return mBitmap;
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mBitmap = Bitmap.createBitmap (width, height, Bitmap.Config.ARGB_8888);;
}
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
CommandManager.java
package com.pop.digitalsign;
import android.graphics.Canvas;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
public class CommandManager {
private List<DrawingPath> currentStack;
public CommandManager(){
currentStack = Collections.synchronizedList(new ArrayList<DrawingPath>());
}
public void addCommand(DrawingPath command){
currentStack.add(command);
}
public void undo (){
final int length = currentStackLength();
if ( length > 0) {
final DrawingPath undoCommand = currentStack.get( length - 1 );
currentStack.remove( length - 1 );
undoCommand.undo();
}
}
public int currentStackLength(){
final int length = currentStack.toArray().length;
return length;
}
public void executeAll( Canvas canvas){
if( currentStack != null ){
synchronized( currentStack ) {
final Iterator<?> i = currentStack.iterator();
while ( i.hasNext() ){
final DrawingPath drawingPath = (DrawingPath) i.next();
drawingPath.draw( canvas );
}
}
}
}
public boolean hasMoreUndo(){
return currentStack.toArray().length > 0;
}
}
DrawingPath.java
package com.pop.digitalsign;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
public class DrawingPath {
public Path path;
public Paint paint;
public void draw(Canvas canvas) {
canvas.drawPath( path, paint );
}
public void undo() {
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#android:color/white" >
<com.pop.digitalsign.DrawingSurface
android:id="#+id/drawingSurface"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center" >
<Button
android:id="#+id/saveBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="saveDrawing"
android:text="#string/save" />
<Button
android:id="#+id/clearBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clearScreen"
android:text="#string/clear" />
</LinearLayout>
</RelativeLayout>
the best suggestion i have is to use this
if (surfaceHolder.getSurface().isValid()) {
Canvas c = surfaceHolder.lockCanvas();
if (first >= 0) {
c.drawARGB(255, 255, 255, 255);
first--;
}
The value of first is 2 you can use what every name you want.
The reason is that the canvas is double buffered so you need to clear both screen by painting it white.
This stops the flickering
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
In DrawingSurface add this method
public void resetCanvas(){ commandManager.clearAllPath(); }
and call this method, when you need to action for clear..
objectNameofDrawingSurefaceClass.resetCanvas();
add the code in CommandManager
public void clearAllPath(){currentStack.clear(); }
ok..