Introduction :
Dans la section précédente, nous avons découvert les trois sous-classes de Xfermode : le paramètre dans le constructeur de PorterDuffXfermode est :PorterDuff.ModeAprès avoir examiné les 16 modes de fusion d'images, nous avons aussi écrit du code pour vérifier les 18 différents modes de fusion dans la documentation. Les 18 modes ajoutent deux nouveaux modes : ADD et OVERLAY ! Bien sûr, vérifier et savoir ne suffit pas. Dans cette section, nous allons écrire un exemple pour nous aider à nous familiariser avec la façon d'utiliser réellement ces modes de fusion fournis par PorterDuff.Mode. L'exemple présenté dans cette section est : l'implémentation d'images circulaires et à coins arrondis !
Dans2.3.4 ImageView (Vue d'image)À la fin, nous avons expliqué l'implémentation la plus simple pour dessiner un ImageView circulaire. Le principe est d'appeler clipPath sur l'image pour découper un cercle !
Et cette section utilisePorterDuff.ModeenDST_INmode pour l’implémenter. Sans plus attendre, commençons cette section !PS : L’exemple de cette section provient de l’illustre Hong Yang —Android Xfermode en action : implémentation d’images circulaires et à coins arrondis
1. L’effet à obtenir et analyse du processus d’implémentation :
Schéma de l’effet après exécution:

Bien, ce qui précède est l’effet que nous voulons obtenir. À implémenter avec cePorterDuff.Mode.DST_INmode ! Analysons le processus d’implémentation :
- Étape 1:DST_INmode !
- Étape 2:
- Bien, maintenant que nous connaissons le principe, nous devons ensuite examiner les problèmes liés à une ImageView personnalisée :
- Ce que nous voulons dessiner est une View avec des coins arrondis ou un cercle, nous devons donc ajouter un attribut pour déterminer cela, et les coins arrondis ont aussi besoin d’un paramètre pour le rayon des coins. Par conséquent, nous pouvons utiliser des attributs personnalisés (attrs.xml), puis dans le constructeur de la View personnalisée, extraire ces paramètres !Ensuite vient le calcul de la taille de l’image :D’abord, si nous la définissons comme un cercle, nous devons alors rendre la largeur et la hauteur égales, en nous basant sur la plus petite valeur. Nous pouvons appeler getMeasuredXxx() dans la méthode onMesure() pour obtenir la largeur et la hauteur, voir laquelle est plus petite, et appeler setMeasuredDimension(x, x) pour définir la largeur et la hauteur !Ensuitedans la méthode onDraw(), nous obtenons la largeur et la hauteur de l’image, puis calculons le rapport de mise à l’échelle en fonction de la largeur/hauteur de l’image et de la largeur/hauteur de la View. Si la largeur/hauteur de l’image ne correspond pas à la largeur/hauteur de la View, la largeur et la hauteur de l’image mise à l’échelle doivent être supérieures à la largeur et la hauteur de la View, donc prenez la valeur la plus grande !
- Ensuite,vient le dessin de l’image. Définissez une méthode pour dessiner la forme, puis après avoir initialisé le pinceau, définissez setXfermode sur PorterDuff.Mode.DST_IN, dessinez d’abord l’image, puis dessinez la forme.
- Enfin,il s'agit de la mise en cache des images. Ici, WeakReference est utilisée pour mettre en cache l'image, ce qui évite d'allouer de la mémoire et de redessiner à chaque fois que onDraw est appelé. Enfin, videz le cache dans invalidate !
Le processus général de mise en œuvre est comme ci-dessus. Une fois le processus connu, consulter le code est bien plus simple !
2. Implémentation du code :
Attributs de contrôle personnalisés :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>
Vient ensuite l'ImageView personnalisée :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;
}
}
Enfin, appelez-le dans le fichier de mise en page :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.html"
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.html"
app:Radius="30dp"
app:type="round" />
</LinearLayout>
D'accord, si vous ne comprenez pas le code la première fois, relisez-le deux fois de plus et vous comprendrez~
3. Téléchargement de l'exemple de code pour cette section :
XfermodeDemo1.zip
Résumé de la section :
Dans cette section, nous avons expliqué le premier exemple d'application de Xfermode et PorterDuff, en utilisant le mode DST_IN pour implémenter des ImageViews personnalisées circulaires et à coins arrondis. Je pense que tout le monde a maintenant une petite idée de l'application simple de PorterDuff. Il faut battre le fer pendant qu'il est chaud ; dans la section suivante, nous écrirons aussi un exemple pour nous entraîner. Voilà, c'est tout, merci~
