Paint API - Penjelasan terperinci tentang Xfermode dan PorterDuff (3)


Pengenalan kepada bahagian ini:

Dalam bahagian sebelumnya, kami mengetahui tentang anak lelaki ketiga dalam Xfermode: PorterDuff Terdapat satu parameter dalam kaedah pembinaan Xfermode: PorterDuff .Mod , selepas menonton 16 mod pencampuran imej, kami menulis kod kami sendiri untuk mengesahkan dokumen 18 mod campuran berbeza, 18 daripadanya ialah mod ADD dan OVERLAY yang baru ditambah! Sudah tentu, tidak cukup dengan hanya mengesahkan bahawa anda tahu; Dalam bahagian ini, kami akan menulis contoh untuk membantu kami membiasakan diri dengan cara menggunakan PorterDuff.Mode untuk menyediakan kami dengannya dalam amalan. Mod shuffle ini! Contoh yang dibawa dalam bahagian ini ialah: pelaksanaan grafik bulat & bucu bulat!

Dalam 2.3.4 ImageView (paparan imej) kami akhirnya menerangkan yang paling mudah Pelaksanaan melukis ImageView bulat adalah untuk memanggil clipPath pada gambar untuk memotong bulatan!

Bahagian ini dilaksanakan menggunakan mod DST_IN ​​​​dalam Mod PorterDuff Tanpa berlengah lagi, mari mulakan bahagian ini! PS: Contoh dalam bahagian ini diambil daripada kerja Hongyang - Android Xfermode sebenarnya melaksanakan gambar sudut bulat dan bulat Selain itu, saya masih perlu menyiarkan pemaparan PorterDuff.Mode:

1.png


1. Gambarajah kesan yang perlu direalisasikan dan analisis proses pelaksanaan:

Rajah rendering selepas operasi:

2.png

Nah, perkara di atas adalah kesan yang ingin kami capai, melalui mod PorterDuff.Mode.DST_IN ​​​​ini! Mari analisa proses pelaksanaan:

  • Langkah 1: Xfermode tidak lebih daripada dua lapisan graf Yang dilukis pertama dipanggil graf DST (graf sasaran), dan yang dilukis kemudian dipanggil graf SRC (graf asal). Untuk bulatan atau sudut bulat, kita boleh melukis imej untuk dipaparkan (DST) dahulu, dan di sini kita menetapkannya melalui atribut src; Kemudian lukis bulatan dan fillet (SRC) Bahagian yang ingin kami paparkan ialah tempat ia bersilang, dan ia adalah kandungan bahagian gambar. Jadi pilih: mod DST_IN ​​​​!
  • Langkah 2: Sekarang kita tahu prinsipnya, seterusnya kita perlu mempertimbangkan isu yang berkaitan dengan menyesuaikan ImageView:
  • Jika Paparan yang ingin kita lukis adalah bulat atau bulat, kita perlu menambah atribut untuk menilai dan sudut bulat Anda juga memerlukan jejari fillet Parameter, jadi kami boleh menggunakan atribut tersuai (attrs.xml), dan kemudian menyesuaikan kaedah pembinaan View untuk Keluarkan parameter ini!
  • Kemudian datang pengiraan saiz imej: Pertama sekaliJika kita menetapkan bulatan, kita perlu membuat lebar dan tinggi konsisten, dan nilai minimum akan kita lakukan ini dalam kaedah onMesure(). Panggil getMeasuredXxx() untuk mendapatkan lebar dan tinggi, lihat mana yang lebih kecil, panggil setMeasuredDimension(x, x); Kemudian, kita mendapat lebar dan tinggi imej dalam kaedah onDraw(), dan kemudian mengira nisbah penskalaan mengikut lebar dan tinggi imej, serta lebar dan tinggi Lihat, Jika lebar dan tinggi gambar tidak sepadan dengan lebar dan tinggi Paparan, lebar dan tinggi gambar yang terhasil mestilah lebih besar daripada lebar dan tinggi Paparan, jadi, ambil nilai yang lebih besar!
  • Kemudian datang ke lukisan gambar. Kemudian selepas memulakan berus, tetapkan setXfermode kepada PorterDuff.Mode.DST_IN, mula-mula lukis gambar, kemudian lukis grafik
  • Akhir sekali ialah beberapa perkara untuk caching imej WeakReference digunakan di sini untuk cache imej untuk mengelakkan memperuntukkan memori setiap kali onDraw Dan lukis semula, akhirnya kosongkan cache dalam tidak sah!

Proses pelaksanaan umum adalah seperti di atas. Ia akan menjadi lebih mudah untuk membaca kod selepas mengetahui prosesnya!


2. Pelaksanaan kod:

