DrawingView with share image in Sketchware Pro

1. This example shows how to create a DrawingView in Sketchware.

2. Create a new project in Sketchware.

3. Switch On AppCompat and design.

4. In View manager, deselect toolbar for main.xml.

5. In java/Kotlin manager:

a. Add a file DrawingView.java and put following codes in it.


package com.sanju.drawingview;

import android.view.View;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Canvas;
import android.content.Context;
import android.util.AttributeSet;
import android.graphics.Color;
import android.view.MotionEvent;
import android.graphics.PorterDuff;

import java.util.ArrayList;

public class DrawingView extends View {

    private Bitmap mBitmap;
    private Canvas mCanvas;
    private Paint mBitmapPaint;
    private Paint mPaint;
    private Paint circlePaint;
    private Path circlePath;
    private Path mPath;

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    private ArrayList<PathPaint> all_paths = new ArrayList<>();

    public DrawingView(Context context) {
        super(context);
        init();
    }

    public DrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mPath = new Path();

        mBitmapPaint = new Paint(Paint.DITHER_FLAG);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(6f);

        circlePath = new Path();
        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);
        circlePaint.setColor(Color.BLUE);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setStrokeWidth(1f);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
        canvas.drawPath(mPath, mPaint);
        canvas.drawPath(circlePath, circlePaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                mPath.reset();
                mPath.moveTo(x, y);
                mX = x;
                mY = y;
                invalidate();
                break;

            case MotionEvent.ACTION_MOVE:
                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;

                    circlePath.reset();
                    circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
                }
                invalidate();
                break;

            case MotionEvent.ACTION_UP:
                mPath.lineTo(mX, mY);
                circlePath.reset();

                Path p = new Path(mPath);
                Paint pnt = new Paint(mPaint);
                all_paths.add(new PathPaint(p, pnt));

                mCanvas.drawPath(mPath, mPaint);
                mPath.reset();
                invalidate();
                break;
        }
        return true;
    }

    public void undo() {
        if (all_paths.size() > 0) {
            all_paths.remove(all_paths.size() - 1);
        }

        mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

        for (PathPaint pp : all_paths) {
            mCanvas.drawPath(pp.getPath(), pp.getPaint());
        }
        invalidate();
    }
    
    // Clear all drawings
public void clear() {
    all_paths.clear();
    mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    invalidate();
}

// Set stroke width
public void setStrokeWidth(float width) {
    mPaint.setStrokeWidth(width);
}

// Set stroke color
public void setStrokeColor(int color) {
    mPaint.setColor(color);
}


}

b. Add another file PathPaint.java and put following codes in it.


package com.sanju.drawingview;

import android.graphics.Paint;
import android.graphics.Path;

public class PathPaint {

    private Path path;
    private Paint paint;

    public PathPaint(Path path, Paint paint) {
        this.path = path;
        this.paint = paint;
    }

    public Path getPath() {
        return path;
    }

    public Paint getPaint() {
        return paint;
    }
}

c. Add another file SaveUtils.java and put following codes in it.


package com.sanju.drawingview;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.view.View;
import android.widget.Toast;

