2

I'm trying to simulate a TorchView with a background image and a foreground image. It works well on API 27 and below, but draw a rectangle on API 28.

Any idea why it doesn't work on Android Pie?

On API 27 and below API 28
API 27 and below API 28

Torch View class:

class TorchView : View, OnTouchListener {

    var mBitmapBackground: Bitmap? = null
    var mBitmapForeground: Bitmap? = null
    var mMask: Bitmap? = null
    private var mPosX = 0f
    private var mPosY = 0f

    private lateinit var paintMask: Paint
    private lateinit var paintBackground: Paint
    private lateinit var paintForeground: Paint

    private var radius = 150

    constructor(context: Context) : super(context) {
        init()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init()
    }

    fun initBitmaps(bitmapBackground: Bitmap, bitmapForeground: Bitmap, radius: Int){
        this.radius = radius
        mBitmapBackground = bitmapBackground
        mBitmapForeground = bitmapForeground
        mMask = makeRadGrad()
        mPosX = (bitmapBackground.width/2 - radius).toFloat()
        mPosY = (bitmapBackground.height/2 - radius).toFloat()
        invalidate()
    }

    fun init() {
        paintBackground = Paint()

        paintMask = Paint()
        paintMask.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)

        paintForeground = Paint()
        paintForeground.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER)

        isFocusable = true
        isFocusableInTouchMode = true
        this.setOnTouchListener(this)
    }

    public override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val mask = mMask
        val bitmapForeground = mBitmapBackground
        val bitmapBackground = mBitmapForeground
        if(mask != null && bitmapForeground != null && bitmapBackground != null){
            canvas.save()
            canvas.drawBitmap(bitmapBackground, 0f, 0f, paintBackground)
            canvas.drawBitmap(mask, mPosX, mPosY, paintMask)
            canvas.drawBitmap(bitmapForeground, 0f, 0f, paintForeground)
            canvas.restore()
        }
    }

    private fun makeRadGrad(): Bitmap {
        val gradient = RadialGradient(
            radius.toFloat(), radius.toFloat(), radius.toFloat(), -0xff0100,
            0x00000000, android.graphics.Shader.TileMode.CLAMP
        )
        val p = Paint()
        p.isDither = true
        p.shader = gradient

        val bitmap = Bitmap.createBitmap(radius*2, radius*2, Config.ARGB_8888)
        val c = Canvas(bitmap)
        c.drawCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), p)

        return bitmap
    }

    override fun onTouch(v: View?, event: MotionEvent): Boolean {
        mPosX = event.x - radius
        mPosY = event.y - radius
        invalidate()
        return true
    }
}

3 Answers 3

1

check this https://issuetracker.google.com/issues/111819103

try this

 public override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    val mask = mMask
    val bitmapForeground = mBitmapBackground
    val bitmapBackground = mBitmapForeground
    if(mask != null && bitmapForeground != null && bitmapBackground != null){
        canvas.save()
        canvas.drawBitmap(bitmapBackground, 0f, 0f, paintBackground)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            makeRadGradP(canvas, bitmapForeground)
        } else {
            canvas.drawBitmap(mask, mPosX, mPosY, paintMask)
            canvas.drawBitmap(bitmapForeground, 0f, 0f, paintForeground)
         }
        canvas.restore()
    }
}

private fun makeRadGradP(canvas: Canvas, bm: Bitmap) {
        val paint = Paint()
        paint.style = Paint.Style.FILL
        val shader = BitmapShader(bm, TileMode.CLAMP, TileMode.CLAMP)
        paint.shader = shader
        val corners = Path()
        corners.addCircle(mPosX + radius, mPosY + radius, radius.toFloat(), Path.Direction.CW)
        canvas.drawPath(corners, paint)

        val gradient = RadialGradient(
                mPosX + radius, mPosY + radius, radius.toFloat(), 0x00000000,
                Color.parseColor("#df000000"), TileMode.CLAMP
        )
        val p = Paint()
        p.isDither = true
        p.shader = gradient
        canvas.drawCircle(mPosX + radius, mPosY + radius, radius.toFloat(), p)
 }
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot, this is exactly what I needed. Thanks for linking the issue tracker.
1

According to the Official Document

PorterDuff was deprecated in Android API 28

You should replace PorterDuff with BlendMode

enter image description here

Maybe you should do some compatible work on Android API 28

2 Comments

Android API 28 is Pie not Q, Q is still in preview :/
Oh, it seems i made a mistake.
-1

class:

class TorchView : View, OnTouchListener {

    var mBitmapBackground: Bitmap? = null
    var mBitmapForeground: Bitmap? = null
    var mMask: Bitmap? = null
    private var mPosX = 0f
    private var mPosY = 0f

    private lateinit var paintMask: Paint
    private lateinit var paintBackground: Paint
    private lateinit var paintForeground: Paint

    private var radius = 150

    constructor(context: Context) : super(context) {
        init()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init()
    }

    fun initBitmaps(bitmapBackground: Bitmap, bitmapForeground: Bitmap, radius: Int){
        this.radius = radius
        mBitmapBackground = bitmapBackground
        mBitmapForeground = bitmapForeground
        mMask = makeRadGrad()
        mPosX = (bitmapBackground.width/2 - radius).toFloat()
        mPosY = (bitmapBackground.height/2 - radius).toFloat()
        invalidate()
    }

    fun init() {
        paintBackground = Paint()

        paintMask = Paint()
        paintMask.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)

        paintForeground = Paint()
        paintForeground.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER)

        isFocusable = true
        isFocusableInTouchMode = true
        this.setOnTouchListener(this)
    }

    public override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val mask = mMask
        val bitmapForeground = mBitmapBackground
        val bitmapBackground = mBitmapForeground
        if(mask != null && bitmapForeground != null && bitmapBackground != null){
            canvas.save()
            canvas.drawBitmap(bitmapBackground, 0f, 0f, paintBackground)
            canvas.drawBitmap(mask, mPosX, mPosY, paintMask)
            canvas.drawBitmap(bitmapForeground, 0f, 0f, paintForeground)
            canvas.restore()
        }
    }

    private fun makeRadGrad(): Bitmap {
        val gradient = RadialGradient(
            radius.toFloat(), radius.toFloat(), radius.toFloat(), -0xff0100,
            0x00000000, android.graphics.Shader.TileMode.CLAMP
        )
        val p = Paint()
        p.isDither = true
        p.shader = gradient

        val bitmap = Bitmap.createBitmap(radius*2, radius*2, Config.ARGB_8888)
        val c = Canvas(bitmap)
        c.drawCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), p)

        return bitmap
    }

    override fun onTouch(v: View?, event: MotionEvent): Boolean {
        mPosX = event.x - radius
        mPosY = event.y - radius
        invalidate()
        return true
    }
}

1 Comment

Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.