Atribut kawalan tersuai: res/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircleImageView">
        <attr name="Radius" format="dimension"/>
        <attr name="type">
            <enum name="circle" value="0"/>
            <enum name="round" value="1"/>
        </attr>
    </declare-styleable>
</resources>

Kemudian ImageView tersuai: CircleImageView.java:

/**
 * Created by Jay on 2015/10/25 0025.
 */
public class CircleImageView extends ImageView {

    private Paint mPaint;
    private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    private Bitmap mMaskBitmap;
    private WeakReference<Bitmap> mWeakBitmap;

    //图片相关的属性
    private int type;                           //类型,圆形或者圆角
    public static final int TYPE_CIRCLE = 0;
    public static final int TYPE_ROUND = 1;
    private static final int BODER_RADIUS_DEFAULT = 10;     //圆角默认大小值
    private int mBorderRadius;                  //圆角大小


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

    public CircleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        //取出attrs中我们为View设置的相关值
        TypedArray tArray = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
        mBorderRadius = tArray.getDimensionPixelSize(R.styleable.CircleImageView_Radius, BODER_RADIUS_DEFAULT);
        type = tArray.getInt(R.styleable.CircleImageView_type, TYPE_CIRCLE);
        tArray.recycle();
    }

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


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (type == TYPE_CIRCLE) {
            int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
            setMeasuredDimension(width, width);    //设置当前View的大小
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {

        //在缓存中取出bitmap
        Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();
        if (bitmap == null || bitmap.isRecycled()) {
            //获取图片宽高
            Drawable drawable = getDrawable();
            int width = drawable.getIntrinsicWidth();
            int height = drawable.getIntrinsicHeight();

            if (drawable != null) {
                bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
                Canvas drawCanvas = new Canvas(bitmap);
                float scale = 1.0f;
                if (type == TYPE_ROUND) {
                    scale = Math.max(getWidth() * 1.0f / width, getHeight()
                            * 1.0f / height);
                } else {
                    scale = getWidth() * 1.0F / Math.min(width, height);
                }
                //根据缩放比例,设置bounds,相当于缩放图片了
                drawable.setBounds(0, 0, (int) (scale * width),
                        (int) (scale * height));

                drawable.draw(drawCanvas);
                if (mMaskBitmap == null || mMaskBitmap.isRecycled()) {
                    mMaskBitmap = getBitmap();
                }

                mPaint.reset();
                mPaint.setFilterBitmap(false);
                mPaint.setXfermode(mXfermode);

                //绘制形状
                drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint);

                //bitmap缓存起来,避免每次调用onDraw,分配内存
                mWeakBitmap = new WeakReference<Bitmap>(bitmap);

                //绘制图片
                canvas.drawBitmap(bitmap, 0, 0, null);
                mPaint.setXfermode(null);

            }
        }
        if (bitmap != null) {
            mPaint.setXfermode(null);
            canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
            return;
        }
    }

    //缓存Bitmap,避免每次OnDraw都重新分配内存与绘图
    @Override
    public void invalidate() {
        mWeakBitmap = null;
        if (mWeakBitmap != null) {
            mMaskBitmap.recycle();
            mMaskBitmap = null;
        }
        super.invalidate();
    }

    //定义一个绘制形状的方法

    private Bitmap getBitmap() {
        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
                Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);   //抗锯齿
        paint.setColor(Color.BLACK);
        if (type == TYPE_ROUND) {
            canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()),
                    mBorderRadius, mBorderRadius, paint);
        } else {
            canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2, paint);
        }
        return bitmap;
    }
}

Akhirnya panggil dalam fail susun atur: activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.jay.xfermodedemo1.CircleImageView
        android:layout_width="160dp"
        android:layout_height="240dp"
        android:layout_margin="10dp"
        android:src="@mipmap/ic_bg_meizi2"
        app:type="circle" />

    <com.jay.xfermodedemo1.CircleImageView
        android:layout_width="160dp"
        android:layout_height="280dp"
        android:layout_margin="10dp"
        android:src="@mipmap/ic_bg_meizi1"
        app:Radius="30dp"
        app:type="round" />
    
</LinearLayout>

Baiklah, saya tidak faham kod di sekali , anda akan faham selepas membacanya dua kali lagi~


3 Muat turun contoh kod bahagian ini:

XfermodeDemo1.zip

<🎜. >
Ringkasan bahagian ini:

Dalam bahagian ini kami menerangkan contoh aplikasi pertama Xfermode dan PorterDuff, menetapkan mod DST_IN untuk mencapainya Penyesuaian imej bulat dan bulat ImageView, saya percaya semua orang sudah biasa dengan aplikasi mudah PorterDuff. Pukul semasa seterika panas Di bahagian seterusnya, kami akan menulis contoh untuk melatih kemahiran kami~ Okay, itu sahaja, terima kasih~