文章目录
概述
今日学习内容如下:
- 了解 Miwok 语言应用的结构,在该应用中创建多屏,并使用 Intents 将多个 Activity 连接起来。
- 视图回收
- 使用 LinearLayout 和 TextView 展示数据
- 改用 ListView 和 ArrayAdapeter 展示数据
- Memory Profiler 使用
- ……
Miwok应用构建
应用概述
Miwok 是一个用来学习 Miwok语言的应用,应用打开后应该有四个可选的类别,分别为Numbers、Family Members、Colors、Phrases。当点击任意一个类别所在的区域时,将跳转到改类别所在的页面。
结构大概像下面这样:
添加Activity
分别为四个类别新建 Activity,鼠标右击 --> New --> Activity --> EmptyActivity
在首页中为每个类别创建选项,这里使用 LinearLayout 和 TextView:
OnClickListener
现在需要,当在首页点击某个类别时跳转到该类别所在的页面,这就需要监听 视图的 Onclick事件,而监听事件需要用到监听器,官方已经定义好了事件的监听器。
下面直接使用,在首页源码(MainActivity)的 OnCreate 方法方法中添加:
// Find the View that show the number category
TextView numbers = (TextView) findViewById(R.id.numbers);
// Set a clickListener on that View
numbers.setOnClickListener(new View.OnClickListener() {
// …………
});
首先使用 findViewById(R.id.numbers)
获取到了 Numbers 类别的视图,然后调用 numbers.setOnClickListener()
方法为视图设置 OnClick 事件的监听器,它接收 一个 OnClickListener 的实例。
监听器是一个 interface(接口),因此我们需要按照接口中预定义的来实现这个接口,单击事件监听器 OnClickListener 接口中只包含一个方法 void onClick(View v);
,因此单击跳转 Activity 的代码如下:
numbers.setOnClickListener(new View.OnClickListener() {
// The code in this method will be executed when the family category is clicked on.
@Override
public void onClick(View view) {
// Create a new intent to open the {@link NumbersActivity}
Intent numbersIntent = new Intent(MainActivity.this, NumbersActivity.class);
// Start the new activity
startActivity(numbersIntent);
}
});
首先在 OnClickListener 的实现类中 重写 onClick
方法,并在里面定义视图单击时需要执行的操作。
onClick
方法中一共两行代码,第一行创建了一个 Intent 实例,传入两个参数 上下文环境 Context
和 目标Activity(这是一个显式的 Intent),表示将要跳转到 NumbersActivity 页面;第二行代码 则启动这个 Intent 实例,执行真实的 跳转操作。
四个类别的页面跳转也是一样的,分别创建并设置 单击事件的监听器并 使用 Intent 让其跳转套不同的 页面即可,代码省略…………。
修改Activity显示的名称
有一个问题,在创建 Miwok 项目时指定 App Name 为 Miwok,而没有为每个Activity定义 显示的名称,所以默认采用了应用的名称,在 AndroidManifest.xml
中:
想要自定义 Activity 的名称,可以更改 activity 标签中的属性来实现,如 指定 label 属性的值即可更改相应的 Activity 显示的名称
视图回收
LinearLayout 和 TextView
在 Numbers 页面中需要显示 1 到 10 的语言列表,因此考虑使用 线性布局 加 TextView 来实现:
在 Numbers 页面的布局文件中定义一个 id 为 rootView
的 LinearLayout,它将用于垂直排列 1 到 10 的 TextView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:id="@+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".NumbersActivity" />
在 Numbers 页面的 OnCreate 方法中 填加代码 实现,先通过 findViewById
方法获取到根视图LinearLayout的实例,然后 创建一个 TextView 实例传入 this (当前Activity)作为上下文并设置 Text,最后调用根视图的 addView
方法将 TextView 添加进去,循环10次就添加了10个数字。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_numbers);
LinearLayout root = (LinearLayout) findViewById(R.id.rootView);
for (int i = 0; i < 10; i++) {
TextView t = new TextView(this);
t.setText("" + i);
root.addView(t);
}
采用这种方法,如果有 几千个数字要显示,则要循环 几千次,在手机里创建几千个TextView并渲染在手机屏幕上,这会占用较大的内存空间,因为手机屏幕能显示的TextView可能只有几个或十多个,这取决于 TextView的高宽。
设备的内存资源 是有限的。
ListView 和 ArrayAdapeter
LinearLayout + TextView 的会造成资源的浪费。
ListView 和 ArrayAdapeter 则不会,ArrayAdapeter是一个适配器 ,用于管理 视图和 数据。
假如在手机屏幕中可以显示 10 条 TextView ,而我们有 10000 个 数字需要显示,用户在滑动屏幕查看更多的数字时(上面的数字会移出屏幕,下面的数字会进入排列),适配器就会回收 移出数字的视图资源,并将改视图资源用于显示新进入的数字。
代码:
Numbers 的 xml 布局文件:
<?xml version="1.0" encoding="utf-8"?>
<ListView 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:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".NumbersActivity" />
Numbers 页面 的 onCreate 函数:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_numbers);
// Create a array list of words
ArrayList<String> words = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
words.add("" + i);
}
// Create a ArrayAdapter
ArrayAdapter<String> itemsAdapter =
new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, words);
// Get layout instance
ListView listLayout = (ListView) findViewById(R.id.list);
// Set adapter for the layout instance
listLayout.setAdapter(itemsAdapter);
}
- 首先创建了一个包含了 10000 个数字的 列表作为数据来源
- 创建一个 ArrayAdapter 用于管理字符串数据(这里是泛型),
this
参数指定上下文,android.R.layout.simple_list_item_1
是Android中一个列表项布局,words
指定数据来源 -
findViewById(R.id.list)
获取根视图 ListView 的实例 -
listLayout.setAdapter(itemsAdapter)
为 根视图绑定适配器
这样适配器就可以管理 视图视图 和 数据来源了。
最后一步的 listLayout.setAdapter(itemsAdapter);
比较复杂,ListView 定义的 设置适配器方法为 public void setAdapter(ListAdapter adapter)
要求接收一个 ListAdapter
对象,而最终转入的却是 ArrayAdapter
对象。
原因是:
ArrayAdapter 继承自 抽象类 BaseAdapter ,BaseAdapter 实现了 ListAdapter 接口,所以 ArrayAdapter 也是 ListAdapter的实现。
运行效果,图片大,只截部分,:
横线是 android.R.layout.simple_list_item_1
的布局样式
内存性能分析器使用
在 Android Studio 的工具栏中 View --> Tool Windows --> Profiler ,即可打开 Profiler,它显示一个应用内存使用量的实时图表,让您可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。
使用 Memory Profiler 对比 视图回收中,两种方法 在页面显示 1000 个数字的内存消耗情况:
LinearLouyout + TextView 的方式大概占用了 18mb 的内存 资源,ListView + ArrayAdapter 的方式则消耗了 8mb 资源,只使用到了前者的一半不到。
如果有更多的内容需要显示,两者的 内存消耗会更加明显;比如在微博 App 的首页用户可以一直往下刷,查看 几千上万条 动态 ,就使用到了 视图回收的机制。
并且 LinearLouyout 要想在屏幕中滚动查看更多内容,需要配合 ScrollView 实现;而 ListView 默认支持在屏幕中滚动浏览更多的效果。
总结
视图回收 机制在日常使用的 App 中十分常见,如微博,CSDN 等。
今天学的 内容差不多就这些了, 步骤不多,但涵盖的概念和知识点不少,慢慢来。
参考
ArrayAdapter --> BaseAdapter --> ListAdapter
关于 ListView 和 ArrayAdapter 的 Codepath 教程。
关于 ListView 的 Google I/O 演讲 Youtube 视频。