android Bitmap内存优化(三) 测量 ImageView 大小
2015-09-04 21:29
531 查看
一些概念
首先我们要搞清楚这几个概念,根据 View 里面的概念(因为 ImageView 就是继承 View的),一个 View 来说,它有一个 Location(位置),这个是相对于父元素的位置,由一对坐标设置,top 和 left,然后他还有一个 dimensions(大小),width 和 height 控制。所以你在 xml 文件中设置 layouot_width 和 layout_heigth 的时候,就是其实我们有必要了解一个 View 是怎样绘制的,以及它的属性和参数是怎样设置的。
[code]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 文件
[code]<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 参考上一篇文章。[code]// 对 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()方法里面进行的操作
[code]/** * 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 相对于 父元素的左边距的像素距离数值
[code]/** * 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 进行了赋值
[code]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(),这里进行实际的绘制。三个步骤都是由顶向下的,按照树的结构进行的。
[code] /** * 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 是如何绘制的。
相关文章推荐
- android 自定义属性
- Android中的进程机制(Binder)
- 如何使用Android命令
- Android Api Demos登顶之路(五十六)Service Remote Service
- @+id/android:list"的写法
- Android实战简易教程-第五十三枪(通过实现OnScrollListener接口实现上拉加载更多功能)
- android中的一些小技巧
- 【Android进阶】-Android ORM数据库框架 ActiveAndroid入门
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android核心基础-8.Android四大组件之Activity-3.生命周期、横竖屏切换、启动模式、进程管理
- 【转】android如何添加权限
- Android Service与Activity之间通信的几种方式
- When executing step "Deploy to Android device"
- Android下得到APK包含信息
- android定位布局
- Android的javascript扩展
- (Android Studio)ActionBar's Theme/Style [ActionBar主题风格修改]
- android studio开发Activity切换动画以及ButterKnife注解简单应用
- Android Studio最新版本(1.3)JNI开发流程总结
- Android一些设计原则杂项