待更新

LogCat

Android Studio中已经默认添加了LogCat工具,macOS使用control+6,Win使用Alt+6打开。
Android中的日志工具类是Log(android.util.Log),这个类中提供了如下方法:

Log.v() //这个方法打印最为琐碎的,意义最小的日志信息,对应级别verbose
Log.d() //这个方法打印一些调试信息,对应级别debug
Log.i() //这个方法打印一些警告,提示程序的潜在风险,对应级别warn
Log.e() //这个方法用于打印错误信息,对应级别error

尝试

onCreate()方法中添加一行打印日志的语句。
Log.d(“HelloWorldActivity”, “onCreate execute”);
函数传入两个参数,第一个是tag,一般传入当前的类名,主要用于过滤信息,第二个参数是msg,即想要打印的具体内容。

Activity

创建活动

定义一个Activity类的子类,在子类中重写onCreate()方法。

创建和裁剪布局

res/layout目录中新建一个XML布局文件。
尝试添加一个Button

<Button
        android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 1"
        />

其中@+id/button_1是在XML语言中定义一个id的语法。
+去掉,@id/button_1就是引用id的语法。
随后,android:layout_width指定元素宽度,match_parent表示和父元素一样。
android:layout_height自然就是高度,wrap_content表示恰好容纳就可以了。
android:text指定元素中显示的文字内容。

应用布局

onCreate()中写入:setContentView(R.layout.first_layout);传入一个布局文件的id

注册

所有的活动需要在AndroidManifest.xml中注册才能生效。

<activity
            android:name=".FirstActivity"
            android:label="This is FirstActivity">
            <intent-filter>
                <action
                    android:name=
                        "android.intent.action.MAIN" />
                <category
                    android:name=
                        "android.intent.category.LAUNCHER"/>
            </intent-filter>

可以看到,活动的注册声明要放在<application>标签内,这里是通过<activity>标签来对活动进行注册的。
首先我们要使用android:name来制定具体注册哪一个活动,这里的.FirstActivitycom.example.activitytest.FirstActivity的缩写。由于最外层的<manifest>标签中已经通过package属性指定了用户的包名,因此可以省略。
android:label指定活动中的标题栏的内容。
需要注意的是,给主活动指定的label不仅会成为标题栏,而且还会成为Laucher中应用程序的名称。
<intent-filter>标签,中的<action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER"/>使这个活动成为这个程序的主活动。
如果一个程序没有主活动,那么一般这样的程序都是作为第三方服务供其他应用在内部进行调用的。

隐藏标题栏

只需要在setContentView()方法之前加一句:
requestWindowFeature(Window.FEATURE_NO_TITLE);

在活动中使用Toast

只需要在需要触发Toast的地方加上Toast.makeText方法就行。

Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(
            FirstActivity.this, 
            "You clicked Button 1", 
            Toast.LENGTH_SHORT
         ).show();
     }
});

在活动中使用Menu

先在res中创建menu文件夹,创建一个main.xml布局文件,在XML文件中创建菜单布局。

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/add_item"
        android:title="Add"
        />
    <item
        android:id="@+id/remove_item"
        android:title="Remove"
        />
</menu>

Activity中重写onCreateOptionMenuonOptionItemSelected方法。

public boolean onCreateOptionMenu(Menu menu){
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

public boolean onOptionItemSelected(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:
            break;
    }
    return true;
}

销毁一个活动

直接调用finish方法即可。

使用Intent

Intent可以在活动之间跳转。

使用显式Intent

新建一个layout布局文件,在其中创建第二个活动的布局。
AndroidManifest.xml中进行注册。只需要注册一个name即可。
在按钮的点击事件中添加代码:

public void onClick(View v) {
    Intent intent = new Intent(
        FirstActivity.this, 
          SecondActivity.class
    );
    startActivity(intent);
}

构建一个Intent实例,传入FirstActivity.this作为上下文,传入SecondActivity.class作为活动目标。然后通过startActivity方法来执行这个Intent

使用隐式Intent

隐式方法不明确支出想要启动哪一个活动,而是指定了一系列抽象的actioncategory等信息,由系统分析调用哪个活动。
首先在AndroidManifest.xml中配置<intent-filter>

<intent-filter>
    <action 
        android:name="com.example.activity.test.ACTION_START"
        />
    <category 
        android:name="android.intent.category.DEFAULT"
        />
</intent-filter>

这个action标签指明了当前活动可以响应com.example.activity.test.ACTION_START这个actioncategory标签中包含一些附加信息,更精确地指明了当前活动能够相应的Intent中可能包含category。这两个属性同时匹配时才能相应该Intent
现在改变一下按钮的点击事件。

public void onClick(View v) {
    Intent intent = new Intent(
        "com.example.activity.test.ACTION_START"
    );
    startActivity(intent);
}

新的Intent构造方法传入了一个action字符串,可以直接相应。
这里没有设定category,因为在注册表中注册的是DEFAULT默认,会自动添加到Intent
如果我们给Intent实例添加一个categoryintent.addCategory("com.example.activitytest.MY_CATEGORY");
这样就只能匹配特定的category。同时,给注册表中再添加一个相同的category,这样就能正常运行了。

更多隐式Intent的用法

隐式Intent不仅可以启动自己程序的活动,还可以启动其他应用程序的活动,这使得Android多个应用程序之间的功能共享成为可能。
下面演示用隐式Intent打开系统浏览器。

public void onClick(View v) {
    Intent intent = new Intent(
        Intent.ACTION_VIEW
    );
    intent.setData(Uri.parse(
        "http://wzhzzmzzzy.farbox.com"
    ));
    startActivity(intent);
}

