class DrawingView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
private lateinit var mDrawPath: FingerPath
private lateinit var mCanvasBitmap: Bitmap
private lateinit var mCanvasPaint: Paint
private lateinit var mDrawPaint: Paint
private var mBrushSize = 0
private var color = Color.BLACK
private lateinit var canvas: Canvas
init {
setUpDrawing()
}
private fun setUpDrawing() {
mDrawPaint = Paint()
mDrawPath = FingerPath(color, mBrushSize.toFloat())
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
}
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, 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.toFloat()
mDrawPath.reset()
mDrawPath.moveTo(touchX!!, touchY!!)
}
MotionEvent.ACTION_MOVE -> {
mDrawPath.lineTo(touchX!!, touchY!!)
}
MotionEvent.ACTION_UP -> {
mDrawPath = FingerPath(color, mBrushSize.toFloat())
}
else -> return false
}
invalidate()
return true
}
internal inner class FingerPath(var color: Int, var brushThickness: Float) : Path()
}
So, I'm taking a course about Android Development and the instructor is building a drawing app -- and I can't understand how to code works, because he's not explaining the 'why'; so, he types a lot of code without explaining why he uses that variable or why is he overring those functions; therefore, I don't understand how to code work. Could you help me in understanding how this code work?
class DrawingView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
private lateinit var mDrawPath: FingerPath
private lateinit var mCanvasBitmap: Bitmap
private lateinit var mCanvasPaint: Paint
private lateinit var mDrawPaint: Paint
private var mBrushSize = 0
private var color = Color.BLACK
private lateinit var canvas: Canvas
init {
// init block will called first when instance will be created so we
// are calling method setUpDrawing() as it is initialising everything
// that required to draw like color , brush size , brush behaviour
// (round , stroke etc .. ) . in simple manner , we can say painter is
// collecting all required tools before starting to paint
setUpDrawing()
}
private fun setUpDrawing() {
mDrawPaint = Paint()
mDrawPath = FingerPath(color, mBrushSize.toFloat())
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
}
// this method is going to be called by system when size is going to be
// changed so we are here creating blank board on which we are going to
// draw
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, mDrawPaint)
if (!mDrawPath.isEmpty) {
mDrawPaint.strokeWidth = mDrawPath.brushThickness
mDrawPaint.color = mDrawPath.color
canvas.drawPath(mDrawPath, mDrawPaint) // drawing path on canvas
}
}
// this method will be called by system when user is going to touch screen
override fun onTouchEvent(event: MotionEvent?): Boolean {
val touchX = event?.x
val touchY = event?.y
when (event?.action) {
// this event will be fired when user put finger on screen
MotionEvent.ACTION_DOWN -> {
mDrawPath.color = color
mDrawPath.brushThickness = mBrushSize.toFloat()
mDrawPath.reset() // reseting path before we set inital point
mDrawPath.moveTo(touchX!!, touchY!!)// set point from where drawing will be started
}
// this event will be fired when user start to move it's fingure . this will be fired continually until user pickup fingure
MotionEvent.ACTION_MOVE -> {
mDrawPath.lineTo(touchX!!, touchY!!)
}
// this event will be fired when user will pick up fingure from screen
MotionEvent.ACTION_UP -> {
mDrawPath = FingerPath(color, mBrushSize.toFloat())
}
else -> return false
}
invalidate() / /refreshing layout to reflect drawing changes
return true
}
internal inner class FingerPath(var color: Int, var brushThickness: Float) : Path()
}
This code is a custom view class in Android for drawing. The class extends the View class and implements touch event handling to allow a user to draw on the screen.
The class defines instance variables to store the paint, path, bitmap and canvas used for drawing. It also has variables for brush size and color.
In the setUpDrawing method, the paint object for drawing and the path object for tracking the user's touch are set up.
In onSizeChanged, the bitmap object is created and its canvas object is initialized.
In onDraw, the canvas is drawn on the screen and the path is drawn on the canvas if it's not empty.
The onTouchEvent method handles touch events (DOWN, MOVE, UP) and updates the path accordingly.
The FingerPath inner class extends the Path class and contains the color and brush size of the path.
Have you read through this section of the docs? Custom View Components
It explains the basics of creating a custom view, which on a basic level means overriding onDraw (where you draw the contents of the view) and maybe onSizeChanged (where you're told how big the view is - maybe you want to set the thickness of your paint brushes, work out where the centre of the view is etc).
onTouchEvent is a little more advanced, that allows you to handle the user poking at your view - the link up there gets into it, but you'll have a better understanding if you read the section on touch gestures. Broadly, you can get info about interaction events through onTouchEvent and work out things like whether the user touched the view, moved a finger, lifted it or moved it out of bounds etc. You'd use this info to, say, draw stuff under the user's finger for a painting app.
A Canvas is a drawing interface for a Bitmap. Instead of having to work out what pixels to change to what colour to draw a line or whatever, you do all your drawing through a Canvas that wraps that Bitmap. This lets you abstract the process and do high-level stuff like "draw a line", "draw an arc", "draw this entire path", "render this text" and so on. The Canvas works out how to make that happen in the Bitmap's pixel array.
The drawing happens with Paints, which you can think of as an object that says how the drawing happens. What does drawing a line look like? The Paint you use defines things like stroke width, transparency, colour etc - it's like the tool options in a paint program. It also allows you to set things like text size (if you're using it to render text) and do fancy things with blending over the pixels it's being drawn on, create shaders etc. It can also do things like control tiling if you're using a bitmap as the source image.
Generally you'll want to create these Paints at startup and reuse them during your View's lifespan, so it's typical to have a few of them - the one for painting the background, the one for drawing curves, the one for rendering text etc.
Usually in onDraw you just draw the view in its current state. The method takes a Canvas parameter - that's how you draw to the actual bitmap rendered on the UI. So if you had a knob, you could draw the circle, draw the position indicator at the appropriate location, add any text, and you're done!
For a drawing app it's a little more complicated - that "state" you need to remember and draw is the sum of all the drawing operations that have happened. You can get fancy with this (to create levels of undo) but the simplest approach is to create your own Bitmap to store the current drawing. You wrap that in its own Canvas (so you can draw on it) and then all the user interaction draws stuff to that Canvas, so you're painting on the bitmap. Then, in onDraw, you basically just need to paint the contents of that bitmap onto the system-provided Canvas so it gets drawn on-screen - just showing what you have, basically.
The code you have here is a little weird - you have that internal bitmap (called mCanvasBitmap) wrapped in a Canvas (called canvas) but you never actually draw to it. I feel like this line is meant to be rendering the Path you build up with the touch gestures:
override fun onDraw(canvas: Canvas) {
...
if (!mDrawPath.isEmpty) {
...
canvas.drawPath(mDrawPath, mDrawPaint) // drawing path on canvas
}
}
but in onDraw's scope canvas is referring to that parameter being passed in, i.e. the Canvas for drawing to the screen, not the one wrapping mCanvasBitmap. So that internal bitmap always stays empty, and then you just draw the most recent path (this is a good example of watching how you name things - why not mCanvas to be consistent?). It never "builds up" an image by drawing paths onto your internal bitmap. I can't tell what the exact intent is from the code though, I just thought I'd point it out!
I need to disable filtering in Image to display pixel art properly. How can I do it?
I used to do it like in this answer:
val DRAW_FILTER = PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0)
#SuppressLint("RestrictedApi")
class AliasingDrawableWrapper(wrapped: Drawable) : DrawableWrapper(wrapped) {
override fun draw(canvas: Canvas) {
val oldDrawFilter = canvas.drawFilter
canvas.drawFilter = DRAW_FILTER
super.draw(canvas)
canvas.drawFilter = oldDrawFilter
}
}
and
imageView.setImageDrawable(AliasingDrawableWrapper(drawable)
At least fort now, you can't. Jetpack Compose uses a shared Paint created with the anti alias flag on.
I opened an issue asking for the possibility to set our own flags:
https://issuetracker.google.com/issues/172473708
Here's the Compose's declaration in AndroidPaint.kt:
internal fun makeNativePaint() =
android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG)
Since Compose 1.1.0-alpha01 Image takes an optional FilterQuality parameter. Use FilterQuality.None for pixel art:
Image(
...,
filterQuality = FilterQuality.None
)
val paint = Paint()
paint.color = Color.RED
paint.strokeWidth = 3f
paint.style = Paint.Style.STROKE
objectDetector.processImage(getVisionImageFromFrame(frame))
.addOnSuccessListener {
var result = ""
it.forEach { item ->
val id = item.trackingId
val bounds = item.boundingBox
val category = item.classificationCategory
result += category.toString() //TODO : Get the knowledge graph result for this entity
Log.e("TAG",category.toString())
}
callback(result)
}
.addOnFailureListener {
callback("Unable to detect an object")
}
.addOnCompleteListener {
}
Also when the object is being detected a number is being displayed rather than the name of object. For ex: for detecting pen, 010 is being displayed rather than the name "pen". Also I can't get how to draw a bounding box around detected image. Please see to it.
You use the google mlkit for object detection, right?! There is a codelab from google in kotlin where they explain the whole implementation process and also how to draw the bounding boxes.
codelab
The problem with 010 is that you read out the index number of the category. you have to match it with the category name but this is also in the codelab code. pay attention, there is a difference between label and category.
I am trying to test a scenario where my android app remains in the background for a while and some static variables which were lateinit get deallocated.
Is there any way I can manually Uninitialise a lateinit variable or free memory allocated to a certain object in kotlin ?
I was able to achieve this via reflection
val prop = MyClass::class.members.find { it.name == "lateInitVar" } as KMutableProperty
prop.setter.call(MyClass, "null")
I have two packages with one that is the primary package with the MainActivity. The other package gets imported into the MainActivity. The other package deals with drawing on a canvas. However, any changes that I am making to the imported package are not showing up on the actual app. For example, when i change the size of the rectangle being drawn, nothing is actually changing. Does anyone know why this is?
Sorry if this is a trivial question, I just started learning android dev. Also, I got the code off of github, and i'm trying to learn from it. If there is any information that I left out that is crucial please ask. Thank
The code is from: https://github.com/husaynhakeem/android-face-detector
The specific part that I had tried to change is the ANCHOR_RADIUS, ID_OFFSET, as well as the xOffset, yOffset values in the FaceBoundsOverlay. Each variable affects some boundary that is supposed to be used to draw the rectangle and center dot. I pasted the drawAnchor method down below. In that method, when I changed the ANCHOR_RADIUS to 50f, I expected the dot to become larger but nothing was changed.
The FaceBoundsOverlay is in the package called facedetector while the MainActivity is in the package called facedetectorapp
class FaceBoundsOverlay #JvmOverloads constructor(
ctx: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : View(ctx, attrs, defStyleAttr) {
.....
private fun drawAnchor(canvas: Canvas, centerX: Float, centerY:
Float) { private fun drawAnchor(canvas: Canvas, centerX: Float,
centerY: Float) {
canvas.drawCircle(
centerX,
centerY,
ANCHOR_RADIUS,
anchorPaint)
}
companion object {
private const val ANCHOR_RADIUS = 10f
// private const val ANCHOR_RADIUS = 50f
private const val ID_OFFSET = 50f
}
}