package net.coding.program.common.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.ImageView; import net.coding.program.R; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Created by chaochen on 15/2/4. */ public class MinSizeImageView extends GifMarkImageView { protected int[] ATTR = new int[]{ R.attr.minWidth, R.attr.minHeight, R.attr.microSize }; int mMinWidth; int mMinHeight; int mMicroSize; public MinSizeImageView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, ATTR); mMinWidth = a.getDimensionPixelSize(R.styleable.MinSizeImageView_minWidth, 1); mMinHeight = a.getDimensionPixelSize(R.styleable.MinSizeImageView_minHeight, 1); mMicroSize = a.getDimensionPixelSize(R.styleable.MinSizeImageView_microSize, 1); a.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { try { // 反射调用 resolveUri reflectMethod(ImageView.class, "resolveUri"); int w; int h; Drawable mDrawable = getDrawable(); // Desired aspect ratio of the view's contents (not including padding) float desiredAspect = 0.0f; // We are allowed to change the view's width boolean resizeWidth = false; // We are allowed to change the view's height boolean resizeHeight = false; final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); if (mDrawable == null) { // If no drawable, its intrinsic size is 0. // mDrawableWidth = -1; // mDrawableHeight = -1; reflectIntSet("mDrawableWidth", -1); reflectIntSet("mDrawableHeight", -1); w = h = 0; } else { // 反射获取 w = reflectInt(ImageView.class, "mDrawableWidth"); h = reflectInt(ImageView.class, "mDrawableHeight"); if (w <= 0) w = 1; if (h <= 0) h = 1; // We are supposed to adjust view bounds to match the aspect // ratio of our drawable. See if that is possible. boolean mAdjustViewBounds = reflectBoolean("mAdjustViewBounds"); if (mAdjustViewBounds) { resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; desiredAspect = (float) w / (float) h; } } // 反射获取 int pleft = reflectInt(View.class, "mPaddingLeft"); int pright = reflectInt(View.class, "mPaddingRight"); int ptop = reflectInt(View.class, "mPaddingTop"); int pbottom = reflectInt(View.class, "mPaddingBottom"); int widthSize; int heightSize; // 反射获取 boolean mAdjustViewBoundsCompat = reflectBoolean("mAdjustViewBoundsCompat"); int mMaxWidth = reflectInt(ImageView.class, "mMaxWidth"); int mMaxHeight = reflectInt(ImageView.class, "mMaxHeight"); if (resizeWidth || resizeHeight) { /* If we get here, it means we want to resize to match the drawables aspect ratio, and we have the freedom to change at least one dimension. */ // Get the max possible width given our constraints widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec); // Get the max possible height given our constraints heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec); if (desiredAspect != 0.0f) { // See what our actual aspect ratio is float actualAspect = (float) (widthSize - pleft - pright) / (heightSize - ptop - pbottom); if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { boolean done = false; // Try adjusting width to be proportional to height if (resizeWidth) { int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; // Allow the width to outgrow its original estimate if height is fixed. if (!resizeHeight && !mAdjustViewBoundsCompat) { widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); } if (newWidth <= widthSize) { widthSize = newWidth; done = true; } } // Try adjusting height to be proportional to width if (!done && resizeHeight) { int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; // Allow the height to outgrow its original estimate if width is fixed. if (!resizeWidth && !mAdjustViewBoundsCompat) { heightSize = resolveAdjustedSize(newHeight, mMaxHeight, heightMeasureSpec); } if (newHeight <= heightSize) { heightSize = newHeight; } } } } } else { /* We are either don't want to preserve the drawables aspect ratio, or we are not allowed to change view dimensions. Just measure in the normal way. */ w += pleft + pright; h += ptop + pbottom; w = Math.max(w, getSuggestedMinimumWidth()); h = Math.max(h, getSuggestedMinimumHeight()); widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); } if (widthSize < mMinWidth && heightSize < mMinHeight) { float ratioWidth = (float) mMinWidth / (float) widthSize; float ratioHeight = (float) mMinHeight / (float) heightSize; if (ratioWidth < ratioHeight) { widthSize = mMinWidth; heightSize *= ratioWidth; } else { heightSize = mMinHeight; widthSize *= ratioHeight; } } if (widthSize < mMicroSize || heightSize < mMicroSize) { if (widthSize < mMicroSize) { float ratio = (float) mMicroSize / (float) widthSize; widthSize = mMicroSize; heightSize *= ratio; if (heightSize > mMaxHeight) { heightSize = mMaxHeight; } } else { float ratio = (float) mMicroSize / (float) heightSize; heightSize = mMicroSize; widthSize *= ratio; if (widthSize > mMaxWidth) { widthSize = mMaxWidth; } } } setMeasuredDimension(widthSize, heightSize); } catch (Exception e) { Log.d("", "hh " + e); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } private void reflectMethod(Class<?> cls, String name) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method method = cls.getDeclaredMethod(name); method.setAccessible(true); method.invoke(this); } private int reflectInt(Class cls, String name) throws Exception { Field intField = cls.getDeclaredField(name); intField.setAccessible(true); return intField.getInt(this); } private void reflectIntSet(String name, int value) throws Exception { Class<?> cls = ImageView.class; Field intField = cls.getDeclaredField(name); intField.setAccessible(true); intField.setInt(this, value); } private boolean reflectBoolean(String name) throws Exception { Class<?> cls = ImageView.class; Field booleanField = cls.getDeclaredField(name); booleanField.setAccessible(true); return booleanField.getBoolean(this); } private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) { int result = desiredSize; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: /* Parent says we can be as big as we want. Just don't be larger than max size imposed on ourselves. */ result = Math.min(desiredSize, maxSize); break; case MeasureSpec.AT_MOST: // Parent says we can be as big as we want, up to specSize. // Don't be larger than specSize, and don't be larger than // the max size imposed on ourselves. result = Math.min(Math.min(desiredSize, specSize), maxSize); break; case MeasureSpec.EXACTLY: // No choice. Do what we are told. result = specSize; break; } return result; } }