文章目录
- Ch4 程序活动单元Activity
- 4.1 Activity的生命周期
- 4.1.1 生命周期状态
- 4.1.2 生命周期方法
- 4.2 Activity的创建、配置、开启和关闭
- 4.2.1 创建Activity
- 4.2.2 配置Activity
- 4.2.3 开启和关闭Activity
- 4.3 Intent与IntentFilter
- 4.3.1 Intent(意图)
- 4.3.2 IntentFilter(过滤器)
- 4.4 Activity之间的跳转
- 4.4.1 在Activity之间数据传递
- 4.5 Activity的任务栈和启动模式
- 4.5.1 Android中的任务栈
- 4.5.2 Activity的启动模式
- 4.6 使用Fragment(碎片)
- 4.6.1 Fragment简介
- 4.6.2 Fragment的生命周期
- 4.6.3 创建Fragment
- 4.6.4 在Activity中添加Fragment
Ch4 程序活动单元Activity
Android四大组件:Activity,Service,ContentProvider,BroadcastReceiver
Activity负责与用户交互
4.1 Activity的生命周期
4.1.1 生命周期状态
Activity的生命周期指Activity从创建到销毁的整个过程。
- 启动状态
一般情况下,当Activity启动后便会进入运行状态 - 运行状态
Activity在此状态时处于界面最前端,它是可见、有焦点的,可以与用户进行交互。
当Activity处于运行状态时,Android会尽可能地保持这种状态。如果出现内存不足的情况,Android也会先销毁栈底的Activity来确保当前Activity正常运行。 - 暂停状态
Activity对用户来说依然可见,但无法获取焦点,用户对它操作没有响应。(例如Activity上覆盖了一个透明或非全屏的界面) - 停止状态
当Activity完全不可见时处于停止状态。 - 销毁状态
当Activity处于销毁状态时,将被清理出内存
Activity生命周期的启动状态和销毁状态是过渡状态,Activity不会在这两种状态停留
4.1.2 生命周期方法
- onCreat()
Activity创建时调用 - onStart()
Activity即将可见时调用 - onResume()
Activity获取焦点时调用 - onPause()
Activity被其他Activity覆盖或屏幕锁屏时调用 - onStop()
Activity对用户不可见时调用 - onDestroy()
Activity销毁时调用 - onRestart()
Activity从停止状态到再次启动时调用
如果程序中只有一个Activity,则程序无法进行从停止状态到再次启动状态的操作。
当手机横竖屏切换时,会根据AndroidManifest.xml文件中Activity的configChanges属性值的不同而调用相应的生命周期方法。
在进行横竖屏切换时,首先会调用onDestory()方法销毁Activity,之后调用onCreate()方法重建Activity。如果不希望在横竖屏切换时Activity被销毁重建,可以通过configChanges属性进行设置
<activity android:name".ManActivity"
android:configChanges="orientation|KeyboardHidden">
如果希望某一个界面一直处于竖屏或者横屏状态,可以在清单文件中通过设置Activity的screenOrientation属性完成。
竖屏: android:screenOrientation="portrait"
横屏:android:screenOrientation="landscape"
4.2 Activity的创建、配置、开启和关闭
4.2.1 创建Activity
[右击包名]->[new]->[Activity]->[Empty Activity]->创建ActivityExample
4.2.2 配置Activity
创建一个SecondAcitvity类继承Activity,当在ActivityExample的onCreat()方法中启动ScondActivity时,会抛出异常信息
package com.milk.learnproject_activity;
import .AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//启动SecondActivity
Intent intent= new Intent(this,SecondActivity.class);
startActivity(intent);
}
}
每一个创建的Activity都必须在清单文件AndroidManifest.xml中配置才能生效。
配置SecondAcivity示例代码:
<activity
android:name="com.milk.learnproject_activity.SecondActivity"/>
如果Activity所在的包与AndroidManifest.xml文件的标签中通过package属性指定的包名一致,则android:name属性的值可以直接设置为".Activity名称"
<activity
android:name=".SecondActivity"
android:exported="true"
</activity>
android:exported=“true”
这句代码的意思就是该activity允许外部应用调用。
编译器有新建Activity时自动修改AndroidManifest.xml配置的功能
4.2.3 开启和关闭Activity
- 启动Activity
可以通过startActivity()方法开启创建的Activity
public void startActivity(Intend intent)
参数Intent为Android应用中各组件之间通信的桥梁,一个Activity通过Intent表达自己的“意图”。
在MainActivity的onCreate()方法中启动SecondActivity实例代码
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
- 关闭Activity
调用Activity的finish()方法关闭当前的Activity
public void finish()
finish()方法既没有参数,也没有返回值,只需要在Activity的相应事件中调用该方法即可。
4.3 Intent与IntentFilter
如果用户需要从一个Activity切换到另一个Activity,则必须使用Intent来进行切换。Intent用于相同或者不同应用程序组件间的绑定。
4.3.1 Intent(意图)
Intent是程序中各组件间进行交互的一种重要方式,它不仅可以指定当前组件要执行的动作,还可以在不同组件之间进行数据传递。
- 显示Intent
显示Intent指直接指定目标组件
使用Intent显示指定要跳转的目标Activity:
Intent intent = new Intent(this,SecondActivity.class);
startActivity(intent);
第一个参数this表示当前的Activity,第二个参数SecondActivity.class表示要跳转到的目标Activity
- 隐式Intent
隐式Intent不会明确指出需要激活的目标组件
- action:表示Intent对象要完成的动作
- data:表示Intent对象中传递的数据
- category:表示为action添加的额外信息
使用App1打开App2的SecondActivity:
SecondActivity的配置代码
<activity
android:name=".SecondActivity"
android:exported="true" >
<intent-filter>
<action android:name="com.milk.learnproject_activity_secondapp.SE_APP_SE_ACT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
App1打开SecondActivity的Button点击事件:
button1 = (Button) findViewById(.Button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent();
intent.setAction("com.milk.learnproject_activity_secondapp.SE_APP_SE_ACT");
//可以写成Intent intent=new Intent().setAction("com.milk.learnproject_activity_secondapp.SE_APP_SE_ACT");
startActivity(intent);
}
});
打开bilibili应用
package com.milk.learnproject_activity;
import .AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
Button button1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1 = (Button) findViewById(.Button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=getPackageManager().getLaunchIntentForPackage("tv.danmaku.bili");//获得bilibili app的启动包名
startActivity(intent);
}
});
}
}
在使用隐式Intent开启Activity时,系统会默认为该Intent添加"Android.intent.category.DEFAULT"的category,因此为了被开启的Activity能够接收隐式Intent,必须在AndroidManifest.xml文件的Activity标签下的<intent-filter>中为被开启的Activity指定catrgory为"android:intent.category.DEFAULT"。
4.3.2 IntentFilter(过滤器)
当发送一个隐式Intent后,Android系统会将它与程序中每一个组件的过滤器进行匹配,匹配属性有action、data、category,需要三个属性都匹配成功才能唤起相应组件。
- action属性匹配规则
action属性用来指定Intent对象的动作
<intent-filter>
<action android:name="android.intent.action.EDIT"/>
······
<intent-filter>
标签中间可以罗列多个action属性,但是当使用隐式Intent激活组件时,只要Intent携带的action与其中一个标签中action的声明相同,action属性就匹配成功。
在清单文件中为Activity添加标签时,必须添加action属性,否则隐式Intent无法开启该Activity。
- data属性匹配规则
data属性用来指定数据的URI或者数据MIME类型,它的值通常与Intent的action属性有关联。
<intent-filter>
<data android:mimeType="Video/mpeg" android:scheme="http..."/>
···
</intent-filter>
<intent-filter>标签中间可以罗列多个data属性,每个data属性可以指定数据的MIME类型和URI。其中MIME类型可以表示image/ipeg、video/*等媒体类型。
隐式Intent携带的data与其中一个标签中data的声明相同,data属性就匹配成功。
- category属性匹配规则
category属性用于为action添加额外信息,一个interFilter可以不声明category属性,也可以声明多个category属性。
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
······
</intent-filter>
隐式Intent中声明的category必须全部能够与某一个IntentFilter中的category匹配才算匹配成功。IntentFilter中罗列的category属性数量必须大于或者等于隐式Intent携带的category属性数量时,category属性才能匹配成功。如果一个隐式Intent没有设置category属性,那么它可以通过任何一个IntentFilter的category匹配。
4.4 Activity之间的跳转
4.4.1 在Activity之间数据传递
- 使用Intent的putExtra()方法传递数据
通过putExtra()方法将传递的数据存储在Intent对象后,如果想获取该数据,可以通过getXxxExtra()方法来实现。 - 使用Bundle类传递数据
Bundle类与Map接口比较类似,都是通过键值对的形式来保存数据。
Intent intent = new Intent();
intent.setClass(this,SecondActivity.class);
Bundle bundle = new Bundle();//创建Bundle对象
bundle.putString("account","Admin");//封装用户名信息
bundle.putString("password","123456");//封装密码信息
intent.putExtras(bundle);//将Bundle对象封装到Intent对象
startActivity(intent);
SecondActivity获取传递数据:
Bundle bundle = getIntent().getExtras();//获取Bundle对象
String account = bundle.getString("account");
String password = bundle.getString("Password");
4.4.2 Activity之间的数据回转
- startActivityForResult()方法
startActivityForResult(Intent intent, int requestCode)
startActivityForResult()方法用于开启一个Activity,当开启的Activity销毁时,希望从中返回数据。
requestCode表示请求码,用于标识请求来源。
- setResult()方法
setResult(int resultCode, Intent intent)
resultCode表示返回码,用于标识返回的数据来自哪个Activity。
intent用于携带数据并回传到上个界面。
- onActivityResult()
onActivityResult(int requestCode, int resultCode, Intent data)
onActivityResult()方法用于接收回传的数据,并根据传递的参数requestCode、resultCode来识别数据来源
在MainActivity中点击button1控件跳转到SecondActivity示例代码:
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivityForResult(intent,1);
}
});
在SecondActivity中点击button2控件返回数据到MainActivity的示例代码:
button2.setOnClickListener(new View.OnClickListener){
@Override
public void onClick(View view){
Intent intent = new Intent();
intent.putExtra("data","Hello MainActivity");
setResult(2,intent);
finish();
}
});
setResult()方法只负责返回数据,没有跳转功能,需要调用finish()方法关闭SecondActivity。
在MainActivity中调用startActivityForResult()方法启动SecondActivity,在SecondActivity被销毁后程序会回调MainActivity中的OnActivityResult()方法来接收回传的数据,因此需要在MainActivity中重写onActivityResult()方法。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
super.onActivityResult(requestCode,resultCode,data);
if(requestCode == 1 && resultCode == 2){
String acquiredData = data.getStringExtra("data");
Toast.makeText(MainActivity.this,acquiredData,Toast.LENGTH_SHORT).show();
}
}
在SecondActivity的EditView输入文字修改MainActivity的TextView:
MainActivity:
package com.milk.learnproject_activity;
import .AppCompatActivity;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
TextView tv1;
Button button1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = (TextView) findViewById(.Main_TextView1);
button1=(Button)findViewById(.Main_Button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent(MainActivity.this,SecondActivity.class);
startActivityForResult(intent,1);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
super.onActivityResult(requestCode,resultCode,data);
if(requestCode == 1 && resultCode == 2){
String acquiredData = data.getStringExtra("data");
Toast.makeText(MainActivity.this,acquiredData,Toast.LENGTH_SHORT).show();
tv1.setText(acquiredData);
}
}
}
SecondActivity:
package com.milk.learnproject_activity;
import .Activity;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class SecondActivity extends Activity {
EditText edittext1;
Button button1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
edittext1=(EditText)findViewById(.Second_EditView1);
button1=(Button)findViewById(.Second_Button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String text=edittext1.getText().toString();
Intent intent = new Intent();
intent.putExtra("data",text);
setResult(2,intent);
finish();
}
});
}
}
1.在Fragment中使用startActivityForResult的时候,不要使用getActivity().startActivityForResult,而是应该直接使startActivityForResult()。
2.如果activity中重写了onActivityResult,那么activity中的onActivityResult一定要加上super.onActivityResult(requestCode, resultCode, data)。
如果违反了上面两种情况,那么onActivityResult只能够传递到activity中的,无法传递到Fragment中的。
没有违反上面两种情况的前提下,可以直接在Fragment中使用startActivityForResult和onActivityResult,和在activity中使用的一样。
4.5 Activity的任务栈和启动模式
4.5.1 Android中的任务栈
Android的任务栈是一种用来存放Activity实例的容器。任务栈最大的特点就是先进后出。主要有压栈和出栈两个操作。
用户操作的Activity永远都是栈顶的Activity。
4.5.2 Activity的启动模式
- standard模式
standard是Activity的默认启动方式。当android:launchMode没有被指定属性时默认为standard。这种方式的特点是每启动一个Activity就会在栈顶创建一个新的实例(闹钟程序)。
当Activity已经位于栈顶时,再次启动该Activity时还需要创建一个新的实例压入任务栈,不能直接复用。 - singleTop模式
会判断要启动的Activity实例是否位于栈顶,如果位于栈顶则直接复用,否则创建新的实例(浏览器书签)。
如果Activity并未处于栈顶位置,则在栈中还会压入多个不相连的Activity实例 - singleTask模式
每次启动Activity时系统首先检查栈中是否存在当前Activity实例,如果存在则直接使用,并把当前Activity上面的所有实例全部弹出栈(浏览器主页面)。 - singleInstance模式
Activity会启动一个新的任务栈来管理Activity实例,无论从哪个任务栈中启动该Activity,该实例在整个系统中只有一个(Android桌面)。
- 要启动的Activity实例在栈中不存在,则系统先创建一个新任务栈再压入Activity实例
- 要启动的Activity已经存在,系统会把Activity所在任务栈转移到前台,从而显示Activity(来电界面)。
4.6 使用Fragment(碎片)
为了能够同时兼顾到手机和平板电脑的开发,自Android3.0版本开始提供了Fragment
4.6.1 Fragment简介
Fragment是一种嵌入在Activity中的UI片段,它可以用来描述Activity中的一部分布局。
一个Activity可以包含多个Fragment,一个Fragment也可以在多个Activity中使用。
4.6.2 Fragment的生命周期
当在Activity中创建Fragment时,Fragment处于启动状态,
当Activity被暂停时,其中的所有Fragment也被暂停,
当Activity被销毁时,其中的所有Fragment也被销毁,
当一个Activity处于运行状态时,可以单独地对每一个Fragment进行操作:添加时Fragment处于启动状态,删除时Fragment处于销毁状态。
Fragment生命周期相比Activity额外方法:
- onAttach():Fragment和Activity建立关联时调用
- onCreateView():Fragment创建视图(加载布局)时创建
- onActivityCreate():Fragment相关联的Activity已经创建时调用
- onDestroyView(): Fragment关联的视图被移除时调用
- onDetach(): Fragment和Activity解除关联时调用
4.6.3 创建Fragment
创建Fragment时必须创建一个类继承自Fragement。
public class NewListFragment extends Fragment{
@Override
public View on CreareView(LayoutInflater, inflater, ViewGroup container, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.fragment,container, false);
return v;
}
}
4.6.4 在Activity中添加Fragment
Fragment创建完成后并不能单独使用,还需要将Fragment添加到Activity中。
- 在布局文件中添加Fragment
需要在Activity引用的布局文件中使用标签,必须指定android:name属性,属性值为Fragment的全路径名称。
<fragment
android:name="cn.itcast.NewsListFragment"
android:id="@+id/newslist"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- 在Activity中动态加载Fragment
- 创建一个Fragment实例
- 获取FragmentManager的实例
- 开启FragmentTransaction
- 向Activity的布局容器中添加Fragment
- 通过commit()提交事务
public class MainActivity extends Activity{
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
NewsListFragment fragment = new NewsListFragment();
FragmentManage fm = getFragementManager();
FragmentTransaction beginTransaction = fm.beginTransaction();
beginTransaction.replace(.ll,fragment);
beginTransaction.commit();
}
}
“getFragmentManager和.Fragment在API 28中已弃用。
现在,我们应该分别使用支持库中的Fragment和getSupportFragmentManager。
但是,如果您担心这些弃用,则还应注意,API 28也弃用了PreferenceFragments。
相反,您应该使用PreferenceFragmentCompat”应将getFragementManager替换为getSupportFragmentManager()
//1.在Fragment中使用startActivityForResult的时候,不要使用getActivity().startActivityForResult,而是应该直接使startActivityForResult()。
//2.如果activity中重写了onActivityResult,那么activity中的onActivityResult一定要加上super.onActivityResult(requestCode, resultCode, data)。
//如果违反了上面两种情况,那么onActivityResult只能够传递到activity中的,无法传递到Fragment中的。
//没有违反上面两种情况的前提下,可以直接在Fragment中使用startActivityForResult和onActivityResult,和在activity中使用的一样。