文章目录
安装教程
教材:第一行代码(第2版)
基础
1、创建新项目
2、选择Empty Activity
3、将Android改为Project
4、主要文件夹
在活动中加载布局
//MainActivity
package com.example.learntest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //在这个活动中加载一个布局
}
}
在AndroidManifest文件中注册
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.learntest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
//所有活动都要进行注册,在创建新活动时会自动完成这个部分
<activity android:name=".MainActivity"
android:label="This is MainActivity"> //label设置该活动顶部标题栏的内容
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Button
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" //线性布局
android:orientation="vertical" //垂直由上至下
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id = "@+id/button1" //给当前的元素定义一个唯一标识符,之后可以在代码中对这个元素进行操作
//@+id/button1 定义一个id
//@id/button1 引用一个id
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 1"
/>
</LinearLayout>
Toast
这是一种提醒方式,在程序中将一些短小的信息通知给用户,在一段时间后自动消失且不会占用任何屏幕空间。
//在MainActivity中添加
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = (Button) findViewById(R.id.button1); //得到按钮实例
button1.setOnClickListener(new View.OnClickListener(){ //为按钮注册一个监听器
@Override
public void onClick(View v){
Toast.makeText(MainActivity.this,"You clicked Button 1!", //创建对象
Toast.LENGTH_SHORT).show(); //调用show显示
}
});
}
Menu
展示菜单,且不占用任何屏幕空间。
1、在res目录下新建menu文件夹;
2、在menu下新建一个菜单文件(menu resource file),命名为main;
//main.xml
<?xml version="1.0" encoding="utf-8"?>
<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>
//MainActivity
public class MainActivity extends AppCompatActivity {
@Override
//省略
//显示菜单
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
//菜单响应事件
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;
}
}
销毁活动
//MainActivity
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
// Toast.makeText(MainActivity.this,"You clicked Button 1!",
// Toast.LENGTH_SHORT).show();
finish(); //结束当前活动
}
});
使用显示intent在活动间穿梭
创建一个新活动:Main2Activity.java;
生成一个新的布局文件:activity_main2.xml;
新活动会自动在AndroidManifest中注册,
由于这个不是主活动,所以不需要配置< intent-filter >。
//AndroidManifest.xml
<activity android:name=".Main2Activity"></activity>
//修改activity_main2.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id = "@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 2"
/>
</LinearLayout>
//修改第一个活动MainActivity.java的按钮点击事件
//点击按钮,启动新活动Main2Activity,点击back返回
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent = new Intent(MainActivity.this, Main2Activity.class);
startActivity(intent);
}
});
使用隐式intent
隐式intent并不明确指出我们想要启动哪一个活动,
而是指定了一系列更为抽象的action和category等信息,
然后交由系统去分析这个intent,
并帮我们找出合适的活动去启动。
通过在< activity >标签下配置< intent-filter >的内容,
可以指定当前活动能够相应的action和category。
//AndroidManifest.xml
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" /> //指明当前活动可以响应的action
<category android:name="android.intent.category.DEFAULT" /> //附加信息
//action和category同时匹配时,这个活动才能响应该intent
</intent-filter>
</activity>
//修改第一个活动MainActivity.java的按钮点击事件
//点击按钮,启动新活动Main2Activity,点击back返回
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
}
});
每个intent中只能指定一个action,但能指定多个category。
//修改第一个活动MainActivity.java的按钮点击事件
//点击按钮,启动新活动Main2Activity,点击back返回
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent = new Intent("com.example.activitytest.ACTION_START");
intent.addCategory("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
}
});
//AndroidManifest.xml 添加一个category
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.activitytest.MY_CATEGORY" />
</intent-filter>
</activity>
更多隐式intent
//修改MainActivity.java的按钮点击事件
//点击按钮,进入浏览器并打开指定网页
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com")); //将一个网址字符串解析成Uri对象,调用setData()方法将这个Uri对象传递进去
startActivity(intent);
}
});
在< intent-filter >中配置一个< data >标签
与此对应,还可以在< intent-filter >标签中再配置一个< data >标签,
用于更精确地指定当前活动能响应什么类型的数据。
< data >标签主要配置内容:
android:scheme - 指定数据的协议部分,如上例的http
android:host - 指定数据的主机名部分,如上例的www.baidu.com
android:port - 指定数据的端口部分,一般紧随主机名之后
android:path - 指定主机名和端口之后的部分
android:mimeType - 指定可以处理的数据类型,允许使用通配符的方式
创建一个新活动:Main3Activity.java;
生成一个新的布局文件:activity_main3.xml;
//修改activity_main3.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id = "@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 3"
/>
</LinearLayout>
//修改ActivityManifest.xml
<activity android:name=".Main3Activity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE"/>
//不添加上行时,发生报错 Activity supporting ACTION_VIEW is not set as BROWSABLE,但不影响运行
//https://blog.csdn.net/wqh0830/article/details/88528784 解决办法
<data android:scheme="http" />
</intent-filter>
</activity>
调用系统拨号界面
//修改MainActivity按钮事件
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
intent启动活动时传递数据
//修改MainActivity按钮事件
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
String data = "Hello 2";
Intent intent = new Intent(MainActivity.this,Main2Activity.class);
intent.putExtra("extra_data",data); //传递字符串,第一个参数是键,第二个参数是真正要传递的数据
startActivity(intent);
}
});
//修改活动2:Main2Activity.java
//获取数据并打印出来
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d("Main2Activity",data);
}
点击Button1,Logcat中会打印出信息:
2020-06-18 09:37:46.796 12145-12145/com.example.learntest D/Main2Activity: Hello 2
返回数据给上一个活动
startActivityForResult()方法:
也用于启动活动;
期望在活动销毁时能够返回一个结果给上一个活动;
接受两个参数,
第一个参数为intent,
第二个参数是请求码,用于在之后的回调中判断数据来源。
//活动1 MainActivity按钮事件
Intent intent = new Intent(MainActivity.this, Main2Activity.class);
startActivityForResult(intent,1);
//活动2 Main2Activity按钮事件
Button button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent = new Intent();
intent.putExtra("data_return","Hello 1"); //要传递的数据
setResult(RESULT_OK,intent); //向上一个活动返回数据,第一个参数用于向上一个活动返回处理结果,第二个参数把带有数据的intent传递回去
finish(); //销毁当前活动
}
});
由于我们是使用startActivityForResult()方法来启动活动2,
在活动2被销毁后会回调上一个活动的onActivityResult()方法,
所以需要在活动1中重写这个方法来得到返回的数据。
//活动1 MainActivity 添加内容
//通过检查requestCode的值判断数据来源
//确定数据是从活动2返回后,再通过resultCode的值判断处理结果是否成功
//最后从data中取值并打印出来
//这样就完成了向上一个活动返回数据的工作
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
switch(requestCode){
case 1:
if(resultCode == RESULT_OK){
String returnData = data.getStringExtra("data_return");
Log.d("MainActivity", returnData);
}
break;
default:
}
}
点击button1,跳转到活动2;
点击button2,回到活动1;
此时Logcat打印信息:
2020-06-18 09:59:24.824 12517-12517/com.example.learntest D/MainActivity: Hello 1
//如果不是通过button2而是通过back键回到活动1,数据就无法返回。
//解决办法:在活动2中重写onBackPressed()方法
@Override
public void onBackPressed(){
Intent intent = new Intent();
intent.putExtra("data_return","Hello 1");
setResult(RESULT_OK,intent);
finish();
}
活动的生存期
Activity类中定义了7个回调方法,覆盖了活动生命周期的每一个环节。
onCreate()
在活动第一次被创建时调用,
完成活动的初始化操作,如加载布局,绑定事件等;
onStart()
在活动由不可见变为可见时调用;
onResume()
在活动准备好和用户进行交互时调用,
此时活动一定位于返回栈的栈顶,并且处于运行状态;
onPause()
在系统准备去启动或恢复另一个活动时调用;
onStop()
在活动完全不可见时调用,
它与onPause的主要区别:
如果启动的新活动是一个对话框式的活动,只执行onPause;
onDestroy()
在活动被销毁之前调用;
onRestart()
在活动由停止状态变为运行状态时调用,也就是活动被重新启动了。
除onRestart()方法,其它都是两两相对的,所以可以将活动分为3种生存期:
1、完整生存期
onCreate() - onDestroy();
完成各种初始化操作 - 完成释放内存的操作;
2、可见生存期(活动总是处于可见状态)
onStart() - onStop();
对资源进行加载- 对资源进行释放;
保证处于停止状态的活动不会占用过多内存;
3、前台生存期(活动总是处于运行状态)
onResume() - onPause();
此时活动是可以与用户进行交互的。
生命周期实例
1、新建项目LifeCycle
2、新建子活动NormalActivity与DialogActivity
//修改normal_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a normal activity" />
</LinearLayout>
//修改dialog_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a dialog activity" />
</LinearLayout>
//修改ActivityManifest.xml
<activity android:name=".DialogActivity"
android:theme = "@android:style/Theme.Dialog"> //给当前活动指定主题(对话框式)
</activity>
<activity android:name=".NormalActivity"></activity>
//这里发生了报错
//点击start dialog按钮没有跳出对话框,直接黑屏
//解决办法:https://blog.csdn.net/xu_mingze/article/details/74931141
/*
修改DialogActivity.java :
import android.app.Activity;
public class DialogActivity extends Activity {
//AppCompatActivity 改为 Activity
*/
//修改activity_main.xml
//添加2个按钮,用于启动子活动
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start_normal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Normal" />
<Button
android:id="@+id/start_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Dialog" />
</LinearLayout>
//修改MainActivity
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
setContentView(R.layout.activity_main);
Button startNormal = (Button) findViewById(R.id.start_normal);
Button startDialog = (Button) findViewById(R.id.start_dialog);
startNormal.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, NormalActivity.class);
startActivity(intent);
}
});
startDialog.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, DialogActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onStart(){
super.onStart();
Log.d(TAG,"onStart");
}
@Override
protected void onResume(){
super.onResume();
Log.d(TAG,"onResume");
}
@Override
protected void onPause(){
super.onPause();
Log.d(TAG,"onPause");
}
@Override
protected void onStop(){
super.onStop();
Log.d(TAG,"onStop");
}
@Override
protected void onDestroy(){
super.onDestroy();
Log.d(TAG,"onDestroy");
}
@Override protected void onRestart(){
super.onRestart();
Log.d(TAG,"onRestart");
}
}
1、点击start normal
2、1、点击start dialog
理解生命周期,查看logcat:
1、运行程序
主活动第一次被创建时依次执行:
onCreat
onStart
onResume
2、点击start normal
启动NormalActivity
由于Normal活动已经完全将主活动遮挡,主活动进入停止状态,执行:
onPause
onStop
3、点击back键返回主活动
由于之前主活动进入停止状态,所以要restart,然后依次执行:
onRestart
onStart
onResume
4、点击start dialog
启动DialogActivity
由于Dialog活动没有完全将主活动遮挡,主活动进入暂停状态,执行:
onPause
5、点击back键返回
onResume
6、点击back键退出程序
onPause
onStop
onDestroy
活动被回收的解决办法
启动活动B时,由于系统内存不足,活动A被回收;
按下back返回A会执行onCreate(),重建A;
但如果A种存在临时数据和状态,返回A时就消失了,
这个问题可以通过onSaveInstanceState()方法解决,
onSaveInstanceState()回调方法有一个bundle类型的参数,bundle提供了一系列方法用于保存数据。
//在MainActivity中添加
@Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("data_key",tempData); //存储临时数据
}
//在MainActivity中修改onCreate方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
setContentView(R.layout.activity_main);
if(savedInstanceState != null){
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG,tempData); //恢复数据
}