Skip to content

Instantly share code, notes, and snippets.

@s1rius
Last active July 4, 2016 13:27
Show Gist options
  • Save s1rius/50ce8b98d865f8184e28d4ab44f9bf97 to your computer and use it in GitHub Desktop.
Save s1rius/50ce8b98d865f8184e28d4ab44f9bf97 to your computer and use it in GitHub Desktop.

Revisions

  1. s1rius revised this gist Jul 4, 2016. 1 changed file with 0 additions and 39 deletions.
    39 changes: 0 additions & 39 deletions list_item.xml
    Original file line number Diff line number Diff line change
    @@ -1,39 +0,0 @@
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
    android:id="@+id/avatar"
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:layout_marginLeft="12dp"
    android:layout_marginTop="8dp"
    android:src="@mipmap/ic_launcher"/>

    <TextView
    android:id="@+id/title"
    android:layout_toRightOf="@id/avatar"
    android:layout_alignTop="@id/avatar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="12dp"
    android:textSize="22sp"
    android:textColor="@android:color/darker_gray"
    android:text="im a title"/>

    <me.s1rius.multidrawee.MultiDraweeView
    android:id="@+id/pic"
    android:layout_below="@id/title"
    android:layout_toRightOf="@id/avatar"
    android:layout_marginRight="12dp"
    android:layout_marginLeft="12dp"
    android:layout_marginTop="12dp"
    android:layout_marginBottom="8dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    </me.s1rius.multidrawee.MultiDraweeView>

    </RelativeLayout>
  2. s1rius created this gist Jul 3, 2016.
    326 changes: 326 additions & 0 deletions MultiDraweeView.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,326 @@
    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);
    }
    }
    39 changes: 39 additions & 0 deletions list_item.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
    android:id="@+id/avatar"
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:layout_marginLeft="12dp"
    android:layout_marginTop="8dp"
    android:src="@mipmap/ic_launcher"/>

    <TextView
    android:id="@+id/title"
    android:layout_toRightOf="@id/avatar"
    android:layout_alignTop="@id/avatar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="12dp"
    android:textSize="22sp"
    android:textColor="@android:color/darker_gray"
    android:text="im a title"/>

    <me.s1rius.multidrawee.MultiDraweeView
    android:id="@+id/pic"
    android:layout_below="@id/title"
    android:layout_toRightOf="@id/avatar"
    android:layout_marginRight="12dp"
    android:layout_marginLeft="12dp"
    android:layout_marginTop="12dp"
    android:layout_marginBottom="8dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    </me.s1rius.multidrawee.MultiDraweeView>

    </RelativeLayout>