一些概念
首先我们要搞清楚这几个概念,根据 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 是如何绘制的。