ペイントAPI - XfermodeとPorterDuffの詳しい解説 (4)


このセクションの紹介:

前のセクションでは、Xfermode と PorterDuff を使用する最初の例、つまり丸くて円形の画像 ImageView の実装について書きました。 PorterDuff.Mode.DST_IN がもたらした利点を認識しました。このセクションでは、引き続き練習用の例を書いていきます。 8.3.2 描画実践例 がもたらした、美少女の服を脱がす実装をまだ覚えていますか?

1.gif

当時の実装計画では、指のタッチ領域付近の 20*20 ピクセルを透明に設定することでした。レンダリングは次のようになります。

2.gif

問題に気づいたかどうかはわかりません。女性の服は拭くときはすべて正方形ですが、画板を描くときは 絵を描くときに線がとても滑らかになるのですが、この2つを組み合わせて服を拭くときにも滑らかになる方法はありますか? 答えは間違いなく「はい」です。Xfermode を使用してください。このセクションでは、別のモード、DST_OUT モードを使用します。 バラバラな場所にターゲット画像を描画します

3.png

特定のモードを忘れた場合、または 18 のモードすら見ていない場合は、次のページに移動してください: Android 基本入門チュートリアル - 8.3.5 Paint API - Xfermode PorterDuff の詳細説明 (2)さらに、PorterDuff.Mode のレンダリングも投稿したいと思います:

4.png

さて、早速、このセクションを始めましょう~


1 実現されるレンダリングと実装プロセスの分析:

実現予定のレンダリング:

5.gif

7.gif さて、その Gif を何回見たか分かりません。この写真がみんなの好みに合うかどうかはわかりません、Xiaozhu 他の人のアプリから盗まれたものです。電話番号やメールアドレスは聞かないでください。何かを探しています。 グループのベテランドライバーに聞いてください - Jishen、わかりました、導入プロセスを分析しましょう~

  • 実際には 2 つのビットマップがあり、前にあるものは服を着ており、後ろにあるものは服を脱いでいます。 次に、パスを使用してユーザーが描いたグラフィックを記録し、ブラシの DST_OUT モードを設定します。 服を着た画像であるPathと重なったDST(対象画像)が透明になります!わかりました、それは簡単です! ゆっくり磨いていきましょう!
  • まず、前面と背面の画像を保存するために 2 つのビットマップが必要です。ここでは両方のビットマップを全画面表示にします。
  • 次に、ブラシ、角丸、ペンの幅、アンチエイリアスなどを設定します。
  • 次に、描画パスを定義します。これは、ユーザーが領域を描画するためのメソッドであり、Xfermode を設定した後に領域を描画するだけです。
  • 次に、onTouchEventメソッドを書き換えます。この部分は前のカスタム描画ボードと同じです。
  • 最後に onDraw() メソッドを書き直します。最初に背景画像を描画し、領域を描画するユーザーのメソッドを呼び出してから、前景画像を描画します。

少し複雑に見えるかもしれませんが、実際はそうではありません。コードは非常にシンプルです~


2. コードの実装:

単なるカスタム View—StripMeiZi.java

/**
 * Created by Jay on 2015/10/25 0025.
 */
public class StripMeiZi extends View{

    private Paint mPaint = new Paint();
    private Path mPath = new Path();
    private Canvas mCanvas;
    private Bitmap mBeforeBitmap;
    private Bitmap mBackBitmap;
    private int mLastX,mLastY;
    private int screenW, screenH; //屏幕宽高
    private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);


    public StripMeiZi(Context context) {
        this(context, null);
    }

    public StripMeiZi(Context context, AttributeSet attrs) {
        super(context, attrs);
        screenW = ScreenUtil.getScreenW(context);
        screenH = ScreenUtil.getScreenH(context);
        init();
    }


    public StripMeiZi(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init() {
        //背后图片,这里让它全屏
        mBackBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.meizi_back);
        mBackBitmap = Bitmap.createScaledBitmap(mBackBitmap, screenW, screenH, false);
        //前面的图片,并绘制到Canvas上
        mBeforeBitmap = Bitmap.createBitmap(screenW, screenH, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBeforeBitmap);
        mCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(),
                R.mipmap.meizi_before), null, new RectF(0, 0, screenW, screenH), null);
        //画笔相关的设置
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND); // 圆角
        mPaint.setStrokeCap(Paint.Cap.ROUND); // 圆角
        mPaint.setStrokeWidth(80);    // 设置画笔宽
    }

    private void drawPath() {
        mPaint.setXfermode(mXfermode);
        mCanvas.drawPath(mPath, mPaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBackBitmap, 0, 0, null);
        drawPath();
        canvas.drawBitmap(mBeforeBitmap, 0, 0, null);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action)
        {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(mLastX, mLastY);
                break;
            case MotionEvent.ACTION_MOVE:

                int dx = Math.abs(x - mLastX);
                int dy = Math.abs(y - mLastY);

                if (dx > 3 || dy > 3)
                    mPath.lineTo(x, y);

                mLastX = x;
                mLastY = y;
                break;
        }
        invalidate();
        return true;
    }
}

レイアウト コード activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.jay.xfermodedemo2.StripMeiZi
        android:layout_width="match_parent"
        android:layout_height="match_parent"/></RelativeLayout>

3. コードサンプルのダウンロード:

XfermodeDemo2.zip


このセクションの概要:

さて、このセクションでは、Xfermode と PorterDuff の別の実践例を書きました。美容服のデモ(以前と比較) 美しい女性の服を剥がす方法 (タッチポイント付近の 20*20 ピクセルを透明にする) の方がはるかにエレガントです~ コードもはるかに単純です。 Android 画像シャッフル Xfermode がもたらす利点、またはカスタム コントロールの重要性を実感してください。さて、何を待っていますか? IDE を開いてコードを実行し、美しい女性の服を引き裂く快感を味わってください~

6.gif