Drawing Examples
A step by step Drawing example.
Drawing Application
An Android application made in kotlin. Allows users to draw sketches on the screen using different brushes and colours. Functionality of importing images from gallery as background and sharing the drawing via Whatsapp , Email etc..
A drawing app designed for kids with functionalities such as :- • Pick a colour from the listed colours • Pick from 3 different stroke size • Storing the drawing on your phone • Undo Strokes • Importing an image from gallery and add as background • Sharing the drawing via Whatsapp , Email
Here's a demo of the app :-
https://user-images.githubusercontent.com/70281464/178120724-2c7c5a52-551e-4fc7-b056-fd4fb123943e.mov
Full Example
Awaiting below is a full android sample to demonstrate Drawing concept.
Step 1. Dependencies
We need to add some dependencies in our app/build.gradle
file as shown below:
(a). build.gradle
Our app-level
build.gradle
.
We Prepare our dependencies as shown below. You may use later versions.
We will also enable Java8 so that we can utilize a myriad of java8 features.
We then declare our app dependencies under the dependencies
closure. We will need the following 5 dependencies:
- Our
Kotlin-stdlib
library. - Our
Core-ktx
library. - Our
Appcompat
library. - Our
Material
library. - Our
Constraintlayout
library.
Here is our full app/build.gradle
:
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.kidsdrawingapp"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
Step 2. Our Android Manifest
We will need to look at our AndroidManifest.xml
.
(a). AndroidManifest.xml
Our
AndroidManifest
file.
Here we will add the following permission:
- Our
READ_EXTERNAL_STORAGE
permission. - Our
WRITE_EXTERNAL_STORAGE
permission.
Here is the full Android Manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.kidsdrawingapp">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.KidsDrawingApp">
<activity android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:authorities="com.example.kidsdrawingapp.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/path"/>
</provider>
</application>
</manifest>
Step 3. Create XML
Create a directory known as xml
inside your res
directory and add the following xml file:
(a). path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="captured"
path="Android/data/com.example.kidsdrawingapp/files"/>
</paths>
Step 4. Design Layouts
In Android we design our UI interfaces using XML. So let's create the following layouts:
(a). dialog_custom_progress.xml
Our
dialog_custom_progress
layout.
Inside your /res/layout/
directory create an xml layout file named dialog_custom_progress.xml
.
<?xml version="1.0" encoding="utf-8"?>
<!--TODO(Step 1 : Creating a view for custom progress dialog)-->
<!--START-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/textView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Please Wait..."
android:textColor="@android:color/black"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/progressBar"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--END-->
(b). dialog_brush_size.xml
Our
dialog_brush_size
layout.
Create an xml layout file named dialog_brush_size.xml
and add the following widgets:
androidx.constraintlayout.widget.ConstraintLayout
ImageButton
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:gravity="center">
<ImageButton
android:id="@+id/ib_small_brush"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="image_small"
android:src="@drawable/small"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/ib_medium_brush"/>
<ImageButton
android:id="@+id/ib_medium_brush"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="image_medium"
android:src="@drawable/medium"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/ib_small_brush"
app:layout_constraintBottom_toTopOf="@+id/ib_large_brush"/>
<ImageButton
android:id="@+id/ib_large_brush"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="image_large"
android:src="@drawable/large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/ib_medium_brush"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
(c). activity_main.xml
Our
activity_main
layout.
Inside your /res/layout/
directory create an xml layout file named activity_main.xml
.
Design your XML layout using the following 6 UI widgets and ViewGroups:
androidx.constraintlayout.widget.ConstraintLayout
FrameLayout
ImageView
com.example.kidsdrawingapp.DrawingView
LinearLayout
ImageButton
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/fl_drawing_view_layout"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="5dp"
android:background="@drawable/backgroud_drawing_view_layout"
android:padding="1dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@+id/ll_paint_colors">
<ImageView
android:id="@+id/iv_background"
android:contentDescription="background image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
<com.example.kidsdrawingapp.DrawingView
android:id="@+id/drawing_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80FFFFFF"
/>
</FrameLayout>
<LinearLayout
android:id="@+id/ll_paint_colors"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/ll_action_buttons"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fl_drawing_view_layout">
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:onClick="paintClicked"
android:background="@color/skin"
android:layout_margin="2dp"
android:src="@drawable/pallet_normal"
android:tag="@color/skin"/>
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:background="@color/black"
android:layout_margin="2dp"
android:onClick="paintClicked"
android:src="@drawable/pallet_normal"
android:tag="@color/black"/>
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:background="@color/red"
android:layout_margin="2dp"
android:onClick="paintClicked"
android:src="@drawable/pallet_normal"
android:tag="@color/red"/>
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:background="@color/green"
android:onClick="paintClicked"
android:layout_margin="2dp"
android:src="@drawable/pallet_normal"
android:tag="@color/green"/>
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:onClick="paintClicked"
android:background="@color/blue"
android:layout_margin="2dp"
android:src="@drawable/pallet_normal"
android:tag="@color/blue"/>
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:onClick="paintClicked"
android:background="@color/yellow"
android:layout_margin="2dp"
android:src="@drawable/pallet_normal"
android:tag="@color/yellow"/>
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:background="@color/lollipop"
android:onClick="paintClicked"
android:layout_margin="2dp"
android:src="@drawable/pallet_normal"
android:tag="@color/lollipop"/>
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:onClick="paintClicked"
android:background="@color/random"
android:layout_margin="2dp"
android:src="@drawable/pallet_normal"
android:tag="@color/random"/>
<ImageButton
android:layout_width="25dp"
android:layout_height="25dp"
android:contentDescription="color_pallet"
android:onClick="paintClicked"
android:background="@color/white"
android:layout_margin="2dp"
android:src="@drawable/pallet_normal"
android:tag="@color/white"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_action_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<ImageButton
android:id="@+id/ib_gallery"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_gallery"
android:contentDescription="gallery image"
android:scaleType="fitXY"
android:layout_margin="5dp" />
<ImageButton
android:id="@+id/ib_undo"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_undo"
android:contentDescription="undo image"
android:scaleType="fitXY"
android:layout_margin="5dp" />
<ImageButton
android:id="@+id/ib_brush"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_brush"
android:contentDescription="brush image"
android:scaleType="fitXY"
android:layout_margin="5dp" />
<ImageButton
android:id="@+id/ib_save"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_save"
android:contentDescription="brush image"
android:scaleType="fitXY"
android:layout_margin="5dp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Step 5. Write Code
Finally we need to write our code as follows:
(a). DrawingView.kt
Our
DrawingView
class.
Create a Kotlin file named DrawingView.kt
.
We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:
Context
from theandroid.content
package.*
from theandroid.graphics
package.AttributeSet
from theandroid.util
package.TypedValue
from theandroid.util
package.MotionEvent
from theandroid.view
package.View
from theandroid.view
package.
We will be overriding the following functions:
onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int)
.onDraw(canvas: Canvas)
.onTouchEvent(event: MotionEvent?): Boolean
.
We will be creating the following methods:
onClickUndo()
.setUpDrawing()
.setsizeforBrush(parameter)
- We pass aFloat
object as a parameter.setColor(parameter)
- Let's pass aString
object as a parameter.
(a). Our setUpDrawing()
function
Write the setUpDrawing()
function as follows:
private fun setUpDrawing() {
mDrawPaint = Paint()
mDrawPath = CustomPath(color,mBrushSize)
mDrawPaint!!.color = color
mDrawPaint!!.style = Paint.Style.STROKE
mDrawPaint!!.strokeJoin = Paint.Join.ROUND
mDrawPaint!!.strokeCap = Paint.Cap.ROUND
mCanvasPaint = Paint(Paint.DITHER_FLAG)
mBrushSize = 20.toFloat()
}
(b). Our setColor()
function
Write the setColor()
function as follows:
(c). Our setsizeforBrush()
function
Write the setsizeforBrush()
function as follows:
fun setsizeforBrush(newsize:Float) {
mBrushSize = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, newsize, resources.displayMetrics
)
mDrawPaint!!.strokeWidth = mBrushSize
}
Here is the full code:
package replace_with_your_package_name
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
class DrawingView(context: Context,attrs:AttributeSet): View(context,attrs) {
private var mDrawPath: CustomPath? = null
private var mCanvasBitmap: Bitmap? = null
private var mDrawPaint:Paint? = null
private var mCanvasPaint:Paint? = null
private var mBrushSize:Float = 0.toFloat()
private var color = Color.BLACK
private var canvas: Canvas? = null
private val mPaths = ArrayList<CustomPath>()
private val mUndoPaths = ArrayList<CustomPath>()
init {
setUpDrawing()
}
fun onClickUndo(){
if (mPaths.size > 0){
mUndoPaths.add(mPaths.removeAt(mPaths.size - 1))
invalidate()
}
}
private fun setUpDrawing() {
mDrawPaint = Paint()
mDrawPath = CustomPath(color,mBrushSize)
mDrawPaint!!.color = color
mDrawPaint!!.style = Paint.Style.STROKE
mDrawPaint!!.strokeJoin = Paint.Join.ROUND
mDrawPaint!!.strokeCap = Paint.Cap.ROUND
mCanvasPaint = Paint(Paint.DITHER_FLAG)
mBrushSize = 20.toFloat()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mCanvasBitmap = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888)
canvas = Canvas(mCanvasBitmap!!)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawBitmap(mCanvasBitmap!!,0f,0f,mCanvasPaint)
for(path in mPaths){
mDrawPaint!!.strokeWidth = path.brushThickness
mDrawPaint!!.color = path.color
canvas.drawPath(path, mDrawPaint!!)
}
if(!mDrawPath!!.isEmpty) {
mDrawPaint!!.strokeWidth = mDrawPath!!.brushThickness
mDrawPaint!!.color = mDrawPath!!.color
canvas.drawPath(mDrawPath!!, mDrawPaint!!)
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
val touchx = event?.x
val touchy = event?.y
when(event?.action){
MotionEvent.ACTION_DOWN -> {
mDrawPath!!.color = color
mDrawPath!!.brushThickness = mBrushSize
mDrawPath!!.reset()
mDrawPath!!.moveTo(touchx!!,touchy!!)
}
MotionEvent.ACTION_MOVE -> {
mDrawPath!!.lineTo(touchx!!, touchy!!)
}
MotionEvent.ACTION_UP -> {
mPaths.add(mDrawPath!!)
mDrawPath = CustomPath(color,mBrushSize)
}
else -> return false
}
invalidate()
return true
}
fun setsizeforBrush(newsize:Float) {
mBrushSize = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, newsize, resources.displayMetrics
)
mDrawPaint!!.strokeWidth = mBrushSize
}
fun setColor(newColor:String){
color = Color.parseColor(newColor)
mDrawPaint!!.color = color
}
internal inner class CustomPath(var color:Int,var brushThickness:Float): Path() {
}
}
(b). MainActivity.kt
Our
MainActivity
class.
Create a Kotlin file named MainActivity.kt
.
We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:
Manifest
from theandroid
package.Activity
from theandroid.app
package.Dialog
from theandroid.app
package.Intent
from theandroid.content
package.PackageManager
from theandroid.content.pm
package.Bitmap
from theandroid.graphics
package.Canvas
from theandroid.graphics
package.Color
from theandroid.graphics
package.Image
from theandroid.media
package.MediaScannerConnection
from theandroid.media
package.AsyncTask
from theandroid.os
package.AppCompatActivity
from theandroidx.appcompat.app
package.Bundle
from theandroid.os
package.MediaStore
from theandroid.provider
package.View
from theandroid.view
package.*
from theandroid.widget
package.ActivityResultCallback
from theandroidx.activity.result
package.ActivityResultContracts
from theandroidx.activity.result.contract
package.ActivityCompat
from theandroidx.core.app
package.ContextCompat
from theandroidx.core.content
package.get
from theandroidx.core.view
package.AsyncTaskLoader
from theandroidx.loader.content
package.
Next create a class that derives from AppCompatActivity
and add its contents as follows:
We will be overriding the following functions:
onCreate(savedInstanceState: Bundle?)
.onPreExecute()
.doInBackground(vararg params: Any?): String
.onPostExecute(result: String?)
.
We will be creating the following methods:
showBrushSizeChooserDialog()
.paintClicked(parameter)
- We pass aView
object as a parameter.requestStoragePermission()
.isReadStorageAllowed():Boolean
.getBitmapFromView(parameter)
- Pass to this method aView
object as a parameter.showProgressDialog()
.cancelProgressDialog()
.
(a). Our isReadStorageAllowed()
function
Write the isReadStorageAllowed()
function as follows:
private fun isReadStorageAllowed():Boolean{
val result = ContextCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE)
return result == PackageManager.PERMISSION_GRANTED
}
(b). Our requestStoragePermission()
function
Write the requestStoragePermission()
function as follows:
private fun requestStoragePermission() {
if(ActivityCompat.shouldShowRequestPermissionRationale(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE).toString())){
Toast.makeText(this,"Need Permission to add background",Toast.LENGTH_LONG).show()
}
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE),
STORAGE_PERMISSION_CODE)
}
(c). Our cancelProgressDialog()
function
Write the cancelProgressDialog()
function as follows:
(d). Our showBrushSizeChooserDialog()
function
Write the showBrushSizeChooserDialog()
function as follows:
private fun showBrushSizeChooserDialog(){
val brushDialog = Dialog(this)
brushDialog.setContentView(R.layout.dialog_brush_size)
brushDialog.setTitle("Brush size: ")
val smallBtn = brushDialog.findViewById<ImageButton>(R.id.ib_small_brush)
val mediumBtn = brushDialog.findViewById<ImageButton>(R.id.ib_medium_brush)
val largeBtn = brushDialog.findViewById<ImageButton>(R.id.ib_large_brush)
val drawing_view = findViewById<DrawingView>(R.id.drawing_view)
smallBtn.setOnClickListener {
drawing_view.setsizeforBrush(10.toFloat())
brushDialog.dismiss()
}
mediumBtn.setOnClickListener {
drawing_view.setsizeforBrush(20.toFloat())
brushDialog.dismiss()
}
largeBtn.setOnClickListener {
drawing_view.setsizeforBrush(30.toFloat())
brushDialog.dismiss()
}
brushDialog.show()
}
(e). Our paintClicked()
function
Write the paintClicked()
function as follows:
fun paintClicked(view: View){
val drawing_view = findViewById<DrawingView>(R.id.drawing_view)
if(view!== mImageButtonCurrentPaint){
val imageButton = view as ImageButton
val colorTag = imageButton.tag.toString()
drawing_view.setColor(colorTag)
imageButton.setImageDrawable(
ContextCompat.getDrawable(this,R.drawable.pallet_pressed)
)
mImageButtonCurrentPaint!!.setImageDrawable(
ContextCompat.getDrawable(this,R.drawable.pallet_normal)
)
mImageButtonCurrentPaint = view
}
}
(f). Our showProgressDialog()
function
Write the showProgressDialog()
function as follows:
private fun showProgressDialog(){
mProgressDialog = Dialog(this@MainActivity)
mProgressDialog.setContentView(R.layout.dialog_custom_progress)
mProgressDialog.show()
}
(g). Our getBitmapFromView()
function
Write the getBitmapFromView()
function as follows:
private fun getBitmapFromView(view: View):Bitmap{
val returnedBitmap = Bitmap.createBitmap(view.width,view.height,Bitmap.Config.ARGB_8888)
val canvas = Canvas(returnedBitmap)
val bgDrawable = view.background
if(bgDrawable != null)
{
bgDrawable.draw(canvas)
}else {
canvas.drawColor(Color.WHITE)
}
view.draw(canvas)
return returnedBitmap
}
Here is the full code:
package replace_with_your_package_name
import android.Manifest
import android.app.Activity
import android.app.Dialog
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.media.Image
import android.media.MediaScannerConnection
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore
import android.view.View
import android.widget.*
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.get
import androidx.loader.content.AsyncTaskLoader
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
class MainActivity : AppCompatActivity() {
private var mImageButtonCurrentPaint: ImageButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val drawing_view = findViewById<DrawingView>(R.id.drawing_view)
val ib_brush = findViewById<ImageButton>(R.id.ib_brush)
val ll_paint_colors = findViewById<LinearLayout>(R.id.ll_paint_colors)
mImageButtonCurrentPaint = ll_paint_colors[1] as ImageButton
mImageButtonCurrentPaint!!.setImageDrawable(
ContextCompat.getDrawable(this,R.drawable.pallet_pressed)
)
drawing_view.setsizeforBrush(20.toFloat())
ib_brush.setOnClickListener {
showBrushSizeChooserDialog()
}
val iv_background = findViewById<ImageView>(R.id.iv_background)
val ib_gallery = findViewById<ImageButton>(R.id.ib_gallery)
val getAction = registerForActivityResult(ActivityResultContracts.GetContent(),
ActivityResultCallback { uri ->
iv_background.setImageURI(uri)
})
ib_gallery.setOnClickListener {
getAction.launch("image/*")
}
val ib_undo = findViewById<ImageButton>(R.id.ib_undo)
ib_undo.setOnClickListener {
drawing_view.onClickUndo()
}
val fl_drawing_view_layout = findViewById<FrameLayout>(R.id.fl_drawing_view_layout)
val ib_save = findViewById<ImageButton>(R.id.ib_save)
ib_save.setOnClickListener {
if(isReadStorageAllowed())
{
BitmapAsyncTask(getBitmapFromView(fl_drawing_view_layout)).execute()
}
else{
requestStoragePermission()
}
}
}
private fun showBrushSizeChooserDialog(){
val brushDialog = Dialog(this)
brushDialog.setContentView(R.layout.dialog_brush_size)
brushDialog.setTitle("Brush size: ")
val smallBtn = brushDialog.findViewById<ImageButton>(R.id.ib_small_brush)
val mediumBtn = brushDialog.findViewById<ImageButton>(R.id.ib_medium_brush)
val largeBtn = brushDialog.findViewById<ImageButton>(R.id.ib_large_brush)
val drawing_view = findViewById<DrawingView>(R.id.drawing_view)
smallBtn.setOnClickListener {
drawing_view.setsizeforBrush(10.toFloat())
brushDialog.dismiss()
}
mediumBtn.setOnClickListener {
drawing_view.setsizeforBrush(20.toFloat())
brushDialog.dismiss()
}
largeBtn.setOnClickListener {
drawing_view.setsizeforBrush(30.toFloat())
brushDialog.dismiss()
}
brushDialog.show()
}
fun paintClicked(view: View){
val drawing_view = findViewById<DrawingView>(R.id.drawing_view)
if(view!== mImageButtonCurrentPaint){
val imageButton = view as ImageButton
val colorTag = imageButton.tag.toString()
drawing_view.setColor(colorTag)
imageButton.setImageDrawable(
ContextCompat.getDrawable(this,R.drawable.pallet_pressed)
)
mImageButtonCurrentPaint!!.setImageDrawable(
ContextCompat.getDrawable(this,R.drawable.pallet_normal)
)
mImageButtonCurrentPaint = view
}
}
private fun requestStoragePermission() {
if(ActivityCompat.shouldShowRequestPermissionRationale(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE).toString())){
Toast.makeText(this,"Need Permission to add background",Toast.LENGTH_LONG).show()
}
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE),
STORAGE_PERMISSION_CODE)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode==PackageManager.PERMISSION_GRANTED){
if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this@MainActivity,"Permission granted to read storage file",Toast.LENGTH_LONG).show()
}else{
Toast.makeText(this,"Oops you were just denied the permission",Toast.LENGTH_LONG).show()
}
}
}
private fun isReadStorageAllowed():Boolean{
val result = ContextCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE)
return result == PackageManager.PERMISSION_GRANTED
}
private fun getBitmapFromView(view: View):Bitmap{
val returnedBitmap = Bitmap.createBitmap(view.width,view.height,Bitmap.Config.ARGB_8888)
val canvas = Canvas(returnedBitmap)
val bgDrawable = view.background
if(bgDrawable != null)
{
bgDrawable.draw(canvas)
}else {
canvas.drawColor(Color.WHITE)
}
view.draw(canvas)
return returnedBitmap
}
private inner class BitmapAsyncTask(val mBitmap:Bitmap): AsyncTask<Any, Void, String>(){
private lateinit var mProgressDialog:Dialog
override fun onPreExecute() {
super.onPreExecute()
showProgressDialog()
}
override fun doInBackground(vararg params: Any?): String {
var result = ""
if(mBitmap != null)
{ try {
val bytes = ByteArrayOutputStream()
mBitmap.compress(Bitmap.CompressFormat.PNG,90,bytes)
val f = File(externalCacheDir!!.absoluteFile.toString()+File.separator
+"KidDrawingApp_"+ System.currentTimeMillis()/1000+".png")
val fos = FileOutputStream(f)
fos.write(bytes.toByteArray())
fos.close()
result = f.absolutePath
}catch (e: Exception){
result = ""
e.printStackTrace()
}
}
return result
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
cancelProgressDialog()
MediaScannerConnection.scanFile(this@MainActivity, arrayOf(result),null){
path, uri -> val shareIntent = Intent()
shareIntent.action = Intent.ACTION_SEND
shareIntent.putExtra(Intent.EXTRA_STREAM,uri)
shareIntent.type = "image/png"
startActivity(
Intent.createChooser(shareIntent,"Share")
)
}
}
private fun showProgressDialog(){
mProgressDialog = Dialog(this@MainActivity)
mProgressDialog.setContentView(R.layout.dialog_custom_progress)
mProgressDialog.show()
}
private fun cancelProgressDialog(){
mProgressDialog.dismiss()
}
}
companion object {
private const val STORAGE_PERMISSION_CODE = 1
private const val GALLERY = 2
}
}
Reference
Download the code below:
No. | Link |
---|---|
1. | Download Full Code |
2. | Read more here. |
3. | Follow code author here. |