简析静态xml布局如何通过动态代码实现

Posted 开心每一日

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简析静态xml布局如何通过动态代码实现相关的知识,希望对你有一定的参考价值。

先看一下xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/contentView"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <!--静态xml加载部分-->
    <LinearLayout
        android:id="@+id/ll01"
        android:layout_width="0dp"
        android:background="@android:color/holo_orange_dark"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_static_loading"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="我是静态xml加载的布局"/>

        <TextView
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="静态xml加载的布局"/>

        <Button
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="left|center"
            android:text="按钮1"/>

        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:scaleType="fitCenter"
            android:src="@mipmap/q01"/>

        <TextView
            android:id="@+id/tv_margin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:background="#000"
            android:text="marginLeft为10dp"
            android:textColor="#fff"
            android:textSize="20sp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="20dp"
            android:background="#000"
            android:text="padLeft为20dp"
            android:textColor="#fff"
            android:textSize="20sp"/>
    </LinearLayout>

    <!--动态xml加载部分-->
  <!--  <LinearLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">

    </LinearLayout>-->

</LinearLayout>



 
<LinearLayout
        android:id="@+id/ll01"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">
将会从此处开始用动态代码重复下面的写法,当然也少不了一些说明。

整体架构如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/contentView"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <!--静态xml加载部分-->
    <LinearLayout
        android:id="@+id/ll01"
        android:layout_width="0dp"
        android:background="@android:color/holo_orange_dark"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_static_loading"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="我是静态xml加载的布局"/>

        <TextView
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="静态xml加载的布局"/>

        <Button
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="left|center"
            android:text="按钮1"/>

        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:scaleType="fitCenter"
            android:src="@mipmap/q01"/>

        <TextView
            android:id="@+id/tv_margin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:background="#000"
            android:text="marginLeft为10dp"
            android:textColor="#fff"
            android:textSize="20sp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="20dp"
            android:background="#000"
            android:text="padLeft为20dp"
            android:textColor="#fff"
            android:textSize="20sp"/>
    </LinearLayout>

    <!--动态xml加载部分-->
  <!--  <LinearLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">

    </LinearLayout>-->

</LinearLayout>





会看到布局文件中有宽度和字体大小单位分别是dp和sp,然而动态加载时,只能以px为单位,因此,我们先把单位初始化为px单位,看mian.java:

package com.demo.linearlayoutdemo;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainLLDemoActivity extends AppCompatActivity {
    /**
     * 150dp对应的px值
     */
    private int on150Dp;
    /**
     * 1dp = ?px
     */
    private int perDp;

    private int on10Dp;

    private int on20Dp;

    private float on20Sp ;

    private TextView tvStaticLoading;
    private TextView tvMargin;
    private ViewGroup contentView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_lldemo);
        tvStaticLoading = (TextView) findViewById(R.id.tv_static_loading);
        tvMargin = (TextView) findViewById(R.id.tv_margin);
        contentView = (ViewGroup) findViewById(R.id.contentView);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        on150Dp = tvStaticLoading.getWidth();
        perDp = on150Dp / 150;
        on10Dp = perDp * 10;
        on20Dp = perDp * 20;
        on20Sp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20,
                getResources().getDisplayMetrics());
    }

}

此处,我们分2个方法拿到dp和sp,在这里会看到,博主是

@Override
    public void onWindowFocusChanged(boolean hasFocus) {

    }
在这个方法里面取值,因为在onCreate中是取不到值得。看看以下这个与众不同的取法。

     on20Sp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, 
            getResources().getDisplayMetrics());
这个方法也是用于将dp和sp转换成对应的尺寸,看看TypedValue.applyDimension做了啥:

    /**
     * Converts an unpacked complex data value holding a dimension to its final floating 
     * point value. The two parameters <var>unit</var> and <var>value</var>
     * are as in {@link #TYPE_DIMENSION}.
     *  
     * @param unit The unit to convert from.
     * @param value The value to apply the unit to.
     * @param metrics Current display metrics to use in the conversion -- 
     *                supplies display density and scaling information.
     * 
     * @return The complex floating point value multiplied by the appropriate 
     * metrics depending on its unit. 
     */
    public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }
可以看出,他是将对应的px,dp,sp,pt,in,mm转换成对应的尺寸,其中dp和px有一个叫density系数维系关系,sp和对应的尺寸也有一个scaledDensity系数维系关系。
好的,现在开始说说动态代码写布局的实现方式了。之前说过了,在onCreate的时候是得不到对应的dp和sp值得,所以我们在得到值后再动态加载。

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        on150Dp = tvStaticLoading.getWidth();
        perDp = on150Dp / 150;
        on10Dp = perDp * 10;
        on20Dp = perDp * 20;
        dynamicXML();
    }

    private void dynamicXML() {
        
    }

此时,先看看开始的效果:




这是一开始的部分:

    <LinearLayout
        android:id="@+id/ll01"
        android:layout_width="0dp"
        android:background="@android:color/holo_orange_dark"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">
