package com.netease.nim.uikit.common.ui.imageview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; import com.netease.nim.uikit.R; import com.netease.nim.uikit.common.framework.NimSingleThreadExecutor; import com.netease.nim.uikit.common.util.media.BitmapDecoder; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.core.download.ImageDownloader; import com.nostra13.universalimageloader.core.imageaware.NonViewAware; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.utils.MemoryCacheUtils; import java.io.IOException; public class MsgThumbImageView extends ImageView { private Drawable mask; // blend mask drawable public MsgThumbImageView(Context context) { super(context); } public MsgThumbImageView(Context context, AttributeSet attrs) { super(context, attrs); } public MsgThumbImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } private DisplayImageOptions options = createImageOptions(); private boolean hasLoaded = false; private static final DisplayImageOptions createImageOptions() { int defaultIcon = R.drawable.nim_image_default; return new DisplayImageOptions.Builder() .showImageOnLoading(defaultIcon) .showImageOnFail(defaultIcon) .cacheInMemory(true) .cacheOnDisk(true) .bitmapConfig(Bitmap.Config.RGB_565) .build(); } private static final Paint paintMask = createMaskPaint(); private static final Paint createMaskPaint() { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setFilterBitmap(true); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); return paint; } @Override protected void onDraw(Canvas canvas) { if (mask != null) { // bounds int width = getWidth(); int height = getHeight(); // create blend layer canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG); // // mask // if (mask != null) { mask.setBounds(0, 0, width, height); mask.draw(canvas); } // // source // { canvas.saveLayer(0, 0, width, height, paintMask, Canvas.ALL_SAVE_FLAG); super.onDraw(canvas); canvas.restore(); } // apply blend layer canvas.restore(); } else { super.onDraw(canvas); } } @Override public boolean isOpaque() { return false; } public void loadAsPath(String pathName, int width, int height, int maskId) { setBlendDrawable(maskId); setImageBitmap(BitmapDecoder.decodeSampled(pathName, width, height)); } public void loadAsPath(boolean isOriginal, final String path, final String tag, final int width, final int height, final int maskId) { if (TextUtils.isEmpty(path)) { setTag(null); loadAsResource(R.drawable.nim_image_default, maskId); return; } if (!isOriginal || getTag() == null || !getTag().equals(tag)) { hasLoaded = false; // 由于ViewHolder复用,使得tag发生变化,必须重新加载 } setTag(tag); // 解决ViewHolder复用问题 // async load if (!hasLoaded) { // load default image first loadAsResource(R.drawable.nim_image_default, maskId); final String uri = ImageDownloader.Scheme.FILE.wrap(path); final ImageSize imageSize = new ImageSize(width, height); ImageLoader.getInstance().displayImage(uri, new NonViewAware(imageSize, ViewScaleType.CROP), options, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { if (getTag() != null && getTag().equals(tag) && !hasLoaded) { setImageBitmap(loadedImage); hasLoaded = true; // 没有复用情况下,已经加载过了,不要重复加载,会闪烁 } } @Override public void onLoadingFailed(final String imageUri, View view, FailReason failReason) { // 视频缩略图后缀.mp4等导致ImageLoader解码失败 if (failReason.getType() == FailReason.FailType.DECODING_ERROR) { loadBmpAsync(imageUri, path, imageSize, tag); } } }); } } private void loadBmpAsync(final String imageUri, final String path, final ImageSize imageSize, final String tag) { NimSingleThreadExecutor.getInstance().execute(new NimSingleThreadExecutor.NimTask<Bitmap>() { @Override public Bitmap runInBackground() { return decodeBmpAndSave(path, imageSize, imageUri); } @Override public void onCompleted(Bitmap result) { if (result != null && (getTag() != null && getTag().equals(tag) && !hasLoaded)) { setImageBitmap(result); hasLoaded = true; } } }); } private Bitmap decodeBmpAndSave(String path, ImageSize imageSize, String imageUri) { Bitmap bitmap = BitmapDecoder.decodeSampled(path, imageSize.getWidth(), imageSize.getHeight()); if (bitmap != null) { String memoryCacheKey = MemoryCacheUtils.generateKey(imageUri, imageSize); ImageLoader.getInstance().getMemoryCache().put(memoryCacheKey, bitmap); try { ImageLoader.getInstance().getDiskCache().save(imageUri, bitmap); } catch (IOException e) { e.printStackTrace(); } } return bitmap; } public void loadAsResource(int resId, int maskId) { setBlendDrawable(maskId); setImageResource(resId); } private void setBlendDrawable(int maskId) { mask = maskId != 0 ? getResources().getDrawable(maskId) : null; } }