package me.s1rius.multidrawee; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.drawable.ScalingUtils; import com.facebook.drawee.generic.GenericDraweeHierarchy; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.DraweeHolder; import com.facebook.drawee.view.MultiDraweeHolder; import com.facebook.imagepipeline.common.ResizeOptions; import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.imagepipeline.request.ImageRequestBuilder; import java.util.ArrayList; import java.util.List; public class MultiDraweeView extends View { public interface OnItemClickListener { void onPicClick(int picIndex, Rect itemRect); } public static final String TAG = MultiDraweeView.class.getSimpleName(); public static final boolean DBG = true; private static int MAX_DRAWEE_COUNT = 9; MultiDraweeHolder mMultiDraweeHolder; private int mColumnCount = 3; private int mSpaceSize; private int mDraweeSize; private List mUris = new ArrayList<>(); private GestureDetector mGestureDetector; private OnItemClickListener mItemClickListener; private int drawnTimes; private int drawnDuration; public MultiDraweeView(Context context) { super(context); init(context); } public MultiDraweeView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public MultiDraweeView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.mItemClickListener = onItemClickListener; } private void init(Context context) { mSpaceSize = (int)dipToPixels(context, 3f); GenericDraweeHierarchyBuilder hierarchyBuilder = new GenericDraweeHierarchyBuilder(getResources()) .setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP) .setFadeDuration(0) .setPlaceholderImage(R.color.colorAccent); mMultiDraweeHolder = new MultiDraweeHolder<>(); for (int i = 0; i < MAX_DRAWEE_COUNT; i++) { DraweeHolder draweeHolder = DraweeHolder.create(hierarchyBuilder.build(), context); draweeHolder.getTopLevelDrawable().setCallback(this); mMultiDraweeHolder.add(draweeHolder); } mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapConfirmed(MotionEvent e) { if (mItemClickListener != null) { if (mUris.size() == 1) { mItemClickListener.onPicClick(0, getBoundsFromIndex(0)); } else { float x = e.getX(); float y = e.getY(); int itemIndex = getIndexFromPoint(x, y); mItemClickListener.onPicClick(itemIndex, getBoundsFromIndex(itemIndex)); if (DBG) Log.i(TAG, "motionEvent x = " + x + " y = " + y); } } return super.onSingleTapConfirmed(e); } @Override public boolean onDown(MotionEvent e) { return getIndexFromPoint(e.getX(), e.getY()) >= 0; } }); } @Override public boolean onTouchEvent(MotionEvent event) { if (null != mGestureDetector) { return mGestureDetector.onTouchEvent(event); } return super.onTouchEvent(event); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { long start = System.currentTimeMillis(); int widthSize = MeasureSpec.getSize(widthMeasureSpec); mDraweeSize = (MeasureSpec.getSize(widthMeasureSpec) - mSpaceSize * (mColumnCount - 1)) / mColumnCount; int rowCount = mUris.size() / mColumnCount + (mUris.size() % mColumnCount == 0 ? 0 : 1); int heightSize = mDraweeSize * rowCount + mSpaceSize * (rowCount - 1); setMeasuredDimension(widthSize, heightSize); long end = System.currentTimeMillis(); long measureDuration = end - start; if (DBG) Log.i(TAG, this.hashCode() + " onMeasure" + measureDuration); } public void setImageUris(List uris) { for (int i = 0; i < MAX_DRAWEE_COUNT; i++) { mMultiDraweeHolder.get(i).getTopLevelDrawable().setCallback(null); } drawnTimes = 0; drawnDuration = 0; this.mUris = uris; ResizeOptions options; if (uris != null && uris.size() > 0) { options = new ResizeOptions((int)dipToPixels(getContext(), 100f), (int)dipToPixels(getContext(), 100f)); for (int i = 0; i < uris.size(); i++) { ImageRequest imageRequest = null; if (i < uris.size()) { Uri uri = Uri.parse(uris.get(i)); if (DBG) Log.i(TAG, this.hashCode() + " set image url " + uri.toString()); imageRequest = ImageRequestBuilder .newBuilderWithSource(uri) .setResizeOptions(options) .setProgressiveRenderingEnabled(false) .build(); mMultiDraweeHolder.get(i).getHierarchy().setPlaceholderImage(R.color.colorAccent); } DraweeController controller = Fresco.newDraweeControllerBuilder() .setImageRequest(imageRequest) .setOldController(mMultiDraweeHolder.get(i).getController()) .build(); mMultiDraweeHolder.get(i).setController(controller); mMultiDraweeHolder.get(i).getTopLevelDrawable().setCallback(this); } } requestLayout(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); attach(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); detach(); } @Override public void onStartTemporaryDetach() { super.onStartTemporaryDetach(); detach(); } @Override public void onFinishTemporaryDetach() { super.onFinishTemporaryDetach(); attach(); } void attach() { mMultiDraweeHolder.onAttach(); } void detach() { for (int i = 0; i < MAX_DRAWEE_COUNT; i++) { mMultiDraweeHolder.get(i).getTopLevelDrawable().setCallback(null); } mMultiDraweeHolder.onDetach(); } @Override protected void onDraw(Canvas canvas) { if (getMeasuredWidth() == 0 && getMeasuredHeight() == 0) { return; } long start = System.currentTimeMillis(); super.onDraw(canvas); int left = 0, top = 0; for (int i = 0; i < mUris.size(); i++) { Drawable drawable = mMultiDraweeHolder.get(i).getTopLevelDrawable(); if (i % mColumnCount == 0) { left = 0; } if (drawable != null) { drawable.setBounds(left, top, left + mDraweeSize, top + mDraweeSize); drawable.draw(canvas); } left += mDraweeSize + mSpaceSize; if (i % mColumnCount != 0 && i % mColumnCount == mColumnCount - 1) { top += mDraweeSize + mSpaceSize; } } long end = System.currentTimeMillis(); drawnTimes ++; drawnDuration += (end - start); if (DBG) Log.i(TAG, this.hashCode() + " onDraw() method duration = " + drawnDuration + "ms" + " draw " + drawnTimes + " times" + " uri count " + mUris.size()); } private Rect getBoundsFromIndex(int index) { Rect rect = new Rect(); int left = 0, top = 0; if (index >= 0) { for (int i = 0; i < mUris.size(); i++) { if (i % mColumnCount == 0) { left = 0; } if (mUris.size() == 1) { rect.set(left, top, left + getMeasuredWidth(), top + getMeasuredHeight()); break; } else if (i == index) { rect.set(left, top, left + mDraweeSize, top + mDraweeSize); break; } left += mDraweeSize + mSpaceSize; if (i % mColumnCount != 0 && i % mColumnCount == mColumnCount - 1) { top += mDraweeSize + mSpaceSize; } } } else { rect.set(left, top, left + getMeasuredWidth(), top + getMeasuredHeight()); } return rect; } private int getIndexFromPoint(float x, float y){ if (mUris.size() == 1) { return 0; } int rowIndex = 0, columnIndex = 0, itemIndex; for (int i = 0; i < mColumnCount; i++) { int left = mDraweeSize * i + mSpaceSize *i; int right = left + mDraweeSize; if (x >= left && x < right) { columnIndex = i; } } while (true) { int top = rowIndex * mDraweeSize + rowIndex * mSpaceSize; int bottom = top + mDraweeSize; if (y >= top && y < bottom) { break; } rowIndex ++; } itemIndex = rowIndex * mColumnCount + columnIndex; if (itemIndex >= mUris.size()) { itemIndex = -1; } return itemIndex; } @Override protected boolean verifyDrawable(Drawable who) { if (DBG) Log.i(TAG, this.hashCode() + "verifyDrawable"); return mMultiDraweeHolder.verifyDrawable(who) || super.verifyDrawable(who); } @Override public void invalidateDrawable(@NonNull Drawable drawable) { int dirtyIndex = -1; for (int i = 0; i < mMultiDraweeHolder.size(); i++) { if (drawable == mMultiDraweeHolder.get(i).getTopLevelDrawable()) { dirtyIndex = i; break; } } if (dirtyIndex != -1) { Rect invalidateRect = getBoundsFromIndex(dirtyIndex); if (invalidateRect.height() != 0 && invalidateRect.width() != 0) { invalidate(invalidateRect); if (DBG) Log.i(TAG, this.hashCode() + " drawable code " + drawable.hashCode() + " invalidateDrawable " + getBoundsFromIndex(dirtyIndex).flattenToString()); } } } public float dipToPixels(Context context, float dipValue) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics); } }