Introduction à cette section :

Dans la section précédente, nous avons écrit le premier exemple d'utilisation de Xfermode et PorterDuff : l'implémentation d'une ImageView d'image arrondie et circulaire. Nous avons constaté les avantages que PorterDuff.Mode.DST_IN nous apporte. Dans cette section, nous continuons à écrire des exemples pour nous entraîner. Vous souvenez-vous encore8.3.2 Exemple pratique de la classe Drawingl'implémentation qui consiste à retirer les vêtements de la belle que nous vous avons montrée ?

À ce moment-là, notre implémentation consistait à rendre transparents les pixels 20*20 près de la zone touchée par le doigt. L'image de rendu était comme ceci :

Je ne sais pas si vous avez remarqué un problème : lorsque nous effaçons les vêtements de la belle, les zones effacées sont toutes carrées, mais lorsque l'on dessine sur le tableau de dessin, les lignes sont très fluides. Existe-t-il un moyen de combiner les deux pour que, lorsque nous effaçons les vêtements, ce soit aussi fluide ? La réponse est certainement oui, il suffit d'utiliser Xfermode ! Dans cette section, nous utilisons un autre mode,DST_OUTmode ! Dessinez l'image de destination dans les zones sans intersection

Si vous avez oublié un certain mode ou n'avez même jamais vu les 18 modes, veuillez vous rendre à :Tutoriel Android pour débutants - 8.3.5 API Paint - Explication détaillée de Xfermode et PorterDuff (II)De plus, nous devons encore publier le diagramme de rendu de PorterDuff.Mode :

Bon, sans plus attendre, commençons le contenu de cette section~


1. Le rendu à obtenir et l'analyse du processus d'implémentation :

Le rendu à obtenir:

Eh bien, je ne sais pas combien de fois vous avez regardé ce GIF. Je me demande si l'image plaît à tout le monde. Le petit cochon a été récupéré depuis l'application de quelqu'un d'autre. Ne me demandez pas le numéro de code, ne laissez pas votre e-mail ni rien ; je ne sais rien~ Pour trouver le code, demandez au vieux pilote du groupe - Ji Shen. Très bien, analysons le processus d'implémentation~

  • Parlons du principe. En fait, il y a deux Bitmaps, un devant et un derrière. Celui de devant porte des vêtements, et celui de derrière n'en porte pas. Ensuite, un Path est utilisé pour enregistrer le graphique dessiné par l'utilisateur, puis on définit le mode DST_OUT pour notre pinceau. Ensuite, la partie du DST (image de destination) qui chevauche le Path, c'est-à-dire l'image qui porte des vêtements, deviendra transparente ! D'accord, très simple ! Affinons-le lentement !
  • Tout d'abord, nous avons besoin de deux Bitmaps pour stocker les images de devant et de derrière. Ici, nous faisons en sorte que les deux Bitmaps occupent tout l'écran !
  • Ensuite, configurez le pinceau : coins arrondis, largeur de trait, anticrénelage, etc. !
  • Définissez ensuite une méthode pour dessiner le Path, c'est-à-dire la zone dessinée par l'utilisateur. Après avoir défini Xfermode, il suffit de dessiner la zone !
  • Redéfinissez ensuite la méthode onTouchEvent. Cette partie est identique au précédent tableau de dessin personnalisé !
  • Enfin, redéfinissez la méthode onDraw() : dessinez d'abord l'image d'arrière-plan, appelez la méthode pour dessiner la zone dessinée par l'utilisateur, puis dessinez l'image de premier plan !

Cela peut sembler un peu compliqué, mais en fait ça ne l'est pas. Le code est super simple~


2. Implémentation du code :

Il y a directement une seule View personnalisée -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;
    }
}

Code de mise en pageactivity_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. Téléchargement de l'exemple de code :

XfermodeDemo2.zip


Résumé de la section :

Bon, dans cette section, nous avons écrit un autre exemple pratique de Xfermode et PorterDuff—la démo de l'arrachage des vêtements de la belle à la main. Comparé à notre manière précédente d'arracher les vêtements de la belle (rendre transparents les pixels 20*20 près du point de contact), c'est bien plus délicat~ Le code est aussi bien plus simple, non ? Avez-vous expérimenté les avantages que nous apporte Xfermode pour la composition d'images Android, ou son importance pour les contrôles personnalisés ? Eh bien, qu'attendez-vous ? Ouvrez votre IDE, parcourez le code et goûtez au plaisir d'arracher les vêtements de la belle à la main~