Following up with my problem I describe here i'm trying to draw some shapes to a canvas that isn't associated with any view/layout (it's totally in memory & never drawn to the screen) and then use the bitmap I drew in my activity.
The problem:
It seems like my draw method never gets called. I have some log messages within it and I never see them print, and when I view the bitmap during runtime in Android Studio's debugger its always blank.
I've read various posts about having to call setWillNotDraw(false) to get the onDraw() method in a custom view to trigger, but because i'm never going to render this canvas to the screen my custom class extends Drawable() instead of View(context which doesn't include that method. This seems like a good choice since View includes lots of logic for user touches and other actions that my no-UI, background-generated drawable won't use.
That being said I still only saw blank bitmaps and no log messages from onDraw() when I extended View instead of Drawable and called setWillNowDraw(false) in the classes' constructor.
What's causing my canvas to always be blank?
class CustomImage : Drawable() {
var bitmap: Bitmap
private var barcodeText = Paint(ANTI_ALIAS_FLAG).apply {
color = Color.BLACK
textSize = 24f
}
private var circlePainter = Paint(ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
}
init {
bitmap = Bitmap.createBitmap(LABEL_SIDE_LENGTH, LABEL_SIDE_LENGTH, Bitmap.Config.ARGB_8888)
}
override fun draw(canvas: Canvas) {
Timber.d("canvas: In draw")
canvas.setBitmap(bitmap)
canvas.apply {
drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
drawText("002098613", LABEL_SIDE_LENGTH - 50f, 50f, barcodeText)
drawCircle(200f, 200f, 100f, circlePainter)
}
Timber.d("canvas: $canvas")
}
override fun setAlpha(alpha: Int) {
}
override fun getOpacity(): Int {
return PixelFormat.OPAQUE
}
override fun setColorFilter(colorFilter: ColorFilter?) {
}
companion object {
const val LABEL_SIDE_LENGTH = 1160
}
}
class MyActivity: AppCompatActivity(){
private lateinit var customImgBitmap: Bitmap
override fun onCreate(savedInstanceState: Bundle?) {
customImgBitmap = createImgBitmap()
val test = POLabelGenerator
}
...
private fun createImgBitmap(): Bitmap {
return CustomImage.bitmap
}
}
Related
I'm building my first game in Android Studio. Right now, dots fall from the top of the screen down to the bottom. For some reason, in Layout Inspector the view of each dot is the entire screen even though the dots are comparatively small. This negatively affects the game since when a user presses anywhere on the screen, it deletes the most recently created dot rather than the one pressed. I want to get the dot's view to match the size of the actual dots without effecting other functionality.
Dot.kt
class Dot(context: Context, attrs: AttributeSet?, private var dotColor: Int, private var xPos: Int, private var yPos: Int) : View(context, attrs) {
private var isMatching: Boolean = false
private var dotIsPressed: Boolean = false
private var isDestroyed: Boolean = false
private lateinit var mHandler: Handler
private lateinit var runnable: Runnable
init {
this.isPressed = false
this.isDestroyed = false
mHandler = Handler()
runnable = object : Runnable {
override fun run() {
moveDown()
invalidate()
mHandler.postDelayed(this, 20)
}
}
val random = Random()
xPos = random.nextInt(context.resources.displayMetrics.widthPixels)
startFalling()
startDrawing()
}
// other methods
fun getDotColor() = dotColor
fun getXPos() = xPos
fun getYPos() = yPos
fun isMatching() = isMatching
fun setMatching(matching: Boolean) {
this.isMatching = matching
}
fun dotIsPressed() = dotIsPressed
override fun setPressed(pressed: Boolean) {
this.dotIsPressed = pressed
}
fun isDestroyed() = isDestroyed
fun setDestroyed(destroyed: Boolean) {
this.isDestroyed = destroyed
}
fun moveDown() {
// code to move the dot down the screen
yPos += 10
}
fun checkCollision(line: Line) {
// check if dot is colliding with line
// if yes, check if dot is matching or not
// update the dot state accordingly
}
fun startFalling() {
mHandler.post(runnable)
}
fun startDrawing() {
mHandler.postDelayed(object : Runnable {
override fun run() {
invalidate()
mHandler.postDelayed(this, 500)
}
}, 500)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
if (!isDestroyed) {
val paint = Paint().apply {
color = dotColor
}
canvas?.drawCircle(xPos.toFloat(), yPos.toFloat(), 30f, paint)
}
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private var score = 0
private lateinit var scoreCounter: TextView
private val dots = mutableListOf<Dot>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
createLine(Color.RED, 5000)
scoreCounter = TextView(this)
scoreCounter.text = score.toString()
scoreCounter.setTextColor(Color.WHITE)
val layout = findViewById<ConstraintLayout>(R.id.layout)
layout.setBackgroundColor(Color.BLACK)
val params = ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT
)
params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID
params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID
scoreCounter.layoutParams = params
layout.addView(scoreCounter)
val dotColors = intArrayOf(Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW)
val random = Random()
val handler = Handler()
val runnable = object : Runnable {
override fun run() {
val dotColor = dotColors[random.nextInt(dotColors.size)]
createAndAddDot(0, 0, dotColor)
handler.postDelayed(this, 500)
}
}
handler.post(runnable)
}
fun updateScore(increment: Int) {
score += increment
scoreCounter.text = score.toString()
}
fun createAndAddDot(x: Int, y: Int, color: Int) {
Log.d("Dot", "createAndAddDot called")
val dot = Dot(this, null, color, x, y)
val layout = findViewById<ConstraintLayout>(R.id.layout)
layout.addView(dot)
dots.add(dot)
dot.setOnTouchListener { view, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
val dotToRemove = dots.find { it == view }
dotToRemove?.let {
layout.removeView(it)
dots.remove(it)
updateScore(1)
view.performClick()
}
}
true
}
}
fun createLine(color: Int, interval: Int) {
Log.d("Line", "createLine called")
val line = Line(color, interval)
val lineView = Line.LineView(this, null, line)
val layout = findViewById<ConstraintLayout>(R.id.layout)
if (layout == null) {
throw IllegalStateException("Layout not found")
}
layout.addView(lineView)
val params = ConstraintLayout.LayoutParams(2000, 350)
lineView.layoutParams = params
params.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID
params.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID
params.bottomMargin = (0.1 * layout.height).toInt()
}
}
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Your view here -->
<View
android:id="#+id/view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- Guideline set to 10% from the bottom -->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/bottom_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.1" />
</androidx.constraintlayout.widget.ConstraintLayout>
I tried changing the view size with
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) val diameter = 40 // or any other desired diameter for the dots setMeasuredDimension(diameter, diameter) }
That made the view size a square stuck in the top left corner. As I played around with it, I could only get dots to show in that small window in the top corner rather than moving down the screen from different starting x-positions
Your custom view isn't a dot, it's a large display area that draws a dot somewhere inside it and animates its position. In onDraw you're drawing a circle at xPos (a random point on the screen width via displayMetrics.widthPixels) and yPos (an increasing value which moves the dot down the view).
There are two typical approaches to things like this:
use simple views like ImageViews. Let the containing Activity or Fragment add them to a container and control their position, maybe using the View Animation system. Handle player interaction by giving them click listeners and let the view system work out what's been clicked.
create a custom view that acts as the game area. Let that custom view control the game state (what dots exist, where they currently are) and draw that state in onDraw. Handle touch events on the view, and work out if those touches coincide with a dot (by comparing to the current game state).
What you're doing is sort of a combination of the two with none of the advantages that either approach gives on its own. You have multiple equally-sized "game field" views stacked on top of each other, so any clicks will be consumed by the top one, because you're clicking the entire view itself. And because your custom view fills the whole area, you can't move it around with basic view properties to control where the dot is - you have to write the logic to draw the view and animate its contents.
You could implement some code that handles the clicks and decides whether the view consumes it (because it intersects a dot) or passes it on to the next view in the stack, but that's a lot of work and you still have all your logic split between the Activity/Fragment and the custom view itself.
I think it would be way easier to just pick one approach - either use ImageViews sized to the dot you want and let the view system handle the interaction, or make a view that runs the game internally. Personally I'd go with the latter (you'll find it a lot easier to handle dots going out of bounds, get better performance, more control over the look and interaction etc, no need to cancel Runnables) but it's up to you!
I have checked my code several times and don't know where is it going wrong. i am trying to build a drawing app and after doing all this code I am unable to get any output. Not able to draw on the canvas.
please help me get rid of this issue.
This is my DrawingView.kt
package com.example.drawingapp
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.core.content.res.ResourcesCompat
class DrawingView(context : Context, attrs:AttributeSet): View(context, attrs){
private var myDrawPath: CustomPath? = null
private var myCanvasBitmap: Bitmap? = null
private var myDrawPaint: Paint? = null
private var myCanvasPaint: Paint? = null
private var myCanvas: Canvas?= null
private var myBrushSize :Float = 0.toFloat()
private var myColor = Color.BLACK
init{
drawingSetUp()
}
private fun drawingSetUp(){
myDrawPaint = Paint()
myDrawPath = CustomPath(myColor,myBrushSize)
myDrawPaint!!.color= myColor
myDrawPaint!!.style = Paint.Style.STROKE
myDrawPaint!!.strokeJoin =Paint.Join.ROUND
myDrawPaint!!.strokeCap =Paint.Cap.ROUND
myCanvasPaint= Paint(Paint.DITHER_FLAG)
myBrushSize=20.toFloat()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
myCanvasBitmap= Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
myCanvas = Canvas(myCanvasBitmap!!)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
myCanvas!!.drawBitmap(myCanvasBitmap!!, 0f,0f , myCanvasPaint)
if (!myDrawPath!!.isEmpty){
myDrawPaint!!.strokeWidth = myDrawPath!!.brushThickness
myDrawPaint!!.color= myDrawPath!!.color
myCanvas!!.drawPath(myDrawPath!!, myDrawPaint!!)
}
}
#SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
val touchX = event?.x
val touchY = event?.y
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
myDrawPath!!.color = myColor
myDrawPath!!.brushThickness = myBrushSize
myDrawPath!!.reset()
if (touchX != null) {
if (touchY != null) {
myDrawPath!!.moveTo(touchX, touchX)
}
}
}
MotionEvent.ACTION_MOVE -> {
if (touchX != null) {
if (touchY != null) {
myDrawPath!!.lineTo(touchX, touchY)
}
}
}
MotionEvent.ACTION_UP->{
myDrawPath = CustomPath(myColor, myBrushSize)
}
else->return false
}
invalidate()
return true
}
internal inner class CustomPath(var color:Int, var brushThickness:Float): Path(){
}
}
This is my activity_main.xml file
<?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">
<com.example.drawingapp.DrawingView
android:id="#+id/drawing_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
And this is my MainActivity.kt file
package com.example.drawingapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
You're drawing to the wrong Canvas. You need to draw to the one passed in to onDraw, that's the one linked to what your View will actually display.
When you do this:
myCanvasBitmap= Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
myCanvas = Canvas(myCanvasBitmap!!)
you're creating your own, personal Bitmap, and creating your own personal Canvas to draw stuff on that bitmap. You can do that - but unless you draw that bitmap to the canvas onDraw gives you at some point, you'll never see it.
I'm assuming you wanted to do this:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// draw your stored bitmap to the view's canvas
canvas.drawBitmap(myCanvasBitmap!!, 0f,0f , myCanvasPaint)
if (!myDrawPath!!.isEmpty){
myDrawPaint!!.strokeWidth = myDrawPath!!.brushThickness
myDrawPaint!!.color= myDrawPath!!.color
// draw this stored path to the view's canvas, on top of
// the stored bitmap you just drew
canvas.drawPath(myDrawPath!!, myDrawPaint!!)
}
}
i.e. drawing to canvas and not myCanvas. I don't know where you're actually drawing stuff to myCanvas though (to build up your stored image), you still need to do that so you have something to paint on the view's canvas. I'm assuming that happens elsewhere and the path you're drawing in onDraw is like a temporary overlay that isn't part of the stored image yet
Also just as a piece of advice, you need to avoid that !! stuff everywhere - that's a big sign you've made something nullable that shouldn't be nullable, and now you have to fight with the null-checking system because you're sure it can't be null but the system thinks it could be.
If you just move all your drawingSetup code into init, it'll see that you're assigning everything a value and you won't need to make them null. It's because you're assigning them through another function called from init that it can't be sure you're doing that - it's a limitation of the language unfortunately. Or you could just assign them directly:
private var myDrawPath = CustomPath(myColor,myBrushSize)
private lateinit var myCanvasBitmap: Bitmap
private var myDrawPaint = Paint().apply {
color= myColor
style = Paint.Style.STROKE
strokeJoin = Paint.Join.ROUND
strokeCap = Paint.Cap.ROUND
}
// etc
If you do want to keep that initialisation function (e.g. because you want to reset everything to normal) at least make your vars lateinit non-nullable types - if they're never going to be null when they're accessed, don't make them nullable! You just have to make sure lateinit stuff is assigned a value before something tries to read it - but the same is true for making something null and doing !! every time you access it, aka "trust me bro it's not null"
I'm writing a screenshot app using Android MediaProjection Api in which an overlay button is shown on top of everything and user can click it to capture a screenshot anywhere. Since MediaProjection records screen content, overlay button itself is in captured screenshots. To hide the button when capturing screenshot, I tried to set view visibility to INVISIBLE, take screenshot and revert it back to VISIBLE but since changing visibility is an async operation in Android, sometimes overlay button is still present in recorded shots.
I Changed to below snippet and it worked in my experiments:
floatingButton?.setOnClickListener { view ->
view.visibility = View.INVISIBLE
view.postDelayed(100) {
takeShot()
view.post {view.visibility = View.VISIBLE}
}
}
But it's basically saying I feeling good that in 100ms, button would be invisible. It's not a good solution and in the case of videos, in 100ms content could be very different from what user actually saw at that moment.
Android doesn't provide a onVisibiltyChangedListener kind of thing, so how could I perform a task after ensuring that a view visibility has changed?
Edit 1
Here's the takeShot() method:
private fun takeShot() {
val image = imageReader.acquireLatestImage()
val bitmap = image?.run {
val planes = image.planes
val buffer: ByteBuffer = planes[0].buffer
val pixelStride = planes[0].pixelStride
val rowStride = planes[0].rowStride
val rowPadding = rowStride - pixelStride * width
val bitmap = Bitmap.createBitmap(
width + rowPadding / pixelStride,
height,
Bitmap.Config.ARGB_8888
)
bitmap.copyPixelsFromBuffer(buffer)
image.close()
bitmap
}
bitmap?.let{
serviceScope.launch {
gallery.store(it)
}
}
}
The codes are inside of a foreground service and when user accepts media projection, I create ImageReader and VirtualDisplay:
imageReader = ImageReader.newInstance(size.width, size.height, PixelFormat.RGBA_8888, 2)
virtualDisplay = mediaProjection.createVirtualDisplay(
"screen-mirror",
size.width,
size.height,
Resources.getSystem().displayMetrics.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, // TODO: DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC ??
imageReader.surface, null, null
)
mediaProjection.registerCallback(object : MediaProjection.Callback() {
override fun onStop() {
virtualDisplay.release()
mediaProjection.unregisterCallback(this)
}
}, null)
I've tried without suspension and coroutine stuff and result was the same, so they most likely are irrelevant to problem.
Seems my problem is related to MediaProjection and that would be a separate question, but this question itself is relevant.
I ended up using this (almost copy-pasting core-ktx code for doOnPreDraw()). Pay attention that:
This doesn't work for View.INVISIBLE, because INVISIBLE doesn't trigger a "layout"
I don't endorse this, since it's GLOBAL, meaning that every visibility change related to "someView" view hierarchy, will call the onGlobalLayout method (and therefore your action/runnable).
I save the accepted answer for a better solution.
// usage
// someView.doOnVisibilityChange(become = View.GONE) {
// someView is GONE, do stuff here
// }
inline fun View.doOnVisibilityChange(become: Int, crossinline action: (view: View) -> Unit) {
OneShotVisibilityChangeListener(this) { action(this) }
visibility = newVisibility
}
class OneShotVisibilityChangeListener(
private val view: View,
private val runnable: Runnable
) : ViewTreeObserver.OnGlobalLayoutListener, View.OnAttachStateChangeListener {
private var viewTreeObserver: ViewTreeObserver
init {
viewTreeObserver = view.viewTreeObserver
viewTreeObserver.addOnGlobalLayoutListener(this)
view.addOnAttachStateChangeListener(this)
}
override fun onGlobalLayout() {
removeListener()
runnable.run()
}
private fun removeListener() {
if (viewTreeObserver.isAlive) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
} else {
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
view.removeOnAttachStateChangeListener(this)
}
override fun onViewAttachedToWindow(v: View) {
viewTreeObserver = v.viewTreeObserver
}
override fun onViewDetachedFromWindow(v: View) {
removeListener()
}
}
I am working on a paint tool for android and have attempted to implement a 'fill with color' tool. Where a user is able to fill a particular area with color depending on what they have selected. I have found examples of Flood Fill for Java but cant see anything similar for Kotlin.
I have tried the following - https://developer.android.com/reference/kotlin/android/graphics/Path.FillType
But have not had too much luck since it instead uses the surrounding colour and not what colour the user has selected.
EDIT:
A snippet of the code I am working with:
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>()
private val mRedoPaths = ArrayList<CustomPath>()
private var counterUndo = 0
init {
setUpDrawing()
}
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)
}
//Change Canvas to Canvas? if fails
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 setFill(){
mDrawPaint!!.style = Paint.Style.FILL
}
}
This is kind of a complicated subject - if you want to draw a Path with a particular fill and colour, you need to create a Paint and use setColor (and setStyle to make it Paint.Style.FILL.
But Paths are for vector graphics, they're all defined lines and curves. Unless you're creating the shapes that the user can fill in as paths in the first place, you'll have trouble defining one to fit an arbitrary area.
I'm guessing you're actually using bitmaps, like a normal paint program, and you want to tap on a pixel and have that change colour, and also recursively change the colour of surrounding pixels if they meet a certain threshold. So you'll have to get the Canvas's Bitmap, and work out how to move through the pixels, changing them as you go (e.g. with Bitmap.setPixel())
There are a lot of algorithms for doing this, you'll just need to pick one and implement it. You probably don't want to use Paths to do it though!
Nothing is being drawn, even tho function is called.
I tried:
1. Moving super.onDraw(canvas) to beginning and end of onDraw
2. Replacing it with draw(canvas)
3. Creating my view dynamically and statically (I mean, in XML)
4. Adding setWillNotDraw(false)
class GameView(context: Context) : View(context) {
init {
setWillNotDraw(false) // doesn't change anything
setBackgroundColor( Color.DKGRAY ) // this actually works, but onDraw is not, why?
}
private val ballPaint = Paint(ANTI_ALIAS_FLAG).apply {
color = 0xfefefe
style = Paint.Style.FILL
}
override fun onDraw(canvas: Canvas?) {
println("Test") // printed!
canvas!!.apply {
drawCircle((width/2).toFloat(), (height/2).toFloat(), 100.0f, ballPaint )
}
super.onDraw(canvas)
}
}
What am I doing wrong?
Thanks to pskink, that was the problem
color = 0xfffefefe.toInt()