这里指定actionIntent.ACTION_VIEW,这是一个Android系统内置的动作,常量值为android.intent.action.VIEW,然后通过Url.parse方法,解析网址成一个Url对象,调用setDate将其传递进去。

标签

setData方法可以接受一个Uri对象,,指定当前Intent正在操作的数据。通常数据都是以字符串的形式传入到Uri.parse方法中解析产生。
与之对应,我们可以在<intent-filter>标签中再配置一个<data>标签,用于更加精确地指定当前活动能够响应什么类型的数据。下面是<data>标签中可以配置的内容:

android:scheme 
//用于指定数据的协议部分,如http
android:host
//用于指定数据的主机名部分,如www.baidu.com
android:port
//用于指定数据的端口部分,一般跟随在主机名后
android:path
//用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容
android:mimeType
//用于指定可以处理的数据类型,允许使用通配符的方式进行指定

只有<data>标签中指定的内容和Intent中携带的Data完全一致,当前活动才能相应。只要设置android:schemehttp,就可以相应所有的http协议的Intent了。

向下一个活动传递数据

通过putExtra()方法的重载,可以把我们想要传递的数据暂存在Intent中,启动了另一个活动之后,只需要把这些数据再从Intent中取出就可以了。比如说FirstActivity中有一个字符串,现在想把它传递到下一个活动当中:

String s = "Hello SecondActivity";
Intent intent = new Intent(
    FirstActivity.this, 
    SecondActivity.class);
intent.putExtra("extra_data", s);

然后再SecondActivity中取出数据:

Intent i = getIntent();
String s = i.getStringExtra("extra_data");
Log.d("SecondActivity", s);

首先通过getIntent方法获取到用于启动SecondActivityIntent,然后调用getStringExtra方法,传入相应的键值,就可以得到相应的数据了。如果传递int就用getIntExtra方法, 布尔值等以此类推。

返回数据给上一个活动

Activity中有startActivityFroResult方法,也是用于启动活动的,但是这个方法期望在活动销毁时返回一个结果给上一个活动。
该方法接受两个参数,第一个参数的Intent,第二个参数是请求码,用于在之后的回调中判断数据来源。

Intent intent = new Intent(
    SecondActivity.this, ThirdActivity.class
);
startActivityForResult(intent, 1);
Button button3 = (Button) findViewById(R.id.button_3);
button3.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent();
        intent.putExtra(
            "data_return", 
            "Hello FirstActivity");
        setResult(RESULT_OK, intent);
        finish();
    }
});

在活动中构建一个Intent用于传递数据。要传递的数据存放在Intent中,然后调用了setResult方法,这个方法专用于向上一个活动返回数据。
setResult方法有两个参数,第一个用于向上一个活动返回处理结果,一般只用RESULT_OK或者RESULT_CANCELED这两个值,第二个参数将带有数据的Intent传递回去。
由于我们使用startActivityForResult方法,在下一个活动销毁之后会回调上一个活动的onActivityResult方法,因此需要在FirstActivity中重写来得到返回的数据。
onActivityResult方法有三个参数,第一个参数请求码requestCode,第二个参数是返回数据时传入的处理结果resultCode,第三个是携带着返回数据的Intent data
因为一个活动中可能调用startActivityForResult启动很多不同的活动,每个活动返回的数据都会回调,因此需要检查处理结果resultCode来判断数据来源,确定数据是从SecondActivity返回的之后,我们再通过resultCode的值来判断处理结果是否成功。最后从data中取值并且打印出来。
但是还有一个问题!如果通过Back键来返回,那数据怎么带回来呢?这个时候需要重写一下onBackPressed方法。

@Override
public void onBackPressed(){
    Intent intent = new Intent();
    intent.putExtra("data_return", "Hello FirstActivity");
    setResult(RESULT_OK, intent);
    finish();
}

活动的生命周期

返回栈

Android中的活动是可以层叠的。每创建一个新活动,就会覆盖在原活动之上,点击Back键就会销毁最上面的活动,下面的一个活动就会重新显示出来。
Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。每启动一个新活动,就会入栈,每当按下Back或者调用finish(),就会弹栈。

活动状态

活动有四种状态:运行、暂停、停止、销毁。
在栈顶时运行、屏幕上可见时暂停、不再处于栈顶时停止、弹栈后销毁。

活动生存期

onCreate() //活动第一次创建时会调用
onStart()  //活动由不可见变为可见是调用
onResume() //活动准备好与用户交互时调用,此时活动位于栈顶
onPause()  //在系统准备去启动或者恢复另一个活动时调用,通常会释放一些占用CPU的数据,保存一些关键数据
onStop()   //活动完全不可见时调用,如果新活动不是对话框式则调用onStop,否则调用onPause
onDestroy()//活动被销毁之前调用,之后活动将变为销毁状态
onRestart()//活动由停止变为运行之前调用

活动被回收了怎么办

如果活动被回收了,而弹栈之后到达了,就会再一次调用活动的onCreate方法,唯一的问题是活动中暂存的临时数据都丢失了。Activity中提供了一个onSaveInstanceState的回调方法,这个方法会保证一定在活动被回收之前调用,因此可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。
onSaveInstanceState方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据。每个方法需要传入两个参数,一个是键,一个是真正要保存的内容。

@Override
protected void onSaveInstanceState(Bundle outState){
    super.onSaveInstanceState(outState);
      String tempData = "Somethin";
      outState.putString("data_key", tempData);
}

数据保存之后,onCreate方法中的Bundle参数就会获得这些数据。只需要再次取出即可。

if (savedInstanceState != null){
      String tempData = 
        savedInstanceState.getString("data_key");
    Log.d(TAG, tempData);
}