我正在使用自定义视图在Android应用中实现数字签名功能 我希望用户能够在不关闭对话框或重新创建活动的情况下清除并重试新签名。当我清除时,它会成功清除签名,但不允许绘制新签名并绘制一些黑色叠加层以及先前绘制的签名。
这是绘制数字签名的类 Signature.java
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.graphics.PorterDuff;
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.io.File;
import java.io.FileOutputStream;
public class Signature extends View
{
public static final float STROCK_WIDTH = 5f;
public static final float HALF_STROKE_WIDTH = STROCK_WIDTH / 2;
private Paint paint = new Paint();
View mContent;
File mPath1;
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
boolean clear = false;
public Signature(Context context)
{
super(context);
}
public Signature(Context context, AttributeSet attrs)
{
super(context, attrs);
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(STROCK_WIDTH);
}
public Signature(Context context, AttributeSet attrs, View view, File pathToSave)
{
this(context, attrs);
mContent = view;
mPath1 = pathToSave;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
setPaint();
}
public void setPaint()
{
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFF0000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
public String save(View v)
{
String imagePath = null;
Log.v("TAG", "Width :" + v.getWidth());
Log.v("TAG", "Height :" + v.getHeight());
if (mBitmap == null)
{
mBitmap = Bitmap.createBitmap(mContent.getWidth(), mContent.getHeight(), Bitmap.Config.RGB_565);
}
mBitmap = v.getDrawingCache();
try
{
FileOutputStream mFileOutStream = new FileOutputStream(mPath1);
mBitmap.compress(Bitmap.CompressFormat.PNG, 90, mFileOutStream);
mFileOutStream.flush();
mFileOutStream.close();
imagePath = MediaStore.Images.Media.insertImage(getContext().getContentResolver(), mBitmap, "title", null);
Log.v("log_tag", "url: " + imagePath + "::we are saving at :" + mPath1);
}
catch (Exception e)
{
Log.v("log_tag", e.toString());
}
return imagePath;
}
public void clear()
{
mPath.reset();
mPaint.reset();
clear = true;
mPath1.delete();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
postInvalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
try
{
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
catch (Exception e)
{
e.printStackTrace();
}
}
@Override
protected void onDraw(Canvas canvas)
{
if (clear)
{
clear = false;
}
else
{
canvas.drawColor(0xFFFFFFFF);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y)
{
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y)
{
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
{
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up()
{
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
float x = event.getX();
float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
使用上述类的活动
MainActivity.java
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.ContextWrapper;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btnOpen;
String imagePath = null,str_signature = "";
ImageView iv_sign;
public static String tempDir;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
btnOpen= (Button) findViewById(R.id.btnOpen);
btnOpen.setOnClickListener(this);
iv_sign= (ImageView) findViewById(R.id.ivSign);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onClick(View v) {
if(v==btnOpen)
{
SignatureFragment dialog = new SignatureFragment(onImageClicked);
dialog.show(MainActivity.this.getSupportFragmentManager(), "NoticeDialogFragment");
}
}
ImageSaved onImageClicked = new ImageSaved() {
@Override
public void onImageSaved(String path) {
imagePath = path;
Uri uri = Uri.parse(imagePath);
Log.d("TAG", "We got image path :" + imagePath);
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
iv_sign.setImageBitmap(bitmap);
ImageView img = iv_sign;
BitmapDrawable mBitmapDrawable = (BitmapDrawable) img.getDrawable();
Bitmap mBitmap = mBitmapDrawable.getBitmap();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] byteArrayImage = baos.toByteArray();
str_signature = Base64.encodeToString(byteArrayImage,
Base64.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
}
};
public static class SignatureFragment extends android.support.v4.app.DialogFragment implements View.OnClickListener {
LinearLayout ll_signature_view;
Signature signature;
Button btn_done, btn_clear,btnCancel;
String EXTERNAL_DIR = "TTD";
ImageSaved saveListner;
File mypath;
@SuppressLint("ValidFragment")
public SignatureFragment(ImageSaved onImageClicked) {
saveListner = onImageClicked;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
@Override
public void onDetach() {
saveListner = null;
super.onDetach();
}
public SignatureFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(android.support.v4.app.DialogFragment.STYLE_NO_FRAME, R.style.Base_Theme_AppCompat_Light_Dialog);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.signature_view, container, false);
try
{
ll_signature_view = (LinearLayout) rootView.findViewById(R.id.ll_signature_view);
btn_done = (Button) rootView.findViewById(R.id.btn_done);
btn_clear = (Button) rootView.findViewById(R.id.btn_clear);
btnCancel = (Button) rootView.findViewById(R.id.btn_cancel);
tempDir = Environment.getExternalStorageDirectory() + "/" + EXTERNAL_DIR + "/";
File directory = getActivity().getCacheDir();
prepareDirectory();
String uniqueId = getTodaysDate() + "_" + getCurrentTime() + "_" + Math.random();
String current = uniqueId + ".png";
mypath = new File(directory, current);
signature = new Signature(getActivity(), null, ll_signature_view, mypath);
ll_signature_view.setBackgroundColor(Color.WHITE);
ll_signature_view.addView(signature, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
btn_done.setOnClickListener(this);
btn_clear.setOnClickListener(this);
btnCancel.setOnClickListener(this);
}
catch(Exception ex)
{
ex.printStackTrace();
}
return rootView;
}
@Override
public void onClick(View v) {
if (v == btn_done) {
ll_signature_view.setDrawingCacheEnabled(true);
String imagePath = signature.save(ll_signature_view);
if (saveListner != null) {
saveListner.onImageSaved(imagePath);
}
dismiss();
}
if (v == btnCancel) {
dismiss();
}
if(v==btn_clear)
{
signature.clear();
}
}
private boolean prepareDirectory() {
try {
if (makedirs()) {
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(getActivity(), "Could not initiate File System.. Is Sdcard mounted properly?", Toast.LENGTH_SHORT).show();
return false;
}
}
private boolean makedirs() {
File tempdir = new File(tempDir);
if (!tempdir.exists()) {
tempdir.mkdirs();
}
if (tempdir.isDirectory()) {
File[] files = tempdir.listFiles();
for (File file : files) {
if (!file.delete()) {
System.out.println("Failed to delete " + file);
}
}
}
return (tempdir.isDirectory());
}
private String getTodaysDate() {
final Calendar c = Calendar.getInstance();
int todaysDate = (c.get(Calendar.YEAR) * 10000) +
((c.get(Calendar.MONTH) + 1) * 100) +
(c.get(Calendar.DAY_OF_MONTH));
Log.w("DATE:", String.valueOf(todaysDate));
return (String.valueOf(todaysDate));
}
private String getCurrentTime() {
final Calendar c = Calendar.getInstance();
int currentTime = (c.get(Calendar.HOUR_OF_DAY) * 10000) +
(c.get(Calendar.MINUTE) * 100) +
(c.get(Calendar.SECOND));
Log.w("TIME:", String.valueOf(currentTime));
return (String.valueOf(currentTime));
}
}
}
布局文件
signature_view.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:layout_margin="8dp">
<LinearLayout
android:id="@+id/ll_signature_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@drawable/edittext_border_white"
android:orientation="horizontal">
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp">
<Button
android:id="@+id/btn_done"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="DONE" />
<Button
android:id="@+id/btn_clear"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="CLEAR" />
<Button
android:id="@+id/btn_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="CANCEL" />
</LinearLayout>
</LinearLayout>
activity_main.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:padding="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:background="@android:color/white">
<TextView
android:id="@+id/tvWelcome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/Welcome" />
<Button
android:layout_marginTop="10dp"
android:id="@+id/btnOpen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/Open"
android:gravity="center"
android:layout_below="@+id/tvWelcome"/>
<ImageView
android:id="@+id/ivSign"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:layout_below="@+id/btnOpen"
/>
</RelativeLayout>
答案 0 :(得分:0)
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
试试这个
canvas.drawColor(0xFFFFFFFF);
并设置图层类型
mCanvas.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
答案 1 :(得分:0)
在这里回答我自己的问题.. :)
根据我的要求,我已经关闭并重新打开一个解决了我的问题的对话框。
在MainActivity.java中
if(v==btn_clear)
{
dismiss();
SignatureFragment dialog = new SignatureFragment(saveListner);
dialog.show(getActivity().getSupportFragmentManager(), "NoticeDialogFragment");
}
答案 2 :(得分:0)
使用图书馆: 'com.github.gcacace:签名垫:1.2.1'
在此,您可以使用SignaturePad类的clear()方法。使用清除所有输入的数据将被删除,您可以添加新内容。