package cn.sinata.xldutils.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import androidx.annotation.Nullable; 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.AspectRatioMeasure; import com.facebook.drawee.view.DraweeHolder; import com.facebook.drawee.view.MultiDraweeHolder; import java.util.ArrayList; import java.util.List; import cn.sinata.xldutils.utils.DensityUtil; /** * 网格展示多张图片的ImageView,图片通过Freso获取展示,调用setUrls(List urls)设置数据源。 * Created by liaoxiang on 16/7/27. */ @SuppressWarnings("unused") public class MultiImageView extends View{ private final AspectRatioMeasure.Spec mMeasureSpec = new AspectRatioMeasure.Spec(); private float mAspectRatio = 1.0f;//默认正方形 @SuppressWarnings("SpellCheckingInspection") private MultiDraweeHolder multiDraweeHolder; private boolean mInitialised = false; private Context mContext; private int padding = 0; private ArrayList drawables = new ArrayList<>(); boolean mIsCapturingGesture; boolean mIsClickCandidate; long mActionDownTime; float mActionDownX; float mActionDownY; float mSingleTapSlopPx; public MultiImageView(Context context) { super(context); init(context); } public MultiImageView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public MultiImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } /** This method is idempotent so it only has effect the first time it's called */ private void init(Context context) { final ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mSingleTapSlopPx = viewConfiguration.getScaledTouchSlop(); mContext = context; if (mInitialised) { return; } padding = DensityUtil.dip2px(context,2); mInitialised = true; multiDraweeHolder = new MultiDraweeHolder<>(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int size = multiDraweeHolder.size(); int usedW = 0; int usedH = 0; drawables.clear(); for (int i = 0; i < multiDraweeHolder.size(); ++i) { Drawable drawable = multiDraweeHolder.get(i).getTopLevelDrawable(); if (drawable != null) { if (size == 1){ int w = getWidth(); int h = getHeight(); if (h<=0){ h = w; } drawable.setBounds(usedW,usedH,w+usedW,h); }else if (size < 4){ int w = (getWidth()- (padding*(size-1)))/size; drawable.setBounds(usedW,usedH,w+usedW,w+usedH); usedW += w+padding; }else if (size == 4){ //行首重置x坐标 if (i%2==0){ usedW = 0; } int w = (getWidth()-padding)/2; usedH = (i/2)*(w+padding); drawable.setBounds(usedW,usedH,w+usedW,w+usedH); usedW += w+padding; }else { //行首重置x坐标 if (i%3==0){ usedW = 0; } int w = (getWidth()- (padding*2))/3; usedH = (i/3)*(w+padding); drawable.setBounds(usedW,usedH,w+usedW,w+usedH); usedW += (w+padding); } drawables.add(drawable); } } multiDraweeHolder.draw(canvas); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); onAttach(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); onDetach(); } @Override public void onStartTemporaryDetach() { super.onStartTemporaryDetach(); onDetach(); } @Override public void onFinishTemporaryDetach() { super.onFinishTemporaryDetach(); onAttach(); } /** Called by the system to attach. Subclasses may override. */ protected void onAttach() { doAttach(); } /** Called by the system to detach. Subclasses may override. */ protected void onDetach() { doDetach(); } /** * Does the actual work of attaching. * * Non-test subclasses should NOT override. Use onAttach for custom code. */ protected void doAttach() { multiDraweeHolder.onAttach(); } /** * Does the actual work of detaching. * * Non-test subclasses should NOT override. Use onDetach for custom code. */ protected void doDetach() { drawables.clear(); int size = multiDraweeHolder.size(); for (int i = 0; i < size; i++) { multiDraweeHolder.get(i).getTopLevelDrawable().setCallback(null); } multiDraweeHolder.onDetach(); } @Override public boolean onTouchEvent(MotionEvent event) { //点击重新加载图片,如果开启需要打开此段代码 // if (multiDraweeHolder.onTouchEvent(event)) { // return true; // } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mIsCapturingGesture = true; mIsClickCandidate = true; mActionDownTime = event.getEventTime(); mActionDownX = event.getX(); mActionDownY = event.getY(); break; case MotionEvent.ACTION_MOVE: if (Math.abs(event.getX() - mActionDownX) > mSingleTapSlopPx || Math.abs(event.getY() - mActionDownY) > mSingleTapSlopPx) { mIsClickCandidate = false; } break; case MotionEvent.ACTION_CANCEL: mIsCapturingGesture = false; mIsClickCandidate = false; break; case MotionEvent.ACTION_UP: mIsCapturingGesture = false; if (Math.abs(event.getX() - mActionDownX) > mSingleTapSlopPx || Math.abs(event.getY() - mActionDownY) > mSingleTapSlopPx) { mIsClickCandidate = false; } if (mIsClickCandidate) { if (event.getEventTime() - mActionDownTime <= ViewConfiguration.getLongPressTimeout()) { clickCheck(event,0); } else { //长按 clickCheck(event,1); } } mIsClickCandidate = false; break; } return true; } private void clickCheck(MotionEvent event,int type){ //检查点击区域 if (drawables.size()>0){ for (int i = 0; i < drawables.size(); i++) { if (drawables.get(i).getBounds().contains((int)event.getX(),(int)event.getY())){ if (type == 0){//单击 if (imageClickListener!=null){ imageClickListener.onClick(i); break; } }else {//长按 if (imageLongClickListener!=null){ imageLongClickListener.onLongClick(i); break; } } } } } } /** * Sets the desired aspect ratio (w/h). */ public void setAspectRatio(float aspectRatio) { if (aspectRatio == mAspectRatio) { return; } mAspectRatio = aspectRatio; requestLayout(); } /** * Gets the desired aspect ratio (w/h). */ public float getAspectRatio() { return mAspectRatio; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mMeasureSpec.width = widthMeasureSpec; mMeasureSpec.height = heightMeasureSpec; updateMeasureSpec( mMeasureSpec, mAspectRatio, getLayoutParams(), getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); super.onMeasure(mMeasureSpec.width, mMeasureSpec.height); } private void updateMeasureSpec( AspectRatioMeasure.Spec spec, float aspectRatio, @Nullable ViewGroup.LayoutParams layoutParams, int widthPadding, int heightPadding) { if (aspectRatio <= 0 || layoutParams == null) { return; } int size = multiDraweeHolder.size(); if (shouldAdjust(layoutParams.height)) { int widthSpecSize = MeasureSpec.getSize(spec.width); int desiredHeight = 0; if (size == 1 || size == 4 || size > 6){ desiredHeight = (int) ((widthSpecSize - widthPadding) / aspectRatio + heightPadding); }else if (size == 2 ){ desiredHeight = (int) ((widthSpecSize - widthPadding - padding)/ 2 / aspectRatio + heightPadding); }else if (size == 3){ desiredHeight = (int) ((widthSpecSize - widthPadding - padding)/ 3 / aspectRatio + heightPadding); }else if (size<7){ desiredHeight = (int) ((widthSpecSize - widthPadding - padding)/ 3 / aspectRatio*2 + padding + heightPadding); } int resolvedHeight = View.resolveSize(desiredHeight, spec.height); spec.height = MeasureSpec.makeMeasureSpec(resolvedHeight, MeasureSpec.EXACTLY); } else if (shouldAdjust(layoutParams.width)) { int heightSpecSize = MeasureSpec.getSize(spec.height); int desiredWidth = (int) ((heightSpecSize - heightPadding) * aspectRatio + widthPadding); int resolvedWidth = View.resolveSize(desiredWidth, spec.width); spec.width = MeasureSpec.makeMeasureSpec(resolvedWidth, MeasureSpec.EXACTLY); } } private boolean shouldAdjust(int layoutDimension) { // Note: wrap_content is supported for backwards compatibility, but should not be used. return layoutDimension == 0 || layoutDimension == ViewGroup.LayoutParams.WRAP_CONTENT; } @Override protected boolean verifyDrawable(Drawable who) { super.verifyDrawable(who); return multiDraweeHolder.verifyDrawable(who); } public void setUrls(List urls){ if (urls==null){ return; } multiDraweeHolder.clear(); for (int i = 0; i < urls.size(); i++) { GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources()) .setPlaceholderImage(new ColorDrawable(Color.GRAY)) .setPlaceholderImageScaleType(ScalingUtils.ScaleType.CENTER_CROP) .build(); //noinspection SpellCheckingInspection DraweeHolder mDraweeHolder = DraweeHolder.create(hierarchy, mContext); DraweeController controller = Fresco.newDraweeControllerBuilder() .setUri(Uri.parse(urls.get(i))) .setAutoPlayAnimations(true) // 设置加载图片完成后是否直接进行播放 .setOldController(mDraweeHolder.getController()) .build(); mDraweeHolder.setController(controller); multiDraweeHolder.add(mDraweeHolder); } int size = multiDraweeHolder.size(); for (int i = 0; i < size; i++) { multiDraweeHolder.get(i).getTopLevelDrawable().setCallback(this); } } private OnImageClickListener imageClickListener; public void setOnImageClickListener(OnImageClickListener imageClickListener){ this.imageClickListener = imageClickListener; } private OnImageLongClickListener imageLongClickListener; public void setOnImageLongClickListener(OnImageLongClickListener imageLongClickListener){ this.imageLongClickListener = imageLongClickListener; } public interface OnImageClickListener{ void onClick(int position); } public interface OnImageLongClickListener{ void onLongClick(int position); } }