I am developing an android application, in this one page after filling up the details, at the end of the page i need to place some imageview or fragement in which user can able to sign thier signature it by touching. so that i can save that signature as image while clicking submit.
i have googled about this but i did not get any tutorial kind of thing to start.
Please help me out from this or share some link if you guys know.
Thanks in Advance,
Srikanth A
this is my class file,
package com.example.test_signature;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable.Callback;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
public class SignatureView extends SurfaceView implements OnTouchListener, Callback, android.view.SurfaceHolder.Callback {
public SignatureView(Context context) {
super(context);
init();
}
public SignatureView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
this.setBackgroundColor(Color.WHITE);
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
this.setOnTouchListener(this);
this.getHolder().addCallback(this);
}
public void setStrokeWidth(float width){
mPaint.setStrokeWidth(width);
this.invalidate();
}
public void setColor(int color){
mPaint.setColor(color);
this.invalidate();
}
public void clear(){
mDots = new ArrayList<List<Dot>>();
//To prevent an exception
mDots.add(new ArrayList<Dot>());
this.invalidate();
}
private List<List<Dot>> mDots = new ArrayList<List<Dot>>();
private class Dot{
public float X = 0;
public float Y = 0;
public Dot(float x, float y){
X = x;
Y = y;
}
}
#Override
public boolean onTouch(View view, MotionEvent event) {
//mLastActivity = Calendar.getInstance();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
mDots.add(new ArrayList<Dot>());
mDots.get(mDots.size() - 1).add(new Dot(event.getX(), event.getY()));
this.invalidate();
break;
case MotionEvent.ACTION_UP:
mDots.get(mDots.size() - 1).add(new Dot(event.getX(), event.getY()));
this.invalidate();
break;
case MotionEvent.ACTION_MOVE:
mDots.get(mDots.size() - 1).add(new Dot(event.getX(), event.getY()));
this.invalidate();
break;
}
return true;
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
public void surfaceCreated(SurfaceHolder arg0) {
}
public void surfaceDestroyed(SurfaceHolder arg0) {
}
public Paint mPaint;
#Override
protected void onDraw(Canvas canvas) {
for(List<Dot> dots : mDots){
for(int i = 0; i < dots.size(); i++){
if(i - 1 == -1)
continue;
canvas.drawLine(dots.get(i - 1).X, dots.get(i - 1).Y, dots.get(i).X, dots.get(i).Y, mPaint);
}
}
}
public Bitmap getBitmap(){
Bitmap b = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
this.draw(c);
return b;
}
public boolean exportFile(String pathString, String fileString){
File path = new File(pathString);
path.mkdirs();
if(!fileString.toLowerCase(Locale.ENGLISH).contains(".png")){
fileString += ".png";
}
File file = new File(path, fileString);
FileOutputStream out;
try {
out = new FileOutputStream(file);
this.getBitmap().compress(Bitmap.CompressFormat.PNG, 90, out);
return true;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
}
this is My XML file in which i have created the view for the user to put a signature.
<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=".SignatureView" >
<View
android:id="#+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.example.test_signature.SignatureView
android:id="#+id/signature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:height="18dp"
android:width="18dp" />
</RelativeLayout>
i have created this sample to test the signature functionality. But whenever i run this code, my application get crashes. Anyone please help me out. I think i have done a mistake in my XML.
#FabianCook : pla look out the code i have pasted. I'hv followed your procedure oly. but can u pls tel me wats the problem in the code ?
I have edited my XML file as you suggested, but still my application getting crashes whenever i open.
<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=".SignatureView" >
<View
android:id="#+id/id"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.example.test_signature.SignatureView
android:id="#+id/signature"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
public class SignatureView extends SurfaceView implements OnTouchListener, Callback {
public SignatureView(Context context) {
super(context);
init();
}
public SignatureView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
this.setBackgroundColor(Color.WHITE);
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
this.setOnTouchListener(this);
this.getHolder().addCallback(this);
}
public void setStrokeWidth(float width){
mPaint.setStrokeWidth(width);
this.invalidate();
}
public void setColor(int color){
mPaint.setColor(color);
this.invalidate();
}
public void clear(){
mDots = new ArrayList<List<Dot>>();
//To prevent an exception
mDots.add(new ArrayList<Dot>());
this.invalidate();
}
private List<List<Dot>> mDots = new ArrayList<List<Dot>>();
private class Dot{
public float X = 0;
public float Y = 0;
public Dot(float x, float y){
X = x;
Y = y;
}
}
#Override
public boolean onTouch(View view, MotionEvent event) {
//mLastActivity = Calendar.getInstance();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
mDots.add(new ArrayList<Dot>());
mDots.get(mDots.size() - 1).add(new Dot(event.getX(), event.getY()));
this.invalidate();
break;
case MotionEvent.ACTION_UP:
mDots.get(mDots.size() - 1).add(new Dot(event.getX(), event.getY()));
this.invalidate();
break;
case MotionEvent.ACTION_MOVE:
mDots.get(mDots.size() - 1).add(new Dot(event.getX(), event.getY()));
this.invalidate();
break;
}
return true;
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
}
public Paint mPaint;
#Override
protected void onDraw(Canvas canvas) {
for(List<Dot> dots : mDots){
for(int i = 0; i < dots.size(); i++){
if(i - 1 == -1)
continue;
canvas.drawLine(dots.get(i - 1).X, dots.get(i - 1).Y, dots.get(i).X, dots.get(i).Y, mPaint);
}
}
}
public Bitmap getBitmap(){
Bitmap b = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
this.draw(c);
return b;
}
public boolean exportFile(String pathString, String fileString){
File path = new File(pathString);
path.mkdirs();
if(!fileString.toLowerCase(Locale.ENGLISH).contains(".png")){
fileString += ".png";
}
File file = new File(path, fileString);
FileOutputStream out;
try {
out = new FileOutputStream(file);
this.getBitmap().compress(Bitmap.CompressFormat.PNG, 90, out);
return true;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
}
I think the code explains its self
This code takes care of everything. Has some nice little functions for different things like saving the image to a file etc.
Place this in one of your packages, and then in your XML file replace
<View android:id="#+id/id"/>
with
<package.name.SignatureView android:id="#+id/id"/>
USAGE
Create a new class in your package called SignatureView, delete everything in this file except for the first line, you should be left with package your.package.name
On the third line paste the code above and press Ctrl-Shift-O and it will import everything you need.
Save this file and then build your project.
In your XML file use:
<your.package.name.SignatureView
android:id="#+id/signature"
android:width="match_parent"
android:height="match_parent"/>
Save this and build your project.
In your activity you can use:
SignatureView mSignature;
mSignature = (SignatureView) this.findViewById(R.id.signature);
And then access the functions you need to save the signature to a file:
mSignature.exportFile("/mnt/sdcard/", "signaturefile.png");
hope this code helps you:)
esign_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<android.gesture.GestureOverlayView
android:id="#+id/signaturePad"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:background="#android:color/white"
android:clickable="false"
android:eventsInterceptionEnabled="true"
android:fadeEnabled="false"
android:gestureColor="#0000ff"
android:gestureStrokeLengthThreshold="0.1"
android:gestureStrokeType="multiple"
android:longClickable="false"
android:orientation="vertical"
android:uncertainGestureColor="#000000"
android:splitMotionEvents="true"
android:fadeOffset="10000000">
</android.gesture.GestureOverlayView>
<RelativeLayout
android:id="#+id/rellay_esign_donebutton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
>
<Button
android:id="#+id/DoneButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Done" />
<Button
android:id="#+id/ClearButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clear" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
Esignature.java
public class Esignature extends Activity {
GestureOverlayView gestureView;
String path;
File file;
Bitmap bitmap;
public boolean gestureTouch=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.esign_main);
Button donebutton = (Button) findViewById(R.id.DoneButton);
donebutton.setText("Done");
Button clearButton = (Button) findViewById(R.id.ClearButton);
clearButton.setText("Clear");
path=Environment.getExternalStorageDirectory()+"/signature.png";
file = new File(path);
file.delete();
gestureView = (GestureOverlayView) findViewById(R.id.signaturePad);
gestureView.setDrawingCacheEnabled(true);
gestureView.setAlwaysDrawnWithCacheEnabled(true);
gestureView.setHapticFeedbackEnabled(false);
gestureView.cancelLongPress();
gestureView.cancelClearAnimation();
gestureView.addOnGestureListener(new OnGestureListener() {
#Override
public void onGesture(GestureOverlayView arg0, MotionEvent arg1) {
// TODO Auto-generated method stub
}
#Override
public void onGestureCancelled(GestureOverlayView arg0,
MotionEvent arg1) {
// TODO Auto-generated method stub
}
#Override
public void onGestureEnded(GestureOverlayView arg0, MotionEvent arg1) {
// TODO Auto-generated method stub
}
#Override
public void onGestureStarted(GestureOverlayView arg0,
MotionEvent arg1) {
// TODO Auto-generated method stub
if (arg1.getAction()==MotionEvent.ACTION_MOVE){
gestureTouch=false;
}
else
{
gestureTouch=true;
}
}});
donebutton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
bitmap = Bitmap.createBitmap(gestureView.getDrawingCache());
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
fos = new FileOutputStream(file);
// compress to specified format (PNG), quality - which is
// ignored for PNG, and out stream
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
if(gestureTouch==false)
{
setResult(0);
finish();
}
else
{
setResult(1);
finish();
}
}
});
clearButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
gestureView.invalidate();
gestureView.clear(true);
gestureView.clearAnimation();
gestureView.cancelClearAnimation();
}
});
}
}
Related
I have a class called examplecanvas.java that extends ImageView. The class is called by a custom view in an XML. In that XML there is a button for which I want to set an OnClick in examplecanvas.java.
I have put the OnClickListener in the OnTouchEvent, but it does not work. Here is my code:
examplecanvas.java (extending ImageView):
public class examplecanvas extends ImageView {
float xPos, yPos;
private PointF point;
public examplecanvas(Context context) {
super(context);
}
public examplecanvas(Context context, AttributeSet attrs) {
super(context, attrs);
}
public examplecanvas(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
Button savepoints = (Button) findViewById(R.id.btnpdf );
savepoints public void onClick(View ImageView) {
Toast.makeText(getContext(), "points saved", Toast.LENGTH_SHORT).show();
}
});
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
point = new PointF(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
point.set(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
point.set(x, y);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
point.set(x, y);
invalidate();
break;
}
return true;
}
#Override
protected void onDraw(#NonNull Canvas canvas) {
super.onDraw(canvas);
float flat = (float)doublelat;
float flon = (float)doublelon;
canvas.drawCircle(flat, flon, 100, paint);
if (point != null) {
canvas.drawCircle(point.x, point.y, 100, paint);
canvas.save();
}
}
}
The XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.joanzapata.pdfview.PDFView
android:id="#+id/pdfView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<view
class="com.myapp.trial10.examplecanvas"
android:layout_gravity="center"
android:id="#+id/yourID2"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/mybutton"
android:text="save"
android:id="#+id/btnpdf"
android:layout_gravity="center_horizontal|bottom" />
</FrameLayout>
The Activity:
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.widget.Button;
import android.view.View;
import com.joanzapata.pdfview.PDFView;
public class pdfview3 extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pdfview3);
String assetFileName = "proposed.pdf"; //The name of the asset to open
int pageNumber = 0; //Start at the first page
PDFView pdfView = (PDFView) findViewById(R.id.pdfView); //Fetch the view
pdfView.fromAsset(assetFileName) //Fill it with data
.defaultPage(pageNumber) //Set default page number
.onPageChange(null) //PageChangeListener
.enableSwipe(false)
.load();
}
}
So you want to add onClickListener for the ImageView?
-add setOnClickListener(this); in on create
-make class implement OnClickListener
-implement onClick method
-rename view in xml with View(or put directly package.examplecanvas instead of view and remove class=...)
I am new to Android so I am making a coloring book app just to get myself acquainted to android programming. I have looked up my problem extensively and implemented the solutions but still no progress.
I have an activity 'ColoringActivity' which calls a class 'PaintView' which extends surfaceview. I am trying to update the canvas in a separate thread. I also have a button in the layout which takes the user to another activity for picking colors. The problem is that when the user returns after choosing the color, the canvas becomes empty and I cant draw on the canvas anymore. I think i somehow loose the thread in between activities and although the thread is running in the background, I have no access to it.
I read on this forum that I must implement pause() and resume() methods in the thread class and basically kill the thread when I go to another activity and restart it when I return. Also I read I have to override onPause() and onResume() method in activity class and construct the surfaceview in onResume() so that it is constructed every time user returns to this activity.
I am sorry if it doesnt make much sense because I am lost as well.
My 'ColoringActivity':
package com.ali.coloryourself;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class ColoringActivity extends Activity {
private static final int COLOR_REQUEST_CODE = 100;
public static String file;
public static Bitmap bitmap;
BitmapFactory.Options options;
PaintView paintView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_draw);
Intent intent = getIntent();
file = intent.getStringExtra("fileName");
// paintView = (PaintView) findViewById(R.id.drawingSurface);
}
#Override
protected void onResume() {
paintView = (PaintView) findViewById(R.id.drawingSurface);
paintView.getThread().resume();
super.onResume();
}
#Override
protected void onPause() {
paintView.getThread().pause();
super.onPause();
}
public void pickColor(View v) {
paintView.getThread().pause();
Intent colorIntent = new Intent(this, ColorPickerActivity.class);
startActivityForResult(colorIntent, COLOR_REQUEST_CODE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_CANCELED) {
if (requestCode == COLOR_REQUEST_CODE) {
int color = data.getIntExtra("Color", -1);
// paintView.getPaint().setColor(color);
}
}
}
}
My 'PaintView' class:
package com.ali.coloryourself;
import android.R.color;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
class PaintView extends SurfaceView implements SurfaceHolder.Callback {
private Paint paint = new Paint();
private Canvas canvas;
private PaintThread thread;
private Path path = new Path();
private Bitmap bitmap;
public PaintView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(3);
setThread(new PaintThread(holder));
}
class PaintThread extends Thread {
private boolean mRun;
private SurfaceHolder mSurfaceHolder;
private int mMode;
public static final int STATE_PAUSE = 2;
public static final int STATE_RUNNING = 4;
public PaintThread(SurfaceHolder surfaceHolder) {
mSurfaceHolder = surfaceHolder;
}
#Override
public void run() {
while (mRun) {
try {
canvas = mSurfaceHolder.lockCanvas(null);
if (mMode == STATE_RUNNING) {
if (bitmap == null) {
bitmap = Bitmap.createBitmap(1, 1,
Bitmap.Config.ARGB_8888);
}
}
doDraw(canvas);
canvas.drawBitmap(bitmap, 0, 0, null);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
private void doDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
public void setRunning(boolean b) {
mRun = b;
}
public void pause() {
if (mMode == STATE_RUNNING)
setState(STATE_PAUSE);
}
public void resume() {
setState(STATE_RUNNING);
}
public void setState(int mode) {
synchronized (mSurfaceHolder) {
mMode = mode;
}
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// Log.d("Touch", "I am touching");
float eventX = event.getX();
float eventY = event.getY();
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
// nothing to do
break;
default:
return false;
}
return true;
}
public void surfaceCreated(SurfaceHolder holder) {
if (getThread().getState() == Thread.State.NEW) {
getThread().setRunning(true);
getThread().start();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
getThread().setRunning(false);
getThread().resume();
while (retry) {
try {
getThread().join();
retry = false;
} catch (InterruptedException e) {
}
}
}
public Paint getPaint() {
return paint;
}
public void setPaint(int color) {
this.paint.setColor(color);
}
public PaintThread getThread() {
return thread;
}
public void setThread(PaintThread thread) {
this.thread = thread;
}
}
my 'activity_draw.xml'
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:onClick="pickColor"
android:text="Pick Color" />
<com.ali.coloryourself.PaintView
android:id="#+id/drawingSurface"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/button1"
android:layout_marginTop="10dp"/>
</RelativeLayout>
I know I am missing some very basic thread concept. I need to allow the user to pick color and return and be able to continue drawing. I will be extremely grateful for your help.
I think I have found an answer although I am not sure if it is a good programming practice. I found out that my surface view and thread were created once 'ColoringActivity' was created and destroyed every time 'ColoringActivity' went to background. But once 'ColoringActivity' was restarted and resumed, surface view and thread were not recreated. So i moved the following line
setContentView(R.layout.activity_draw);
to onResume() method and now I can draw on the canvas every time. Now I just need to save the canvas and re load it when the activity comes back on to start off the coloring where the user left it.
I am trying to draw based on my trace over the screen. In this process my onTouch and OnDraw is not being executed. Could you please assist me to correct this code ?
Xml
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="100"
android:orientation="vertical"
android:background="#ffff0000" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="60"
android:gravity="center" >
<com.example.test2.PathDraw
android:id="#+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="140dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="40"
android:gravity="center" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/lion" />
</LinearLayout>
MainActivity
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PathDraw sineWaveSurfaceView = (PathDraw) findViewById(R.id.surfaceView);
}
}
PathDraw
public class PathDraw extends SurfaceView implements SurfaceHolder.Callback
{
private Context context;
// List<Point> points;
ArrayList<Point> points = new ArrayList<Point>();
public PathDraw(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public PathDraw(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
this.context = context;
getHolder().addCallback(this);
}
public PathDraw(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
protected void OnDraw(Canvas canvas, int value) {
Paint paint = new Paint();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
Path path = new Path();
boolean first = true;
for(Point point : points){
if(first){
first = false;
path.moveTo(point.x, point.y);
}
else{
path.lineTo(point.x, point.y);
}
}
canvas.drawPath(path, paint);
}
public boolean onTouch(View view, MotionEvent event) {
// if(event.getAction() != MotionEvent.ACTION_DOWN)
// return super.onTouchEvent(event);
Point point = new Point();
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
Log.d("PathDraw", "point: " + point);
return true;
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
}
OnTouch is not called
I also had the exact same problem earlier today.
Class PathDraw should implement OnTouchListener, then set the listener to itself, do something like this:
public class PathDraw extends SurfaceView implements OnTouchListener, SurfaceHolder.Callback{
...
public PathDraw(Context context){
super(context);
setOnTouchListener(PathDraw.this);
}
OnDraw is not called
This post might help:
Android Custom Layout - onDraw() never gets called
This is my xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout0"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CanvasActivity"
android:baselineAligned="false">
<LinearLayout
android:id="#+id/LinearLayout1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#f9dfcb">
</LinearLayout>
<LinearLayout
android:id="#+id/LinearLayout2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="3"
android:background="#000000"
>
</LinearLayout>
</LinearLayout>
My activity code
import android.app.Activity;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.LinearLayout;
import android.widget.Toast;
public class CanvasActivity extends Activity {
private GestureDetector gestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_canvas);
gestureDetector = new GestureDetector(this, new GestureListener());
LinearLayout LinearLayout1=(LinearLayout)findViewById(R.id.LinearLayout1);
LinearLayout1.setOnTouchListener(new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event){
if (gestureDetector.onTouchEvent(event))
{
return true;
}
return false;
}
});
/*********EDITED*******/
LinearLayout LinearLayout2=(LinearLayout)findViewById(R.id.LinearLayout2);
int width=LinearLayout2.getWidth();
int height=LinearLayout2.getHeight();
LinearLayout2.addView(new Canvasview(this), width, height) ;
LinearLayout2.setOnTouchListener(new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event){
Canvasview cv= new Canvasview(v.getContext());
cv.onTouchEvent(event);
return true;
}
});
}
//discerning swipes
class GestureListener extends SimpleOnGestureListener
{
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY)
{
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE &&
Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
//From Right to Left
Toast.makeText(CanvasActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show();
return true;
}
else if(e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE &&
Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
//From Bottom to Top
Toast.makeText(CanvasActivity.this, "Top Swipe", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
#Override
public boolean onDown(MotionEvent e)
{
return true;
}
}
}
My Canvasview looks like this
public class Canvasview extends SurfaceView implements SurfaceHolder.Callback{
private int width, height;
private Paint textPaint = new Paint();
private Paint touchPaint = new Paint();
private int colors[] = new int[10];
public Canvasview(Context context) {
super(context);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
setFocusable(true); // make sure we get key events
setFocusableInTouchMode(true); // make sure we get touch events
init();
}
private void init()
{
textPaint.setColor(Color.WHITE);
colors[0] = Color.RED;
touchPaint = new Paint();
touchPaint.setColor(colors[0]);
}
public boolean onTouchEvent(MotionEvent event)
{
Canvas c = getHolder().lockCanvas();
if (c != null)
{
if (event.getAction() == MotionEvent.ACTION_UP)
{
// clear everything
}
else
{
int xval = (int)event.getX();
int yval = (int)event.getY();
drawCircle(xval,yval,touchPaint,c);
}
getHolder().unlockCanvasAndPost(c);
}
return true;
}
public void clear(Canvas c){
c = getHolder().lockCanvas();
c.drawColor(Color.BLACK);
getHolder().unlockCanvasAndPost(c);
}
private void drawCircle(int x, int y, Paint paint, Canvas c)
{
c.drawCircle(x, y, 2, paint);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){
Canvas c = getHolder().lockCanvas();
if (c != null)
{
// clear screen
c.drawColor(Color.BLACK);
getHolder().unlockCanvasAndPost(c);
}
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
}
I need the canvas only in linearlayout2, but the canvas occupies the entire screen. I am not sure where I am going wrong.
EDIT:
Well I did few changes and I get the Canvasview in linearlayout2, the ontouchevents of Canvasview detects the touch events but in Canvasview public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) method does not initialize a canvas and therefore it is always null. To draw I need the canvas not to be null. Any help is appreciated
set hardcode value of layout_width for first linear layout and try again
The first problem is that you want to get height and width of a view, that hasn't been drawn yet in onCreate():
int width=LinearLayout2.getWidth();
int height=LinearLayout2.getHeight();
A possible solution for that is to set static values or use a ViewTreeObserver (https://stackoverflow.com/search?q=get+measured+width+height+android).
The other problem you might have is that linear layout doesn't call onTouch() method. You may try using a scrolling view(scrollview for example) instead of linear layout.
I changed my xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout0"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".CanvasActivity"
android:baselineAligned="false">
<View
android:id="#+id/view0"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="3"
android:background="#f9dfcb"
/>
<com.multitel.testwidget.Canvasview
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/surfaceView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</com.multitel.testwidget.Canvasview>
</LinearLayout>
and changed the constructor in Canvasview to
public Canvasview(Context context, AttributeSet attrs)
{
super(context, attrs);
// TODO Auto-generated constructor stub
SurfaceHolder holder = getHolder();
holder.addCallback(this);
setFocusable(true); // make sure we get key events
setFocusableInTouchMode(true); // make sure we get touch events
init();
holder.getSurface();
}
And i could draw on the canvas!!
I have a TextView in ScrollView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ScrollView
android:id="#+id/textAreaScroller"
android:layout_width="400px"
android:layout_height="200px"
android:layout_x="0px"
android:layout_y="25px"
android:fadeScrollbars="false"
android:scrollbarSize="3px"
android:scrollbarStyle="insideOverlay" >
<TextView
android:id="#+id/scrapbook"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="" />
</ScrollView>
<Button
android:id="#+id/upBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Up" />
<Button
android:id="#+id/downBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Down" />
<ImageView
android:id="#+id/imageView"
android:layout_width="400px"
android:layout_height="200px"
/>
</LinearLayout>
TextView is has a lot of text that's why is scrollable. I need to draw the current visible content in TextView to Bitmap. For testing purposes I display this bitmap in ImageView. I have the following code:
public class TextviewToImageActivity extends Activity {
private TextView textView;
private ScrollView textAreaScroller;
private ImageView imageView;
private Handler mHandler;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mHandler = new Handler();
imageView = (ImageView) findViewById(R.id.imageView);
textAreaScroller = (ScrollView) findViewById(R.id.textAreaScroller);
textView = (TextView) findViewById(R.id.scrapbook);
textView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
imageView.setImageBitmap(loadBitmapFromView(textAreaScroller));
return false;
}
});
Button upBtn = (Button) findViewById(R.id.upBtn);
Button downBtn = (Button) findViewById(R.id.downBtn);
upBtn.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
scheduleScroller(upScroller);
imageView.setImageBitmap(loadBitmapFromView(textView));
} else if (event.getAction() == MotionEvent.ACTION_UP) {
mHandler.removeMessages(1);
}
return true;
}
});
downBtn.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
scheduleScroller(downScroller);
imageView.setImageBitmap(loadBitmapFromView(textView));
} else if (event.getAction() == MotionEvent.ACTION_UP) {
mHandler.removeMessages(1);
}
return true;
}
});
loadDoc();
}
private Runnable downScroller = new Runnable() {
public void run() {
textAreaScroller.scrollBy(0, 10);
scheduleScroller(downScroller);
}
};
private Runnable upScroller = new Runnable() {
public void run() {
textAreaScroller.scrollBy(0, -10);
scheduleScroller(upScroller);
}
};
private void scheduleScroller(Runnable scrollerJob) {
Message msg = Message.obtain(mHandler, scrollerJob);
msg.what = 1;
mHandler.sendMessageDelayed(msg, 10);
}
private static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap(400, 200, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.draw(c);
return b;
}
private void loadDoc() {
String s = "";
for (int x = 0; x <= 100; x++) {
s += "Line: " + String.valueOf(x) + "\n";
}
textView.setText(s);
textView.setMovementMethod(new ScrollingMovementMethod());
}
}
The problem is that once I scroll TextView (trigger TouchEvent) the Bitmap doesn't reflect the current content of TextView and instead always has only the beginning content of TextView (it doesn't matter what's the current scroll position). I updated post to provide working code - maybe it will work on sb's other device.
UPDATE
I also tried to check WarrenFaith idea by overriding onDraw in my custom TextView but it somehow still only draw the begining content of TextView:
public class MyTextView extends TextView {
private Bitmap mBitmap;
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public Bitmap getBitmap() {
return mBitmap;
}
#Override
protected void onDraw(Canvas canvas) {
mBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight()
, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(mBitmap);
super.onDraw(canvas);
super.onDraw(c);
}
}
Try to override the onDraw() method of the TextView should work. There you can create a bitmap based on the canvas parameter. Details can be found in my tutorial: How to create and save a screenshot from a SurfaceView
Update:
I fixed your issue:
The activity (I changed the Handler usage and removed some methods. Basically I shrinked the code a bit).
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.text.method.ScrollingMovementMethod;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
/**
* #author WarrenFaith
*/
public class TextToImageActivity extends Activity {
private MyTextView textView;
private ImageView imageView;
private boolean mRepeatDrawing = false;
private Handler mHandler;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.textview);
mHandler = new Handler();
imageView = (ImageView) findViewById(R.id.imageView);
textView = (MyTextView) findViewById(R.id.scrapbook);
Button upBtn = (Button) findViewById(R.id.upBtn);
Button downBtn = (Button) findViewById(R.id.downBtn);
upBtn.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mRepeatDrawing = true;
mHandler.post(upScroller);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
mRepeatDrawing = false;
}
return false;
}
});
downBtn.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mRepeatDrawing = true;
mHandler.post(downScroller);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
mRepeatDrawing = false;
}
return false;
}
});
loadDoc();
}
private Runnable downScroller = new Runnable() {
public void run() {
textView.scrollBy(0, 10);
imageView.setImageBitmap(textView.getBitmap());
if (mRepeatDrawing) {
mHandler.postDelayed(this, 10);
}
}
};
private Runnable upScroller = new Runnable() {
public void run() {
textView.scrollBy(0, -10);
imageView.setImageBitmap(textView.getBitmap());
if (mRepeatDrawing) {
mHandler.postDelayed(this, 10);
}
}
};
private void loadDoc() {
String s = "";
for (int x = 0; x <= 100; x++) {
s += "Line: " + String.valueOf(x) + "\n";
}
textView.setText(s);
textView.setMovementMethod(new ScrollingMovementMethod());
}
}
The custom textview: Important: the trick was to get the scrolling position!
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* #author WarrenFaith
*/
public class MyTextView extends TextView {
private Bitmap mBitmap;
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public Bitmap getBitmap() {
return mBitmap;
}
#Override
protected void onDraw(Canvas canvas) {
mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(mBitmap);
c.translate(0, -getScrollY());
super.onDraw(c);
super.onDraw(canvas);
}
}
The xml: (I removed the ScrollView and let the TextView handle the scrolling)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.testproject.MyTextView
android:id="#+id/scrapbook"
android:layout_width="400px"
android:layout_height="200px"
android:scrollbars="vertical"
android:scrollbarSize="3px"
android:text=""
android:background="#0000ff" />
<Button
android:id="#+id/upBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Up" />
<Button
android:id="#+id/downBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Down" />
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>