Last active
July 4, 2016 13:27
-
-
Save s1rius/50ce8b98d865f8184e28d4ab44f9bf97 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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