动态写法:

    private void dynamicXML() {
        LinearLayout topLL = getLinearLayout();
    }

    private LinearLayout getLinearLayout(){
        LinearLayout ll = new LinearLayout(this);
        LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,1);
        contentView.addView(ll,lllp);
        ll.setBackgroundColor(Color.BLUE);
        ll.setOrientation(LinearLayout.VERTICAL);
        return ll;
    }
此时,看看效果如何:


LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,1);
对应的参数分别如下:

(int width, int height, float weight) 
这就相当于静态xml的如下写法:

 <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
将布局添加到xml布局中,最上层的那个LInearLayout,contentView的排序是横向方式,将背景设置成蓝色,排序方式设置为纵向,反正就是和上述的xml一样。
        contentView.addView(ll,lllp);
        ll.setBackgroundColor(Color.BLUE);
        ll.setOrientation(LinearLayout.VERTICAL);

接下来,要实现这1个

        <TextView
            android:id="@+id/tv_static_loading"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="我是静态xml加载的布局"/>


    private void dynamicXML() {
        LinearLayout topLL = getLinearLayout();
        setTextViewOne(topLL);
    }



    private void setTextViewOne(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.gravity = Gravity.RIGHT;
        textView.setBackgroundColor(Color.WHITE);
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我是动态加载的布局");
        vp.addView(textView,ll);
    }
效果图如下:


将“我是动态加载的布局”,改成和上面一样后,在看看效果:



这里的效果和左边的一样,150DP刚好是那字体的长度。

然后再写:

       <TextView
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="静态xml加载的布局"/>

把tv1和tv2的一起看

    private LinearLayout getLinearLayout(){
        LinearLayout ll = new LinearLayout(this);
        LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,1);
        contentView.addView(ll,lllp);
        ll.setBackgroundColor(Color.BLUE);
        ll.setOrientation(LinearLayout.VERTICAL);
        return ll;
    }

    private void setTextViewOne(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.gravity = Gravity.RIGHT;
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我是动态加载的布局");
        vp.addView(textView,ll);
    }

    private void setTextViewTwo(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我也是动态加载的布局");
        textView.setGravity(Gravity.CENTER);
        vp.addView(textView,ll);
    }
在One的方法,是调用了ll.gravity,而Two的方法,则是调用textview.gravity,然后在看看xml上的差异。

        <TextView
            android:id="@+id/tv_static_loading"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="我是静态xml加载的布局"/>

        <TextView
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="静态xml加载的布局"/>
可以看到,如果是
            android:layout_gravity="right"
则调用ll.gravity,如果是
            android:gravity="center"
则调用textview.gravity,得出一个规律就是,凡是android:layout_打头的属性,都要用LayoutParams调用,在这里因为父布局是LInearLayout,所以用到LinearLayout.LayoutParams。

接下来动态设置下面这2个控件:

        <Button
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="left|center"
            android:text="按钮1"/>

        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:scaleType="fitCenter"
            android:src="@mipmap/q01"/>

添加代码如下:

    private void setButton(ViewGroup vp){
        Button btn = new Button(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(0,on10Dp,0,0);
        btn.setGravity(Gravity.LEFT|Gravity.CENTER);
        btn.setText("按钮1");
        vp.addView(btn,ll);
    }

    private void setImageView(ViewGroup vp){
        ImageView iv = new ImageView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(on150Dp, on150Dp);
        iv.setScaleType(ImageView.ScaleType.FIT_CENTER);
        iv.setImageResource(R.mipmap.q01);
        vp.addView(iv,ll);
    }
调用方法也是雷同的:

    private void dynamicXML() {
        LinearLayout topLL = getLinearLayout();
        setTextViewOne(topLL);
        setTextViewTwo(topLL);
        setButton(topLL);
        setImageView(topLL);
    }

此时,看看效果图:


会发现蓝色部分的按钮比较奇葩,那是因为通过xml设置的button默认会加上一个选择器,我们也通过代码给奇葩的button加上一个选择器看看:

        btn.setBackgroundResource(android.R.drawable.btn_default);
此时,再看看效果图:



正常了吧!

最后2个了,先看看他们的静态设置:

        <TextView
            android:id="@+id/tv_margin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:background="#000"
            android:text="marginLeft为10dp"
            android:textColor="#fff"
            android:textSize="20sp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="20dp"
            android:background="#000"
            android:text="padLeft为20dp"
            android:textColor="#fff"
            android:textSize="20sp"/>
先设置倒数第二个的动态加载:

    private void setLastButOne(ViewGroup vp){
        TextView tv = new TextView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(on10Dp,0,0,0);
        tv.setBackgroundColor(Color.rgb(0,0,0));
        tv.setText("marginLeft为10dp");
        tv.setTextColor(Color.rgb(0xff,0xff,0xff));
        tv.setTextSize(on20Sp);
        vp.addView(tv,ll);
    }
再看看效果图:



会发现,设置的on20Sp好像不太给力,然后再将它设置成:

        tv.setTextSize(20);
再看看效果图:

此刻好像20也是对应20SP,为什么sp可以这样设置,而没有直接设置dp的方法,看看源码的setTextSize是怎样的:

    public void setTextSize(float size) {
        setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
    }
会看到,原来他已经把20转换成SP单位了,所以设置字体大小的时候不用我们操心了!

最后一步的代码则是:

    private void setLastOne(ViewGroup vp){
        TextView tv = new TextView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        tv.setPadding(on20Dp,0,0,0);
        tv.setBackgroundColor(Color.rgb(0,0,0));
        tv.setText("padLeft为20dp");
        tv.setTextColor(Color.rgb(0xff,0xff,0xff));
        tv.setTextSize(20);
        vp.addView(tv,ll);
    }
此时注意padding和margin的区别:

在xml上:

            android:layout_marginLeft="10dp"
            android:paddingLeft="20dp"
一个是android:layout_开头的,一个则不是,根据上面说到的,也可以猜到代码的写法了。

        ll.setMargins(on10Dp,0,0,0);
        tv.setPadding(on20Dp,0,0,0);


好的,就说到这里了,附上全部代码。

xml文章开头就有了,因此这里附上的是main.java文件的代码:

package com.demo.linearlayoutdemo;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainLLDemoActivity extends AppCompatActivity {
    /**
     * 150dp对应的px值
     */
    private int on150Dp;
    /**
     * 1dp = ?px
     */
    private int perDp;

    private int on10Dp;

    private int on20Dp;

    private float on20Sp ;

    private TextView tvStaticLoading;
    private TextView tvMargin;
    private ViewGroup contentView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_lldemo);
        tvStaticLoading = (TextView) findViewById(R.id.tv_static_loading);
        tvMargin = (TextView) findViewById(R.id.tv_margin);
        contentView = (ViewGroup) findViewById(R.id.contentView);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        on150Dp = tvStaticLoading.getWidth();
        perDp = on150Dp / 150;
        on10Dp = perDp * 10;
        on20Dp = perDp * 20;
        on20Sp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20,
                getResources().getDisplayMetrics());
        dynamicXML();
        Toast.makeText(this, "tvMargin.getTextSize():" + tvMargin.getTextSize(), Toast.LENGTH_SHORT).show();
    }

    private void dynamicXML() {
        LinearLayout topLL = getLinearLayout();
        setTextViewOne(topLL);
        setTextViewTwo(topLL);
        setButton(topLL);
        setImageView(topLL);
        setLastButOne(topLL);
        setLastOne(topLL);
    }

    private LinearLayout getLinearLayout(){
        LinearLayout ll = new LinearLayout(this);
        LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,1);
        contentView.addView(ll,lllp);
        ll.setBackgroundColor(Color.rgb(0,0,255));
        ll.setOrientation(LinearLayout.VERTICAL);
        return ll;
    }

    private void setTextViewOne(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.gravity = Gravity.RIGHT;
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我是动态加载的布局");
        vp.addView(textView,ll);
    }

    private void setTextViewTwo(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我也是动态加载的布局");
        textView.setGravity(Gravity.CENTER);
        vp.addView(textView,ll);
    }

    private void setButton(ViewGroup vp){
        Button btn = new Button(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(0,on10Dp,0,0);
        btn.setGravity(Gravity.LEFT|Gravity.CENTER);
        btn.setText("按钮1");
        btn.setBackgroundResource(android.R.drawable.btn_default);
        vp.addView(btn,ll);
    }

    private void setImageView(ViewGroup vp){
        ImageView iv = new ImageView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(on150Dp, on150Dp);
        iv.setScaleType(ImageView.ScaleType.FIT_CENTER);
        iv.setImageResource(R.mipmap.q01);
        vp.addView(iv,ll);
    }

    private void setLastButOne(ViewGroup vp){
        TextView tv = new TextView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(on10Dp,0,0,0);
        tv.setBackgroundColor(Color.rgb(0,0,0));
        tv.setText("marginLeft为10dp");
        tv.setTextColor(Color.rgb(0xff,0xff,0xff));
        tv.setTextSize(20);
        vp.addView(tv,ll);
    }

    private void setLastOne(ViewGroup vp){
        TextView tv = new TextView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        tv.setPadding(on20Dp,0,0,0);
        tv.setBackgroundColor(Color.rgb(0,0,0));
        tv.setText("padLeft为20dp");
        tv.setTextColor(Color.rgb(0xff,0xff,0xff));
        tv.setTextSize(20);
        vp.addView(tv,ll);
    }
}

最后的效果展示图:







以上是关于简析静态xml布局如何通过动态代码实现的主要内容,如果未能解决你的问题,请参考以下文章

AOP 实现的原理简析

静态布局自适应布局流式布局响应式布局弹性布局简析

在Android中,如何将数据从类传递到相应的布局/片段文件?

支持动态或静态片段的不同屏幕尺寸?

如何将视图动态添加到已在 xml 布局中声明的 RelativeLayout?

C++多态 --- 多态实现原理简析