安卓入门之视图与布局

想去了解一下React NativeWeex,转念一想我特么去学Native干嘛?然后现在准备先学习安卓了,在折腾了相关的环境配置并成功跑完Hello World之后,开始试着用前端的思维来理解和学习安卓。下面是关于安卓中样式布局的整理。

<!--more-->

就目前我接触到的东西来看,安卓布局跟网页布局有不少相似之处。现在web前端正在往组件化方向发展,像Vue等框架都实现了Component系统;而安卓内置了大量的View类,代表着不同的视图(“组件”)。 对于View视图这个称谓我到现在都耿耿于怀,明明组件(或者控件)更合适嘛~然而安卓四大组件已经提前把"组件"的称为给拿去了,既然如此,像复选框之类的还是入乡随俗叫做视图吧。

1. 视图

所有在屏幕中可以看见的都是视图。View是一个超类,所有视图都是从它派生出去的,这里转一个图,理清视图之间的关系。 view继承

1.1. 基础视图

安卓内置了大量的视图控件,在Android Studio中的布局Design中可以通过拖拽的方式拜访控件(为什么我立马就联想到了Dreamwaver~)。好吧,还是老老实实背属性吧,根据经验,这种可视化的布局真的是个坑,不过用来快速写标签还是不错的。

常用的视图控件有:

  • TextView,用于放置文本标签,类似于span(这个类似貌似没有任何意义)
  • Button,默认按钮,类似于button
  • EditText,文本输入框,类似于input[type=text]
  • ImageView,图片,类似于img
  • RadioButton,单选框,类似于radio
  • Checkbox,复选框,类似于checkbox
  • Menu,菜单,这个貌似是个新东西

大部分基础视图控件都可以类比为HTML中的各种标签,每个控件都有通用的属性 ,也可能会有自身独特的属性(跟Web中也很相似)。由于现在赶时间,因此都是对着文档写demo的,附官方文档。PS:安卓中好像没有网页上那么严重依赖元素嵌套进行布局,所以UI这块不必过于担心。

除了基础视图控件,还有一些高级控件(使用起来要繁琐一些),在今后的学习中慢慢补充了。

1.2. 列表视图(List View)

列表视图是一种很常见的视图,用于展示相同布局的数据列表,可以类比为网页中的ul,ol等,但是实现列表视图要麻烦一些:

  • 列表视图只是一个容器,需要额外定义列表内部每个条目的布局,可以类比为li
  • 列表视图需要用数据去填充,数据需要通过适配器ArrayAdapter转换才能传递给ListView

因此,实现列表视图的工作主要就是上面两步。

定义布局 由于数据只是准备了最简单的字符串数组,因此一个TextView就够了

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp"
    android:textSize="16sp" >
</TextView>

数据适配 在实际的项目中,对应的列表数据一般会复杂一点,因此适配器一般是需要自定义的,不过这里的单条数据只是一个字符串,因此食用内置的ArrayAdapter就可以了

private String[] COUNTRIES = new String[] { "1", "2", "3", "4", "5"};

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

        // mylist布局上存在一个ListView容器,其id为list_view
        setContentView(R.layout.mylist);

        // 数据适配,listitem就是列表条目的布局
        ArrayAdapter<String> adpt = new ArrayAdapter<String>(this, R.layout.listitem, COUNTRIES);
        // 为ListView设置适配器
        ListView lv = (ListView) findViewById(R.id.list_view);
        lv.setAdapter(adpt);
    }

貌似内置了一个ListActivity类,可以将上面的操作进行简化,这里就不展开了。此外,在很多时候内置的适配器并不能满足我们的需求(一个条目可能会展示相当多的信息),因此需要自定义适配器,这里有不少坑我打算开另外一篇文章了,先挖个坑。

1.3. 网格视图(Grid View)

以二维的、滚动的网格显示它的子视图(view)元素

2. 布局

在web端有浮动布局,定位布局,表格布局,网格布局,flex布局等,在安卓上也有类似的概念,并且在实现上比网页要简单得多(或者说“死板”很多)。

2.1. 线性布局(Linear Layout)