import androidx.core.content.FileProvider;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public final class SaveUtils {

    private SaveUtils() {
        // Utility class
    }

    /**
     * Converts a View into a Bitmap
     */
    public static Bitmap viewToBitmap(View view) {
        Bitmap bitmap = Bitmap.createBitmap(
                view.getWidth(),
                view.getHeight(),
                Bitmap.Config.ARGB_8888
        );

        Canvas canvas = new Canvas(bitmap);
        Drawable background = view.getBackground();

        if (background != null) {
            background.draw(canvas);
        } else {
            canvas.drawColor(Color.WHITE);
        }

        view.draw(canvas);
        return bitmap;
    }

    /**
     * Saves bitmap to internal storage
     */
    public static File saveBitmap(Context context, Bitmap bitmap) {
        if (context == null || bitmap == null) return null;

        File imageFile = new File(context.getFilesDir(), "temp_drawing.png");

        try (FileOutputStream fos = new FileOutputStream(imageFile)) {
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.flush();
            return imageFile;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (!bitmap.isRecycled()) {
                bitmap.recycle();
            }
        }
    }

    /**
     * Shares image using FileProvider
     */
    public static void shareImage(Context context, File imageFile) {
        if (context == null || imageFile == null || !imageFile.exists()) return;

        try {
            Uri imageUri = FileProvider.getUriForFile(
                    context,
                    context.getPackageName() + ".provider",
                    imageFile
            );

            Intent shareIntent = new Intent(Intent.ACTION_SEND);
            shareIntent.setType("image/*");
            shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

            context.startActivity(
                    Intent.createChooser(shareIntent, "Share image via")
            );

        } catch (IllegalArgumentException e) {
            Toast.makeText(context,
                    "FileProvider error: " + e.getMessage(),
                    Toast.LENGTH_SHORT).show();
        } catch (ActivityNotFoundException e) {
            Toast.makeText(context,
                    "No app available to share image",
                    Toast.LENGTH_SHORT).show();
        }
    }
}

6. In main.xml, add a Linear Horizontal containing 6 ImageViews image_strokecolor, image_strokewidth, image_background, image_undo, image_delete, image_download.

Below this, add a Linear Vertical with width and height match_parent. Convert it to com.sanju.drawingview.DrawingView (Replace com.sanju.drawingview with your package name). Also change its ID to drawingView.


7. In AndroidManifest manager, in App Components, put following codes:


<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">

    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

8. In Resource manager, create a folder xml and inside this create a file file_paths.xml. Put following codes in it.


<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path
        name="internal_files"
        path="." />
</paths>

9. Create a more block, showStrokeWidthDialog and put following codes in it.


String[] sizes = {"4", "8", "12", "16", "20"};
        new AlertDialog.Builder(this)
                .setTitle("Select Stroke Width")
                .setItems(sizes, (d, i) ->
                        binding.drawingView.setStrokeWidth(Float.parseFloat(sizes[i])))
                .show();

10. In event image_strokewidth onClick, use the more block showStrokeWidthDialog.

11. Create another more block showStrokeColorDialog and put following codes in it.


        int[] colors = {
                Color.BLACK, Color.RED, Color.BLUE,
                Color.GREEN, Color.YELLOW
        };
        String[] names = {"Black", "Red", "Blue", "Green", "Yellow"};

        new AlertDialog.Builder(this)
                .setTitle("Select Stroke Color")
                .setItems(names, (d, i) ->
                        binding.drawingView.setStrokeColor(colors[i]))
                .show();

12. In event image_strokecolor onClick, use the more block showStrokeColorDialog.

13. Create a more block, showBackgroundColorDialog and put following codes in it.


int[] colors = {
    Color.WHITE, Color.LTGRAY,
    Color.YELLOW, Color.CYAN
};
String[] names = {"White", "Gray", "Yellow", "Cyan"};

new AlertDialog.Builder(this)
    .setTitle("Select Background Color")
    .setItems(names, (d, i) ->
            binding.drawingView.setBackgroundColor(colors[i]))
    .show();

14. In event image_background onClick, use the more block showBackgroundColorDialog.

15. In the event image_undo onClick, use following codes.


binding.drawingView.undo();

16. In the event image_delete onClick, use following codes.


binding.drawingView.clear();

17. In the event image_download onClick, use following codes.


Bitmap bitmap = SaveUtils.viewToBitmap(binding.drawingView);

File savedImage = SaveUtils.saveBitmap(getApplicationContext(), bitmap);

SaveUtils.shareImage(MainActivity.this, savedImage);

18. In Build Settings, select D8 and java version 1.8 and save.

19. Run the project.


Comments

Popular posts from this blog

Simple car racing android game in Sketchware

How to enable upload from webview in Sketchware?

Simple Audio recorder app in Sketchware

Custom dialog box in Sketchware using CustomView

Retrieve contact list in Sketchware