LayoutParams——子控件使用ViewGroup.LayoutParams的情况

转自http://www.cnblogs.com/ajeyone/p/layoutparams.html?utm_source=tuicool&utm_medium=referral

实例:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LinearLayout ll = new LinearLayout(this);
        ll.setOrientation(LinearLayout.VERTICAL);

        TextView tv = new TextView(this);
        tv.setText("I am a text view, haha");
        tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        ll.addView(tv);

        setContentView(ll);
    }

如果仔细看上面那段代码,会发现有个问题,设置TextView的LayoutParams是ViewGroup.LayoutParams,而不是LinearLayout.LayoutParams。查看源代码可以看到LinearLayout.LayoutParams是ViewGroup.LayoutParams的子类,那么会不会产生运行时错误?下面通过分析源代码找到这个问题的答案。

使Layout与View发生关系的语句是LinearLayout.addView(),先找到LinearLayout.java(frameworks/base/core/java/android/widget/LinearLayout.java)发现没有override这个方法,到父类ViewGroup(frameworks/base/core/java/android/view/ViewGroup.java)中寻找addView。

  public void addView(View child) {
        addView(child, -1);
    }
    public void addView(View child, int index) {
        LayoutParams params = child.getLayoutParams();
        if (params == null) {
            // ......
        }
        addView(child, index, params);
    }
    public void addView(View child, int index, LayoutParams params) {
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }


addView有N个重载(overload)的版本,注意第二个是直接获取Child View的LayoutParams,这里没有经过处理,最后传递给了addViewInner()方法,再来看addViewInner()方法的代码:

 private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

        // ...

        if (!checkLayoutParams(params)) {
            params = generateLayoutParams(params);
        }
        
        // ...
    }

发现里面有这样两个调用checkLayoutParams()和generateLayoutParams()可能会修改或替换传进来的params,找到这两个函数发现ViewGroup中的实现几乎是空的:

 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return  p != null;
    }
    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return p;
    }


让我们回到LinearLayout里看看有没有override这两个方法:

@Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LinearLayout.LayoutParams;
    }
    @Override
    public LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LinearLayout.LayoutParams(p);
    }

checkLayoutParams做的事情是检查这个LayoutParam对象是否是LinearLayout.LayoutParams的实例,按照刚开始写的代码运行到这里就返回false了,于是执行generateLayoutParams,通过ViewGroup.LAyoutParams创建一个新的LinearLayout.LayoutParams。这样就保证了使用的一定是LinearLayout.LayoutParams或者其子类。再看一下LinearLayout.LayoutParams的构造函数:

   public float weight;
        public int gravity = -1;
        public LayoutParams(ViewGroup.LayoutParams p) {
            super(p);
        }
        public LayoutParams(ViewGroup.MarginLayoutParams source) {
            super(source);
        }
        public LayoutParams(LayoutParams source) {
            super(source);

            this.weight = source.weight;
            this.gravity = source.gravity;
        }

显然应该调用的是第一个构造函数,然后weight和gravity的值取默认值。为什么要定义第一个和第二个构造函数?因为LinearLayout.LayoutParams继承自ViewGroup.MarginLayoutParams,而ViewGroup.MarginLayoutParams继承自ViewGroup.LayoutParams。可见二者是LinearLayout.LayoutParams继承树上的所有祖先类。添加这两个构造方法使能通过祖先类对象构造自己,配合上面的check和generate使不适配的LayoutParams变得可用。

根据以上分析,可以得出一个小结论,使用代码构建界面的时候,可以随意使用ViewGroup.LayoutParams或者ViewGroup.MarginLayoutParams,因为所有的Layout中的LayoutParams都继承自他们,但是也会付出一点点代价就是会新创建一个对象。不过毕竟靠代码构建界面的场景很少,一般用XML就可以了


总结:

1.设置View时候使用的LayoutParams会被其父控件强制转换为父控件的LayoutParams,也就是说若该View的父控件为LinearLayout则就会被强制转换为LinearLayout.LayoutParams,若是该View的父控件为RelativeLayout则就会被强制转换为RelativeLayout.LayoutParams。

2.设置View时候使用的LayoutParams的值不能为与自己同级的LayoutParams,否则会报类转换错误, 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值