addView()后子布局不居中问题总结与分析
来源:互联网 发布:原生js 双向绑定 编辑:程序博客网 时间:2024/05/21 10:07
题记
最近项目中遇到一个问题,一个自定义view使用addview加入父布局后,突然不居中了,原因是对父布局增加了一层嵌套;分析类似的问题前我们首先需要理解LayoutParams概念。
LayoutParams
Android SDK中的介绍如下:
LayoutParams are used by views to tell their parents how they want to be laid out. See ViewGroup Layout Attributes for a list of all child view attributes that this class supports.The base LayoutParams class just describes how big the view wants to be for both width and height. For each dimension, it can specify one of: FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which means that the view wants to be as big as its parent (minus padding) WRAP_CONTENT, which means that the view wants to be just big enough to enclose its content (plus padding) an exact number There are subclasses of LayoutParams for different subclasses of ViewGroup. For example, AbsoluteLayout has its own subclass of LayoutParams which adds an X and Y value.
其实这个LayoutParams类是用于child view(子视图) 向 parent view(父视图)传达自己的意愿的一个东西(孩子想变成什么样向其父亲说明)其实子视图父视图可以简单理解成。需要注意的是LayoutParams只是ViewGroup的一个内部类,也就是ViewGroup里边这个LayoutParams类是 base class 基类,实际上每个不同的ViewGroup都有自己的LayoutParams子类,比如LinearLayout 也有自己的 LayoutParams;RelativeLayout也有自己的LayoutParams;
问题描述
代码功能为:在activity中动态添加一个TextView,使TextView文本居中;
居中代码如下(没有问题):
xml布局:
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/main" tools:context="demo.sunrise.com.viewdemo.MainActivity"></FrameLayout>
activity中处理
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); View main = findViewById(R.id.main); TextView text = new TextView(this); text.setText("test"); text.setGravity(Gravity.CENTER_HORIZONTAL); ((ViewGroup)(main)).addView(text);}
这段代码是textview中的文字是可以显示到布局的正中间;
不居中代码(有问题):
只是修改xml:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/main" tools:context="demo.sunrise.com.viewdemo.MainActivity"></RelativeLayout>
问题分析
对比居中代码和不居中代码,两者的差别就是xml布局中一个使用FrameLayout,一个使用了RelativeLayout;那么为什么会出现这么截然不同的结果呢?根据我们对LayoutParams的理解,子view的如何显示,是由子view的LayoutParams来通知父view的;那么我们addview时,子view的LayoutParams到底是怎么样呢?
下面我们对addview的源码进行分析:
addview实现源码如下(源文件地址:frameworks/base/core/java/android/view/ViewGroup.java):
/** * <p>Adds a child view. If no layout parameters are already set on the child, the * default parameters for this ViewGroup are set on the child.</p> * * <p><strong>Note:</strong> do not invoke this method from * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> * * @param child the child view to add * * @see #generateDefaultLayoutParams() */ public void addView(View child) { addView(child, -1); } /** * Adds a child view. If no layout parameters are already set on the child, the * default parameters for this ViewGroup are set on the child. * * <p><strong>Note:</strong> do not invoke this method from * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> * * @param child the child view to add * @param index the position at which to add the child * * @see #generateDefaultLayoutParams() */ public void addView(View child, int index) { if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } LayoutParams params = child.getLayoutParams(); if (params == null) { params = generateDefaultLayoutParams(); if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); } } addView(child, index, params); } /** * Adds a child view with this ViewGroup's default layout parameters and the * specified width and height. * * <p><strong>Note:</strong> do not invoke this method from * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> * * @param child the child view to add */ public void addView(View child, int width, int height) { final LayoutParams params = generateDefaultLayoutParams(); params.width = width; params.height = height; addView(child, -1, params); } /** * Adds a child view with the specified layout parameters. * * <p><strong>Note:</strong> do not invoke this method from * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> * * @param child the child view to add * @param params the layout parameters to set on the child */ @Override public void addView(View child, LayoutParams params) { addView(child, -1, params); } /** * Adds a child view with the specified layout parameters. * * <p><strong>Note:</strong> do not invoke this method from * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> * * @param child the child view to add * @param index the position at which to add the child or -1 to add last * @param params the layout parameters to set on the child */ public void addView(View child, int index, LayoutParams params) { if (DBG) { System.out.println(this + " addView"); } if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } // addViewInner() will call child.requestLayout() when setting the new LayoutParams // therefore, we call requestLayout() on ourselves before, so that the child's request // will be blocked at our level requestLayout(); invalidate(true); addViewInner(child, index, params, false); }
可以发现我们使用addview(View v)方法时,子view的LayoutParams 是这么获取的(当子view的LayoutParams没有传递及没有被赋值):params = generateDefaultLayoutParams();下面我们来看generateDefaultLayoutParams()的实现:我们查看FrameLayout和RelativeLayout中generateDefaultLayoutParams()的实现(这里也是java多态的一种表现形式);(由于FrameLayout和RelativeLayout继承至ViewGroup,所以实际调用的是FrameLayout和RelativeLayout的generateDefaultLayoutParams()方法)。
FrameLayout.java:(源文件地址frameworks/base/core/java/android/widget/FrameLayout.java)
/** * Returns a set of layout parameters with a width of * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}. */ @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); }
RelativeLayout.java:(源文件地址frameworks/base/core/java/android/widget/RelativeLayout.java)
/** * Returns a set of layout parameters with a width of * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}, * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning. */ @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); }
至此我们已经可以得出结论:FrameLayout中addview时子view的布局是MATCH_PARENT,而RelativeLayout是WRAP_CONTENT,这也就解释了为何修改过后的代码textview的文本是不居中的了。
解决方案
- 使用FrameLayout布局作为父布局,默认是MATCH_PARENT。
- 使用addView(View child, LayoutParams params)方法进行添加子view(添加时附带子view想要的显示参数),代码如下
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); View main = findViewById(R.id.main); //构建子view想要显示的参数 RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT ,ViewGroup.LayoutParams.WRAP_CONTENT); lp.addRule(RelativeLayout.CENTER_HORIZONTAL); TextView text = new TextView(this); text.setText("test"); text.setGravity(Gravity.CENTER_HORIZONTAL); ((ViewGroup)(main)).addView(text,lp); }}
总结
当动态添加布局时,尽量的将子view的LayoutParams定义好,然后使用addView(View child, LayoutParams params)方法进行添加,避免由于不同布局的默认参数不同,产生结果的不统一;另外使用LayoutParams时,应该是父布局是什么类型的ViewGroup,那么就是使用什么类型的LayoutParams。也就是父布局是FrameLayout,那么就是用FrameLayout.LayoutParams,因为不同ViewGroup的LayoutParams的参数和实现是不同的,务必保持一致。
- addView()后子布局不居中问题总结与分析
- HorizontalScrollView嵌套横向的LinearLayout,addView后 子view无法居中显示问题
- 关于循环addView子布局监听的问题
- 布局添加控件问题——addView
- addView后,切换界面,屏幕显示问题
- WindowManager addView不上去的问题?
- Android Inflater addview导致加载子布局match_parent无效
- addView导致子布局“match_parent”属性失效详解
- ViewSwitcher 子布局无法居中 /子布局无法填满容器问题
- css布局 居中总结
- css布局 居中总结
- css布局,居中总结
- 布局居中问题
- CSS布局居中问题
- ImgeView通过加载布局,addview宽高无效问题
- div使用margin:0 auto居中后,如何使子div实现流式布局
- 关于WindowManager.addView() 不显示添加的View的问题!
- 子序列与子串问题总结
- 初入JavaScript(待修改)
- C
- [kotlin系列] (s2_2)属性、字段、接口、可见性修饰
- 在Angular项目中使用PrimeNG组件
- dashboard windows 前端开发环境搭建
- addView()后子布局不居中问题总结与分析
- 关于机器学习的定义的理解
- uoj 34 多项式乘法(fft入门)
- 文本(字符串)处理与正则表达式
- 搜索专题: HDU2102 A计划
- B
- CodeChef Forest Gathering —— 二分
- Nandflash和Norflash的区别简介
- java.lang.NoSuchMethodError: org.apache.poi.util.POILogger.log(I[Ljava/lang/Object;)V