Slide 1

Slide 1 text

外部SDKのViewに マスク処理をする方法と罠 2019/05/15 potatotips #61 Keita Kagurazaka

Slide 2

Slide 2 text

AWAについて ● 定額制音楽ストリーミングサービス ● 歌詞表示機能あり

Slide 3

Slide 3 text

やりたいこと ● 歌詞表示にグラデーションマスクを適用したい!

Slide 4

Slide 4 text

やりたいこと ● 歌詞表示にグラデーションマスクを適用したい! ただし、歌詞表示Viewは 外部SDKのもので 手をいれることができない!

Slide 5

Slide 5 text

ViewGroup#dispatchDraw class MaskLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr) { override fun dispatchDraw(canvas: Canvas) { // canvasにchild viewを描画する super.dispatchDraw(canvas) } }

Slide 6

Slide 6 text

ViewGroup#dispatchDraw class MaskLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr) { override fun dispatchDraw(canvas: Canvas) { // canvasにchild viewを描画する super.dispatchDraw(canvas) } } ここで単にcanvasにマスク処理する のはNG 後ろのViewはcanvasにすでに描画さ れているため

Slide 7

Slide 7 text

オフスクリーンバッファ private var canvasBitmap: Bitmap? = null private var canvas: Canvas? = null override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { // ViewGroupと同じ大きさのBitmapとCanvasを用意 canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) .also { canvas = Canvas(it) } }

Slide 8

Slide 8 text

オフスクリーンバッファ override fun dispatchDraw(canvas: Canvas) { val offScreenBitmap = canvasBitmap ?: return super.dispatchDraw(canvas) val offScreenBuffer = this.canvas ?: return super.dispatchDraw(canvas) // オフスクリーンバッファをクリア offScreenBuffer.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) // オフスクリーンバッファに子Viewを描画 super.dispatchDraw(offScreenBuffer) // オフスクリーンバッファの内容を渡ってきたcanvasに描画 canvas.drawBitmap(offScreenBitmap, 0f, 0f, null) }

Slide 9

Slide 9 text

オフスクリーンバッファ override fun dispatchDraw(canvas: Canvas) { val offScreenBitmap = canvasBitmap ?: return super.dispatchDraw(canvas) val offScreenBuffer = this.canvas ?: return super.dispatchDraw(canvas) // オフスクリーンバッファをクリア offScreenBuffer.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) // オフスクリーンバッファに子Viewを描画 super.dispatchDraw(offScreenBuffer) // オフスクリーンバッファの内容を渡ってきたcanvasに描画 canvas.drawBitmap(offScreenBitmap, 0f, 0f, null) } ここでoffScreenBitmapを加工すれば OK!

Slide 10

Slide 10 text

グラデーションマスク処理 private var maskPaint: Paint? = null override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { maskPaint = Paint().apply { shader = LinearGradient( 0f, 0f, 0f, h.toFloat(), intArrayOf(Color.TRANSPARENT, Color.WHITE, Color.WHITE, Color.TRANSPARENT), floatArrayOf(0f, 0.15f, 0.85f, 1f), Shader.TileMode.CLAMP ) xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN) } }

Slide 11

Slide 11 text

グラデーションマスク処理 override fun dispatchDraw(canvas: Canvas) { val offScreenBitmap = canvasBitmap ?: return super.dispatchDraw(canvas) val offScreenBuffer = this.canvas ?: return super.dispatchDraw(canvas) val mask = maskPaint ?: return super.dispatchDraw(canvas) offScreenBuffer.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) super.dispatchDraw(offScreenBuffer) offScreenBuffer.drawRect(0f, 0f, offScreenBitmap.width.toFloat(), offScreenBitmap.height.toFloat(), mask ) canvas.drawBitmap(offScreenBitmap, 0f, 0f, null) }

Slide 12

Slide 12 text

罠 ● 対象のViewがエフェクトを持っている場合は気をつけよう

Slide 13

Slide 13 text

罠 ● 対象のViewがエフェクトを持っている場合は気をつけよう OverScrollエフェクトがアルファをもっ ているので、このアルファでマスクがか かってしまう!

Slide 14

Slide 14 text

まとめ ● ViewGroup#dispatchDrawで子Viewの描画をフックすれば子 Viewに自由自在にエフェクトがかけられる ● オフスクリーンバッファを使うことを忘れずに ● 子View自体がエフェクトを持っている場合に注意 ● https://github.com/k-kagurazaka/sample-mask-sdk-view

Slide 15

Slide 15 text

Thanks!