线性布局是一个以线性方向显示它的子视图元素的布局,可以类比网页中的flex来理解(我现在十分怀疑flex布局是不是参考这个线性布局的。)

需要掌握的几个布局属性是

  • orientation: horizontal | vertical,指定布局的方向,跟flex-direction类似
  • layout_weight: number,指定视图的尺寸权重值,跟flex-growflex-shrink属性类似,默认为0
  • layout_gravity: top | center | bottom,指定控件在布局中的对齐方式(该属性受布局方向的影响),跟justify-content类似

安卓的布局中,屏幕适配是一个很大的问题,因此在布局中最好使用自适应而非固定的尺寸,线性布局是最常用的布局。

2.2. 相对布局(Relative Layout)

相对布局是一个以相对位置显示它的子视图元素的布局,所谓的相对位置既可以是相对于父元素的位置(上下左右),也可以是相对于兄弟视图的位置(各种对齐)。可以参考网页中的position:relative相对定位来理解,区别在于参照物不同

  • 在网页中,相对定位是相对于自身原本在文档流中的位置
  • 在安卓中,相对定位需要显示指定参照对象,然后设置对应偏移属性

需要掌握的几个布局属性是

  • layout_alignParentLeft/Top/Right/Bottom: true | false,是否靠近父元素对应的边,如果在两个相反的方向上都设置为真,则会拉长对应控件的尺寸
  • layout_centerInParent/Horizontal/Vertical: true | false,在对应方向上居中,这个比网页要简单得多
  • layout_above/below/toRightOf/toLeftOf: @id/siblingViewId,相对定位于兄弟元素对应方位
  • layout_alignLeft/Right/Top/Bottom: @id/siblingViewId,让两个控件对应边对齐

这样的话如果参照控件的位置发生改变,那是不是所有后续元素的布局都会改变呢?

2.3. 表格布局(Table Layout)

使用行和列标识一个元素的位置,跟网页中的表格基本类似,跟web中早已被抛弃的表格布局类似,安卓中的表格布局也不是很常用。

需要掌握的几个布局属性:

  • shinkColumns: true | false设置表格是否收缩
  • <TableRow></TableRow>定义行
  • layout_column/span,定义控件的列数索引,以及该空间可以占的列宽(几列)

2.4. 其他布局

帧布局(Frame Layout) 最简单的布局,没有任何定位方式,所有的控件都摆放在左上角

绝对布局(Absolute Layout) 以坐标的方式来定位在屏幕上位置,直接设置View的坐标,感觉跟position:fixed应该挺像的,这个布局现在官方已经不推荐使用。

标签布局(Table Layout) 是一个ViewGroup以标签的方式显示它的子视图(view)元素,就像在Firefox中的一个窗口中显示多个网页一样。

3. 布局复用

安卓本身就提供了组件化开发的思想,因此有不少可以减少布局文件上代码重复问题的方法。

3.1. 引入布局

可以在一个layout中引入另外一个layout,回想在ThinkPHP中引入公共模板的方式,预先定义一个模板片段,然后在对应的页面中使用<include />标签引入,在安卓中同样可以

<include layout="@layout/commonTitle" />

3.2. 自定义控件

通过继承View类或者某个控件子类,我们可以自定义相关的控件

public class TitleLayout extends LinearLayout {
    public TitleLayout(Context context, AttributeSet attrs){
        super(context, attrs);
        // R.layout.title 是对应的布局清单
        LayoutInflater.from(context).inflate(R.layout.title, this);
    }
}

然后在对应的layout中引入就可以了,需要注意的是这里需要指明控件的完整类名和包名

<com.example.admin.myapplication.TitleLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
</com.example.admin.myapplication.TitleLayout>

这跟上面直接引入布局的效果是相同的,但是为什么还要使用自定义控件呢?因为,自定义控件可以封装相同的事件处理函数及相关逻辑,在布局被大量复用的情况下比单单引入布局然后再每个活动中去注册相关的事件要省事多了。

4. 小结

关于安卓布局这块暂时就整理到这里,由于只是这两天大致过了一遍,难免有很多遗漏和理解错误的地方,后面再回来补充吧。总之,先快速了解安卓整体结构再继续深入。另外,我还专门在github开了一个项目记录相关的demo