Skip to content

Instantly share code, notes, and snippets.

@s1rius
Last active July 4, 2016 13:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save s1rius/50ce8b98d865f8184e28d4ab44f9bf97 to your computer and use it in GitHub Desktop.
Save s1rius/50ce8b98d865f8184e28d4ab44f9bf97 to your computer and use it in GitHub Desktop.
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<GenericDraweeHierarchy> mMultiDraweeHolder;
private int mColumnCount = 3;
private int mSpaceSize;
private int mDraweeSize;
private List<String> 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<GenericDraweeHierarchy> 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<String> 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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment