Android 动态布局开车

相信从我们写下"hello world"的小demo开始,我们就有了一个观念,Android UI布局是通过layout目录下的XML文件定义的。当然,xml文件的优势很多,可预览,结构清晰等,但是,总有那么些时候,在实现需求的时候用xml的方式会不太灵活,比如布局要求动态变化,这个时候,我们通过使用java代码来控制,在程序运行时,动态的实现对应布局。
我们可以从两个方面,简单的了解一下:

  • 动态添加view,摆脱layout下的xml;
  • 熟悉Drawable子类,摆脱drawable下的xml;

动态添加View

我们日常使用的UI组件大致可以分为两类:

  • 控件(View),如Button、ImageView,一般用于呈现内容和交互;
  • 容器(ViewGroup),如RelativeLayout、LinearLayout,一般用来装控件和容器;

所以,我们在用代码动态添加布局的时候,也需要遵循在xml中编写代码的规则,来段代码直观了解。

1.首先,我们因为不使用xml文件来setContentView了,所以,先new一个父布局:

        //添加父布局
        RelativeLayout root = new RelativeLayout(this);
        root.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
                                                        ViewGroup.LayoutParams.MATCH_PARENT));
        root.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
        setContentView(root);

2.然后,我们在页面中间添加一个button

        //添加一个在页面中间的btn
        Button button = new Button(this);
        button.setId(View.generateViewId());
        button.setText("Hello");
        RelativeLayout.LayoutParams btnParams = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        btnParams.addRule(RelativeLayout.CENTER_IN_PARENT);
        button.setLayoutParams(btnParams);
        root.addView(button);

到这里,我们可以看见,只需要3步,一个控件(button)就已经添加到容器(root)中了:

  • new Button(),并初始化控件相关属性;
  • 根据root的类型,new button的 LayoutParams;
  • 最后把button添加到容器中, root.addView(button);

3.嗯,我们再拓展一下,在button的右下方添加一个listview,

        //添加一个list 在button右下方
        ListView listView = new ListView(this);
        listView.setId(View.generateViewId());
        listView.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
        listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, 
                new String[]{"one", "two", "three"}));
        RelativeLayout.LayoutParams listParams = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        listParams.addRule(RelativeLayout.BELOW, button.getId());
        listParams.addRule(RelativeLayout.RIGHT_OF, button.getId());
        listView.setLayoutParams(listParams);
        root.addView(listView);

需要注意的是,上面代码中的addRule()方法,该方法有两种重载方法:

  • addRule(int verb)
    该方法表示所设置节点的属性不能与其他兄弟节点相关或者属性值为布尔值。比如btnParams.addRule(RelativeLayout.CENTER_IN_PARENT)就表示在RelativeLayout中的相应节点是基于父布局居中。
  • addRule(int verb,int anchor)
    该方法表示所设置节点的属性必须关联其他兄弟节点或者属性值为布尔值。比如listParams.addRule(RelativeLayout.BELOW, button.getId())就表示RelativeLayout中的相应节点放置在一个
    button这个兄弟节点的下面。

BTW,规则如果定义的是一个view相对于另一个view的,一定要初始化另一个view(button)的id不为0,否则规则会失效。通常,为了防止id重复,建议使用系统方法来生成id,也就是第二段代码中的button.setId(View.generateViewId())。

最终,我们出来的效果是这个样子的:

Drawable子类

OK,第二节,我们来看看drawable下的xml文件。drawable目录下的文件,通常是定义了一些selector、shape等,那么,考虑到一个场景,如果我们需要使用后台下发的图片,而不是使用res下面现有的资源呢?根据上一节的经验,xml定义能实现的,java代码中肯定也能实现。下面,就介绍几个常用的Drawable子类:
1. StateListDrawable:对应selector,主要用来描述按钮等的点击态。
下面这段代码大家都熟悉:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/numpad_button_bg_selected" android:state_selected="true"></item>
    <item android:drawable="@drawable/numpad_button_bg_pressed" android:state_pressed="true"></item>
    <item android:drawable="@drawable/numpad_button_bg_normal"></item>
</selector>

这是一个给button使用的背景选择,这种不同状态显示不同背景的xml文件我们称为selector。其实selector的本质是一个drawable对象。如果要用java代码实现上述的selector该如何实现呢?直接上代码:

button.setBackground(DrawableUtil.addStateListBgDrawable(this, R.mipmap.ic_background_normal,
                R.mipmap.ic_background_selected));

    /**
     * selector
     *
     * @param context
     * @param idNormal
     * @param idPressed
     * @return
     */
    public static StateListDrawable addStateListBgDrawable(Context context, int idNormal, int idPressed) {
        StateListDrawable drawable = new StateListDrawable();
        drawable.addState(new int[]{android.R.attr.state_selected}, context.getResources().getDrawable(idPressed));
        drawable.addState(new int[]{android.R.attr.state_pressed}, context.getResources().getDrawable(idPressed));
        drawable.addState(new int[]{android.R.attr.state_enabled}, context.getResources().getDrawable(idNormal));
        drawable.addState(new int[]{}, context.getResources().getDrawable(idNormal));

        return drawable;
    }

简单来说,2步:

  • new 一个StateListDrawable对象;
  • 给drawable对象挨个添上状态,并添加图片资源,和xml中逻辑一样;

2.GradientDrawable:对应渐变色。
直接上代码:

button2.setBackground(DrawableUtil.addGradientBgDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
                new int[]{getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorAccent)}));

/**
     * 渐变色
     * @param orientation
     * @param colors
     * @return
     */
    public static GradientDrawable addGradientBgDrawable(GradientDrawable.Orientation orientation, int[] colors) {
        GradientDrawable drawable = new GradientDrawable();
        drawable.setOrientation(orientation); //定义渐变的方向
        drawable.setColors(colors); //colors为int[],支持2个以上的颜色

        return drawable;
    }

简单来说,也是2步:

  • new一个GradientDrawable 对象;
  • 给对象设置orientation和colors;

最终效果如下:

Paste_Image.png

demo下载地址:https://github.com/dys1715/DynamicLayoutDemo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容