android Bitmap内存优化(三) 测量 ImageView 大小

一些概念

首先我们要搞清楚这几个概念,根据 View 里面的概念(因为 ImageView 就是继承 View的),一个 View 来说,它有一个 Location(位置),这个是相对于父元素的位置,由一对坐标设置,top 和 left,然后他还有一个 dimensions(大小),width 和 height 控制。所以你在 xml 文件中设置 layouot_width 和 layout_heigth 的时候,就是
其实我们有必要了解一个 View 是怎样绘制的,以及它的属性和参数是怎样设置的。

public static void ImageSize(ImageView imgv){
    int width = imgv.getWidth();
    int heigth = imgv.getHeight();
    //int maxWidth = imgv.getMaxWidth();
    //int maxHeight = imgv.getMaxHeight();
    int mesuredWidth = imgv.getMeasuredWidth();
    int mesuredHeight = imgv.getMeasuredHeight();

    int top = imgv.getTop();//对 View 来说是top,其实top 在坐标值上,小于bottom
    int bottom =imgv.getBottom();
    int left = imgv.getLeft();
    int right = imgv.getRight();
    int padingLeft =imgv.getPaddingLeft();
    int padingTop = imgv.getPaddingTop();
    int padingBottom = imgv.getPaddingBottom();

    DisplayMetrics dm =imgv.getContext().getResources().getDisplayMetrics();
    float density = dm.density;
    int densityDpi = dm.densityDpi;
    int xPx = dm.widthPixels;
    int yPx = dm.heightPixels;


    ViewGroup.LayoutParams lp = imgv.getLayoutParams();

    int w = lp.width;
    int h =lp.height;
    int marginLeft =100;

}

xml 文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

tools:context=".MainActivity">

<ImageView
    android:layout_marginTop="50dp"
    android:id="@+id/imgv_animations"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:background="#f25"
    android:layout_marginLeft="60dp"
    android:paddingTop="10dp"
    android:paddingBottom="15dp"
    android:maxWidth="150dp"
    android:maxHeight="150dp"
    android:paddingLeft="20dp"

    />

</RelativeLayout>

测试的结果

这里写图片描述

对 ImageView 测量实际绘制大小

因为 Android 各种分辨率的手机,所以不同手机上同一个代码显示出来的 ImageView 的大小往往实际不同,即使你是写了了宽和高是多少 dp,因为 View 的大小测量时以 px 为单位的,不同屏幕密度的手机,1dp 等于的 px数值不一样,160 dpi 的手机上,1dp等于1px,然而,现在主流一般都是 480dpi的手机了(2015年中旬),所以我们为了保证显示效果同时节省内存,我们需要对 ImageView 进行测量大小,然后根据这个数值,压缩 Bitmap,关于压缩Bitmap 参考上一篇文章。

// 对 ImageView  进行 resize
public static ImageSize ImageViewResize(ImageView imgv){
    int width = imgv.getWidth();
    ViewGroup.LayoutParams layoutParams = imgv.getLayoutParams();
    DisplayMetrics dm = imgv.getResources().getDisplayMetrics();
    if(width == 0){
        width = layoutParams.width;
    }
    if(width == 0){
        width = getAttrValue("maxWidth",imgv);
    }
    if(width ==0){
        width = dm.widthPixels;
    }

    int height =imgv.getHeight() ;
    if(height  ==0){
        height = layoutParams.height;
    }
    if(height == 0){
        height = dm.heightPixels;
    }
    ImageSize imageSize = new ImageSize(width,height);

    return imageSize;
}

public static class ImageSize{
    public  int width;
    public int height;

    public ImageSize(int width, int height) {
        this.width = width;
        this.height = height;
    }
}

// 通过 反射 获得字段属性值
public static int getAttrValue(String valueName,ImageView  imgv){
    if(imgv == null){
        return 0;
    }

    int value = 0;
    try {
        Field  field = imgv.getClass().getDeclaredField(valueName);
        value = field.getInt(imgv);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return  value;
}

tip:关于 属性 maxWidth,你在这种情况会用到,你需要限定最大的长度,但是又希望尽可能的适配内容,所以有时会使用maxWidth或者 maxHeigth 这两个属性,当然对应的有 minWidth 和 minHeigth
这两个属性, api 13以上你可以通过View.getMaxWidth() 方法直接或者这类属性,我这类为了适配系统,所以没有直接用这个方法。

然后,我这边解释一下,我们为什么要这样获取一个ImageView 的宽(高是一样的原理啦)

我们看一下 getWidth()方法里面进行的操作

/**
 * Return the width of the your view.
 *
 * @return The width of your view, in pixels.
 */
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
    return mRight - mLeft;
}

这个字段 mRight ,是 子 View 相对于 父元素的左边距的像素距离数值

/**
 * The distance in pixels from the left edge of this view's parent
 * to the right edge of this view.
 * {@hide}
 */
@ViewDebug.ExportedProperty(category = "layout")
protected int mRight;

tip:这里出现的注解只是为了调试工具观察其数值使用的,并没有具体意义。

我们可以看到,width 的具体赋值是 mRight - mLeft,那我们看看mRight 和 mLeft 各自代表的是,子 View 的左右边距相对于父布局左边距的像素距离数值,那么这两个数值是在哪里赋值的呢?

在 View 的源码里面我们可以看到在这两个方法里面对 mRight 进行了赋值

public final void setRight(int right)

protected boolean setFrame(int left, int top, int right, int bottom)

前者是给子元素右边界设置相对于父元素的左边距的像素数值的,后者是设置一个子 View 的位置和大小的,这两个方法都不能直接被调用,而是由 layout 系统调用,因为UI组件里面的元素大小改变都要重新进行 layout,所以如果你自行去调用会这些执行方法是很危险的。

我们在 View 的 layout()方法里面可以看到上面这两个方法的调用,我们知道一个 UI 视图的绘制,需要经过三个步骤,onMeasure(),这个方法会测量 子View 的大小和相对于父布局的位置,然后是 onLayout,这里进行View 的大小设置和位置设置(相对于父布局),然后是 onDraw(),这里进行实际的绘制。三个步骤都是由顶向下的,按照树的结构进行的。

 /**
 * Assign a size and position to a view and all of its
 * descendants
 *
 * <p>This is the second phase of the layout mechanism.
 * (The first is measuring). In this phase, each parent calls
 * layout on all of its children to position them.
 * This is typically done using the child measurements
 * that were stored in the measure pass().</p>
 *
 * <p>Derived classes should not override this method.
 * Derived classes with children should override
 * onLayout. In that method, they should
 * call layout on each of their children.</p>
 *
 * @param l Left position, relative to parent
 * @param t Top position, relative to parent
 * @param r Right position, relative to parent
 * @param b Bottom position, relative to parent
 */
@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);
        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnLayoutChangeListeners != null) {
            ArrayList<OnLayoutChangeListener> listenersCopy =
                    (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
            int numListeners = listenersCopy.size();
            for (int i = 0; i < numListeners; ++i) {
                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
            }
        }
    }

    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}

上面是 View 里面的 measure()方法,实际上,一个 View 的 width或者height是由父布局和View 共同决定的。具体可以参考 View 是如何绘制的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值