RelativeLayout有一个背景宽高为1635*1029px的图片,需要TextView控制显示在背景图片距离顶部150px的位置处.
1、RelativeLayout宽高自适应,所以会等比例缩放
2、获取RelativeLayout的实际高度
3、计算TextView距离顶部背景图片150px的实际间距
4、动态设置TextView距离顶部的间距
//...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
//获取RelativeLayout的实际高度
int rltLeftHeight = rltLeft.getHeight();
//计算TextView距离顶部背景图片150px的实际间距
int topmargin = (rltLeftHeight * 150) / 1029;
//动态设置TextView距离顶部的间距
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) rltLeft.getLayoutParams();
layoutParams.setMargins(0,topmargin,0,0);
tvTitle.setLayoutParams(layoutParams);
}
//...
view.setPadding(int left, int top, int right, int bottom);
查看源码可以定位到setPadding是在View.class中,设置单位是px,in pixels表示单位px
/**
* Sets the padding. The view may add on the space required to display
* the scrollbars, depending on the style and visibility of the scrollbars.
* So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop},
* {@link #getPaddingRight} and {@link #getPaddingBottom} may be different
* from the values set in this call.
*
* @attr ref android.R.styleable#View_padding
* @attr ref android.R.styleable#View_paddingBottom
* @attr ref android.R.styleable#View_paddingLeft
* @attr ref android.R.styleable#View_paddingRight
* @attr ref android.R.styleable#View_paddingTop
* @param left the left padding in pixels
* @param top the top padding in pixels
* @param right the right padding in pixels
* @param bottom the bottom padding in pixels
*/
public void setPadding(int left, int top, int right, int bottom) {
}
LayoutParams lp = (LayoutParams) view.getLayoutParams();
lp.setMargins(int left, int top, int right, int bottom);
setMargin是在ViewGroup.class中,单位也是像素px,setMargin是MarginLayoutParams.class内部类的。
/**
* Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
* to be done so that the new margins are taken into account. Left and right margins may be
* overridden by {@link android.view.View#requestLayout()} depending on layout direction.
* Margin values should be positive.
*
* @param left the left margin size
* @param top the top margin size
* @param right the right margin size
* @param bottom the bottom margin size
*
* @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
* @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
* @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
* @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
*/
public void setMargins(int left, int top, int right, int bottom) {
}
margin设置话,要注意view.getLayoutParams()是需要强制转换的,要看view的父元素是什么容器。如上面的处理方案,TextView的父容器是RelativeLayout,所以:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) rltLeft.getLayoutParams();
layoutParams.setMargins(0,topmargin,0,0);
tvTitle.setLayoutParams(layoutParams);
根据如上用法,Activity 中在 onCreate()或onResume() 生命周期方法中 通过view.getWidth()或view.getHeight() 方法获取的宽高值都是0。
我们知道View的绘制流程主要分为三步:
测量视图的大小,从顶层父View到子View递归调用measure()方法,measure()调用onMeasure()方法,onMeasure()方法完成绘制工作。
确定视图的位置,从顶层父View到子View递归调用layout()方法,父View将上一步measure()方法得到的子View的布局大小和布局参数,将子View放在合适的位置上。
绘制最终的视图,首先ViewRoot创建一个Canvas对象,然后调用onDraw()方法进行绘制。
① 绘制视图背景。
② 绘制画布的图层。
③ 绘制View内容。
④ 绘制子视图,如果有的话。
⑤ 还原图层。
⑥ 绘制滚动条。
所以,当我们在 Activity 的生命周期方法中直接获取 View 的宽高时,View 可能还没执行完 measure 阶段,获取到的宽高结果就会为 0。
view.post(new Runnable() {
@Override
public void run() {
int width = view.getWidth();
}
});
该方法添加一个Runnable 操作到队列末尾,当等到View attachToWindow时调用。,当View attachToWindow,已经进行了onMeasure()和onLayout()所以可以获取到宽高。
a、利用 ViewTreeObserver 观察者类,监听draw 事件
View将要绘制时,已经经过了onMeasure(),onLayout(),所以可以获取到 View的宽和高。onPreDrawListener可能会被回调多次,所以要注意移除。
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
Log.e(TAG, "onCreate: "+view.getWidth()+","+view.getHeight());
Log.e(TAG, "onCreate: "+view.getMeasuredWidth()+","+view.getMeasuredHeight());
//移除监听
view.getViewTreeObserver().removeOnPreDrawListener(this);
return false;
}
});
b、利用 ViewTreeObserver 观察者类,监听layout 事件
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= 16) {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}else {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
int width = view.getWidth();
}
});
onWindowFocusChanged(boolean hasFocus)会是在Activity当前Window焦点改变时,会回调这个方法,当Activity的获取到焦点时,布局的View已经完成了onMeasure()测量和onLayout()布局,可以正确获取到View的宽和高。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
int width = view.getWidth();
int height = view.getHeight();
}
以上方案,View.post方案获取 View 的宽高属性,相比 ViewTreeObserver 监听处理,还不需要手动移除观察者监听事件,代码更简洁,使用也简单。
getMeasuredWidth()、getMeasuredHeight()获取的是view原始的大小,即这个view在XML文件中配置或者是代码中设置的大小。
getWidth()、getHeight()获取的是这个view最终显示的大小,即有可能等于原始的大小也有可能不等于原始大小。
< END >【Android进化之路】 【Android进化之路】 微信扫描二维码,关注我的公众号。