PorterDuffXfermode とは
PorterDuffXfermodeとはビットマップの合成方法を決めるものです。ベースとなるクラスはXfermodeですので、自分で好みの合成方法を作ることも出来ます。
Canvas
にはPath
やRectF
、Bitmap
などを用いて図形や画像を描画することができます。Path
やRectF
などは形の情報しかもっていないため、これらをCanvas
にかきこむ時にはPaint
を渡して、どのような色で描画するかを指定します。Xfermode
はこのPaint
に対して指定します。Paint
がXfermode
を持っていると、すでにCanvas
に描画されている内容と、新しく描画する内容とを比較して、どのように色を合成するかを決めてくれるようになります。
PorterDuffXfermode
は、PorterさんとDuffさんが決めた合成ルールを実装したクラスで、12種類のモードが用意されています。色が重なる部分について、新しく描画する色を優先するか、既に描画された色を優先するか、混ぜるか、などのモードがあります。
Viewの重ねあわせとPorterDuffXfermode
View
の描画も突き詰めればCanvas
の仕事です。
View
の重ね合わせは後に追加したView
が上に来るようになっています。何も考えなければ、下に配置されるView
は上に来るView
に隠れて見えなくなりますが、上に来るView
がアルファ値の指定で半透明になっていたり、背景のDrawable
が半透明だったりすると、下にあるView
が透けて見えるようになります。
ここで、重ね合わせるView
でPorterDuffXfermode
を使った合成ルールをView#onDraw
やView#dispatchDraw
などで指定すると、半透明なView
を重ね合わせた時の見え方を変えることが出来ます。
例えば、PorterDuff.Mode.CLEAR
モードを指定したPorderDuffXfermode
を使った次のView
を使うと、その下に配置されるView
を消すことが出来ます。
package hoge.fuga;
public class ClearMaskView extends View {
private Paint mPorterDuffPaint;
private RectF mRect;
// コンストラクタから呼ぶ
private void init() {
mPorterDuffPaint = new Paint();
mPorterDuffPaint.setColor(Color.TRANSPARENT);
mPorterDuffPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (mRect == null)
mRect = new RectF(0, 0, getWidth(), getHeight());
canvas.drawRect(mRect, mPorterDuffPaint);
super.dispatchDraw(canvas);
}
}
半透明なViewの重ね合わせとvisibilityプロパティの変更
半透明なView
を上に重ねる時には、下にあるView
を隠したくなることがあります(半透明だと下が透けて見えてしまい目障りに感じるため)。
このとき、上に重ねるView
を表示したら、その下にくるはずのView
をView#setVisibility
で不可視にする方法を取ると、上に重ねるView
が大きく、かつその下にくるView
の数が多くなると、ひたすら下に来るはずのすべてのView
を不可視にしなければならなくなります。また、上に重ねるView
を削除ないし不可視にしたら、下にいたView
を可視状態に戻す必要があります。つまり、可視・不可視をコントロールすべきView
の数だけ面倒臭さが増えます。
半透明なViewの重ね合わせとPorterDuffXfermode
そんなときに先に上げた例のClearMaskView
を使うと、上に重ねるView
の可視・不可視のみを明示的にハンドリングすればよくなります。
例えば、次のような180dp四方の領域に"はろー"と書かれたView
を重ねるcomplex_layout.xml
を考えてみます。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- なんかいろいろたくさん View がある -->
</FrameLayout>
<FrameLayout
android:id="@+id/overlay_views"
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_gravity="center">
<hoge.fuga.ClearMaskView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#33000000"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="はろー"/>
</FrameLayout>
</FrameLayout>
このレイアウトでは、はろーと書かれた領域は半透明な背景を持っていますが、一旦ClearMaskView
が下に描画されるView
をPorterDuffXfermode
で消しているので、下にあるはずのいろいろなView
は見えません。
これで、いちいち細かく可視・不可視のコントロールをする必要性から開放されました。