Just for memory.
2.1 什么是活动
活动(Activity)是一种可以包含用户界面的组件。个人感觉有点类似于 WinForm 里的窗体(Form)。
2.2 活动的基本用法
创建活动
右击包名→New→Activity→Empty Activity,根据提示完成向导即可。
项目中的任何活动都需要重写 Activity 的onCreate()
方法,Android Studio 会自动完成。例如:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
如果在创建活动的时候勾选了 Generate Layout File,那么 Android Studio 会自动生成布局并添加相关代码,还会在 AndroidManifest 中注册;如果不勾选的话,那么上面代码中的 setContentView(R.layout.activity_main);
不会添加。
手动创建和加载布局
创建布局
右击 app/src/main/res 目录→New→Directory,新建 layout 目录。右击 layout→New→Layout resource file,填写名称并选择根元素即可。然后回到活动的代码,在 onCreate
方法中加入上面提到的 setContentView(R.layout.activity_main);
。
注册活动
之后在 AndroidManifest.xml 文件中的 <application>
节中注册活动,添加:
<activity android:name=".[ActivityName]"></activity>
在 <activity>
节中加入:
<action android:name"android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
这里将这个活动注册为主活动,也就是在手机桌面点击图标进入的活动。如果没有将任何活动注册为主活动,那么在主屏幕是不会出现应用图标的。
使用 Toast
Toast 就是程序暂时显示的通知,通常以灰色为背景,出现在屏幕底部——不过这些都是可以自定义的。
以点击按钮弹出 Toast 为例,在活动的 onCreate()
方法中添加:
Button button2 = (Button) findViewById(R.id.button_2); button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(SecondActivity.this, data, Toast.LENGTH_SHORT).show(); } });
findViewById
方法用来获取定义在布局文件中的元素,传入的值取决于在布局文件中指定的 android:id
属性。然后使用 setOnClickListener()
方法为按钮注册监听器——听起来有点像在 C# 中为按钮添加事件,点击按钮就会执行监听器中的 onClick
方法,需要执行的代码就放在 onClick()
中。
下面来看看 Toast 类常用的 makeText
方法:
/** * @param context 在哪个对象中显示 Toast * @param msg 消息内容 * @param duration 持续时间 */ Toast.makeText(Context context, String msg, int duration)
duration
可取的值有 LENGTH_SHORT
和 LENGTH_LONG
,前者约 2.5s,后者约 3s。
使用 makeText
创建出一个 Toast 对象,然后使用 show()
方法显示。
添加菜单
在 res 目录下新建 menu 目录,在 menu 目录下新建 Menu resource file。用下面的 XML 代码设置菜单元素:
<item android:id="[id]" android:title="[title]" />
android:id
指定唯一标识符,android:title
指定显示出来的菜单项目的文字。
在活动中重写 onCreateOptionsMenu()
方法。这里可以按 Ctrl + O,然后敲前几个字母快速插入主要代码。
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; }
通过 getMenuInflater()
方法得到 MenuInflater
对象,向 inflate
方法传入资源文件和 Menu
对象,再让 onCreateOptionsMenu
返回 true
表示允许显示菜单。
接下来需要让菜单响应事件,重写 onOptionsItemSelected()
方法:
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.add_item: Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show(); break; case R.id.remove_item: Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show(); break; default: } return true; }
通过 item.getItemId()
判断点击的是什么,之后自行处理其余逻辑即可。
销毁活动
使用 finish();
即可销毁当前活动。
意图(Intent)
Intent 用于在 Android 程序各个组件中进行交互,可以指定要执行的操作或者传递数据。一般用于启动活动、服务和发送广播。
显式 Intent
button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(FirstActivity.this, SecondActivity.class); startActivity(intent); } });
Intent 的构造参数有多个重载,这里使用的是:
/** * @param packageContext 启动活动的上下文 * @param cls 想要启动的目标活动 */ Intent(Context packageContext, Class<?> cls)
然后将构造的 Intent 传入 startActivity()
方法。这里传入 FirstActivity.this
作为上下文,SecondActivity.class
作为目标活动,即在 FirstActivity 中启动 SecondActivity,“意图”显而易见,故称显式 Intent。
隐式 Intent
相比显式 Intent,隐式 Intent 不会明确指出想要启动哪个活动,而是指定 action
和 category
,交由系统分析该如何处理。
每个程序都可以指定哪个活动处理何种隐式 Intent,只需要向系统注册即可。注册过程是,向 AndroidManifest.xml 加入:
<activity android:name=".SecondActivity" android:label="This is SecondActivity"> <intent-filter> <action android:name="com.example.myapplication.ACTION_START" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.example.myapplication.MY_CATEGORY" /> </intent-filter> </activity>
在 <action>
标签中指定当前的活动可以响应何种 action
,此处为 com.example.myapplication.ACTION_START
。而 <category>
中更确切地指出当前 action
该在何种环境中被响应,常用取值为:
DEFAULT
:默认的执行方式,按照普通活动的执行方式执行。表示所有 Intent 都可以激活它。
HOME
:设置该组件为 Home Activity,也就是作为 Launcher(桌面)。
PREFERENCE
:设置该组件为 Preference。
LAUNCHER
:设置该组件为在当前应用程序启动器中优先级最高的
活动 ,通常为入口 ACTION_MAIN
配合使用。
BROWSABLE
:设置该组件可以使用浏览器启动。表示该活动只能用来浏览网页。
GADGET
:设置该组件可以内嵌到另外的活动中。
在每个 Intent 中只能指定一个 action
,但可以指定多个 category
。
修改 Intent 的构造函数以传递隐式 Intent:
Intent intent = new Intent("com.example.myapplication.ACTION_START"); startActivity(intent);
对于 category
,除了系统定义的之外,我们还可以自定义。只需要在 <intent-filter>
中声明即可。
隐式 Intent 的其他用法
隐式 Intent 还可以用于启动其他应用的活动。这里以打开网页为例,修改点击按钮代码:
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("https://moefactory.com")); startActivity(intent);
Intent.ACTION_VIEW
即 android.intent.action.VIEW
,用于显示用户的数据。会根据提供的数据类型来自动决定启动哪个活动。
setData()
为 Intent 传入需要处理的数据,该方法接受一个Uri
类。这里使用Uri
类来解析网址——值得一提的是,这里的Uri
类和 C# 里的用法很类似的。
此时点击按钮,系统会调用默认浏览器打开指定的网页。当然我们也可以假装自己的程序能响应网址,这样的话需要在 <intent-filter>
中配置一个 <data>
,用于指定能响应什么数据。比如上面我们指定了一个 HTTPS 的网址,那么我们就加入:
<data android:scheme="https" />
<data>
标签可以指定下列内容:
android:scheme
:URI 的协议。
android:host
:URI 的主机名,也就是网站中的域名或者 IP。
android:port
:URI 的端口号。
android:path
:URI 中除去协议、主机名、端口号之外的路径。
android:mimeType
:指定可以处理的 MIME 类型。
此时运行程序,点击按钮,会提示我们选择打开方式,列表里会列出能响应该 Intent 的所有程序。
根据传入 URI 的不同,系统也会做出不同的响应。比如 URI 设置为 tel:10086
就会调用默认电话程序并将 10086 作为待拨号码。
在活动间传递数据
向下一个活动传递数据
有时候我们需要在活动之间传递数据,此时我们可以通过 Intent 的 putExtra()
方法来指定,语法如下(此处可以简单理解为键值对):
/** * @param name 键 * @param value 值,可以接受多种数据类型 */ Intent.putExtra(String name, <?> value);
之后在下一个活动中取出数据。这里要用到 getIntent()
和另一个方法。前者的语法没有参数,返回一个 Intent
类。然后,根据不同的数据类型,需要用不同的方法获得数据,比如取得字符串则用 getStringExtra()
,取出布尔值则用 getBooleanExtra()
等。虽然方法名称不同,但都只需要传入一个参数 name
来指定要接受的数据即可。此处的 name
和 putExtra()
的 name
键对应。
向上一个活动返回数据
首先我们需要让被启动的活动知晓需要返回数据。使用 startActivityForResult()
代替 startActivity()
,语法如下:
/** * @param intent 要传递的 Intent * @param requestCode 唯一请求码 */ startActivityForResult(Intent intent, int requestCode);
在被启动的活动中,除了使用 putExtra()
来指定要返回的数据之外,还要使用 setResult()
来返回一个结果,以便于上一个活动处理。语法如下:
/** * @param requestCode 唯一请求码,与从上一个活动传入的值对应 * @param intent 要传递的 Intent,此处构造其仅为返回数据 */ setResult(int resultCode, Intent intent);
在被启动的活动销毁之后,会回调上一个活动的 onActivityResult()
方法,重写之:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { switch (requestCode) { case 1: if (resultCode == RESULT_OK) { String returnedData = data.getStringExtra("data_return"); Toast.makeText(FirstActivity.this, returnedData, Toast.LENGTH_sHORT).show(); } break; } }
onActivityResult()
带有三个参数,requestCode
是被销毁的活动传入的请求码,resultCode
是在 setResult()
方法中设置的结果,data
即携带着数据的 Intent。注意区分前两者。之后可以用 switch-case 处理。
如果想要在用户按下返回键销毁活动时也能传递数据,在被启动的活动中重写 onBackPressed()
方法,加入相关代码即可。