I am trying to implement an image view which displays the signature I draw in the SignatureActivity class. I am receiving an error on line:
bitmap.compress(CompressFormat.PNG, 90, mFileOutStream);
public class SignatureActivity extends AppCompatActivity {
Button mClear, mGetSign, mCancel;
File file;
LinearLayout mContent;
View view;
signature mSignature;
Bitmap bitmap;
// Creating Separate Directory for saving Generated Images
String DIRECTORY = Environment.getExternalStorageDirectory().getPath() + "/UserSignature/";
String pic_name = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String StoredPath = DIRECTORY + pic_name + ".png";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signature);
mContent = (LinearLayout) findViewById(R.id.canvasLayout);
mSignature = new signature(getApplicationContext(), null);
mSignature.setBackgroundColor(Color.WHITE);
// Dynamically generating Layout through java code
mContent.addView(mSignature, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mClear = (Button) findViewById(R.id.clear);
mGetSign = (Button) findViewById(R.id.getsign);
mGetSign.setEnabled(false);
mCancel = (Button) findViewById(R.id.cancel);
view = mContent;
mGetSign.setOnClickListener(onButtonClick);
mClear.setOnClickListener(onButtonClick);
mCancel.setOnClickListener(onButtonClick);
// Method to create Directory, if the Directory doesn't exists
file = new File(DIRECTORY);
if (!file.exists()) {
file.mkdir();
}
}
Button.OnClickListener onButtonClick = new Button.OnClickListener() {
@Override
public void onClick(View v) {
if (v == mClear) {
Log.v("log_tag", "Panel Cleared");
mSignature.clear();
mGetSign.setEnabled(false);
} else if (v == mGetSign) {
Log.v("log_tag", "Panel Saved");
if (Build.VERSION.SDK_INT >= 23) {
isStoragePermissionGranted();
} else {
view.setDrawingCacheEnabled(true);
mSignature.save(view, StoredPath);
Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();
// Calling the same class
recreate();
}
} else if(v == mCancel){
Log.v("log_tag", "Panel Canceled");
// Calling the BillDetailsActivity
Intent intent = new Intent(SignatureActivity.this, MainActivity.class);
startActivity(intent);
}
}
};
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (getApplicationContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
} else {
return true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
view.setDrawingCacheEnabled(true);
mSignature.save(view, StoredPath);
Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();
// Calling the same class
recreate();
}
else
{
Toast.makeText(this, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
}
}
public class signature extends View {
private static final float STROKE_WIDTH = 5f;
private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
private Paint paint = new Paint();
private Path path = new Path();
private float lastTouchX;
private float lastTouchY;
private final RectF dirtyRect = new RectF();
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(STROKE_WIDTH);
}
public void save(View v, String StoredPath) {
Log.v("log_tag", "Width: " + v.getWidth());
Log.v("log_tag", "Height: " + v.getHeight());
if (bitmap == null) {
bitmap = Bitmap.createBitmap(mContent.getWidth(), mContent.getHeight(), Bitmap.Config.RGB_565);
}
Canvas canvas = new Canvas(bitmap);
try {
// Output the file
FileOutputStream mFileOutStream = new FileOutputStream(StoredPath);
v.draw(canvas);
// Convert the output file to Image such as .png
bitmap.compress(CompressFormat.PNG, 90, mFileOutStream);
Intent intent = new Intent(SignatureActivity.this, MainActivity.class);
intent.putExtra("imagePath", StoredPath);
startActivity(intent);
finish();
mFileOutStream.flush();
mFileOutStream.close();
} catch (Exception e) {
Log.v("log_tag", e.toString());
}
}
public void clear() {
path.reset();
invalidate();
mGetSign.setEnabled(false);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
mGetSign.setEnabled(true);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
lastTouchX = eventX;
lastTouchY = eventY;
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
resetDirtyRect(eventX, eventY);
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
expandDirtyRect(historicalX, historicalY);
path.lineTo(historicalX, historicalY);
}
path.lineTo(eventX, eventY);
break;
default:
debug("Ignored touch event: " + event.toString());
return false;
}
invalidate((int) (dirtyRect.left - HALF_STROKE_WIDTH),
(int) (dirtyRect.top - HALF_STROKE_WIDTH),
(int) (dirtyRect.right + HALF_STROKE_WIDTH),
(int) (dirtyRect.bottom + HALF_STROKE_WIDTH));
lastTouchX = eventX;
lastTouchY = eventY;
return true;
}
private void debug(String string) {
Log.v("log_tag", string);
}
private void expandDirtyRect(float historicalX, float historicalY) {
if (historicalX < dirtyRect.left) {
dirtyRect.left = historicalX;
} else if (historicalX > dirtyRect.right) {
dirtyRect.right = historicalX;
}
if (historicalY < dirtyRect.top) {
dirtyRect.top = historicalY;
} else if (historicalY > dirtyRect.bottom) {
dirtyRect.bottom = historicalY;
}
}
private void resetDirtyRect(float eventX, float eventY) {
dirtyRect.left = Math.min(lastTouchX, eventX);
dirtyRect.right = Math.max(lastTouchX, eventX);
dirtyRect.top = Math.min(lastTouchY, eventY);
dirtyRect.bottom = Math.max(lastTouchY, eventY);
}
}
}
Any help would be appreciated. I have created a thread however it still is not working.
new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bitmap.compress(CompressFormat.PNG, 90, mFileOutStream);
}}.start();
Fixed this error by changing code for granting permission to device
Button.OnClickListener onButtonClick = new Button.OnClickListener() {
@Override
public void onClick(View v) {
if (v == mClear) {
Log.v("log_tag", "Panel Cleared");
mSignature.clear();
mGetSign.setEnabled(false);
} else if (v == mGetSign) {
Log.v("log_tag", "Panel Saved");
if (Build.VERSION.SDK_INT >= 23 && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
isStoragePermissionGranted();
} else {
view.setDrawingCacheEnabled(true);
mSignature.save(view, StoredPath);
Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();
// Calling the same class
recreate();
}
} else if(v == mCancel){
Log.v("log_tag", "Panel Canceled");
// Calling the BillDetailsActivity
Intent intent = new Intent(SignatureActivity.this, MainActivity.class);
startActivity(intent);
}
}
};
You are calling the bitmap.compress method on the main (UI) thread, which can block the main thread and cause your UI to freeze. To fix this, you should run this code on a background (worker) thread - which you should be doing anyway, because you're also doing file I/O, which can also block the main thread.
There are many ways to do this, using AsyncTask, Executors, Threads, Handlers, etc. You can find some info in the official documentation here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With