1.什么是Activity.
Activity是和用户交互的界面,即我们看得见的最大的那部分,类似于网页.
Activity的创建需要实现以下两个步骤:
1.实现用户界面.
2.在清单文件中声明 Activity.
创建过程中会出现两个选项:Generate Layout file和Launcher Activity分别表示自动为当前活动创建一个对应的布局文件和将此文件设置为当前项目的主活动。
所有的活动都需要在AndroidManifest(清单文件)中进行注册才能生效,假设我们新创建Activity命名为:FirstActivity,会发现FirstActivity已经自动在AndroidManifest(清单文件)中进行注册了,一般来说是可以运行的,但是这次我们无法运行因为我们创建项目的时候没有勾选Launcher Activity导致目前没有主活动,因而程序运行时无法分辨先从哪个活动开始启动.因此要在AndroidManifest(清单文件)中自己想设为主活动的<activity>标签的内部加入<intent-filter>标签并添加以下两句申明:
<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>
//注:同时可添加label效果,即在顶部标题栏加入自己想要的内容,且给主活动指定的label不仅会
成为标题栏中的内容还会成为启动器中应用程序显示的名称
3. Button的在layout文件中的建立
以上已经创立了FirstActivity,我们在其对应的layout文件可以建立Button.
<Button android:id="@id+/button_1" 设置此按钮的唯一标识符 android:layout_weight="match_parent"or"warp_content" //前者表示当前元素和父元素一样宽而后者表示只要大小刚好能包含里面的内容即可 android:layout_height="match_parent"or"warp_content" //表示高度如上 android:text="xxxx" //指定了元素中显示的内容 />4.Button在主文件中的相关设置
在主文件的onCreate()方法中加入以下代码:
Public class FirstActivity extends appcompatActivity{ @Override Protected void onCreate(Bundle savedInstancestate){ Super.onCreate(saveInstancestate); setContentView(R.layout.first_layout); //(红字部分改为相应的文件名和其对应的layout文件名) } //setContentView()用于给当前活动加载一个布局,我们需要传入一个布局文件的id. // 项目添加的任何资源都会在R文件中生成一个相应资源的id.只需要调用 // R.layout.first_layout就可以得到first_layout.xml布局中的id, // 然后将这个值传入setContentView方法中.
小插曲:1.在活动中使用Toast
Toast是一种非常好的提醒方式,在程序中将一些短小的信息通知给用户,并一段时间后自动消失,不会占用任何屏幕空间.首先定义一个弹出Toast,在FirstActivity中加入了一个按钮正好符合要求,在onCreate()方法中加入如下内容:
protected voidonCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.frist_layout); Button button1=(Button)findViewById(R.id.button_1); //findViewById()方法用于获取在布局文件中定义的元素,我们传入了R.id.button_1 来得到按钮, // 这个值是first_layout.xml中通过android:id属性指定的.findViewById得到的对象 // 是一个view对象我们需要向下转型成为Button对象 button1.setOnClickListener(newView.OnClickListener() { //我们调用setOnClickListener()方法为按钮注册一个监听器, // 点击时就会执行监听器中的onClick()方法.从而在onClick()方法中编写Toast @Override public void onClick(View v) { Toast.makeText(FirstActivity.this,”you clicked Button 1”, Toast.LENGTH_SHORT).show(); //利用静态方法makeText()创建出一个Toast对象。 // 然后调用show()方法将Toast显示 } }); } //makeText()方法需要传入三个参数。第一个参数是conText也就是Toast要求的上下文, // 由于活动本就是conText对象因此以上直接传入FirstAcvity.this即可,第二个参数是Toast显示的内容, // 第三个是Toast显示的时长,有Toast.LENGTH_SHORT OR LONG可供选择。 }
2. 使用Menu
手机与电脑不同,手机的屏幕空间有限,因此可以使用Menu来节约屏幕空间。在res目录下创建新的Directory,并命名为Menu,在此文件夹下创建的一个叫main的Menu resource file.在此目录下的main.xml中添加如下代码:
<?xml version="1.0" encoding="utf-8"?> <menuxmlns: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"/> <!-- //item>用于创建具体的一个菜单项目,并且通过android:id与按钮一样来指定 唯一的标识符,而android:title用来给菜单项指定一个名称--> </menu>
接下来回到FirstActivity中按下Ctrl+O来重写onCreateOptionsMenu()方法,并在onCreateOptionsMenu()方法中添加如下内容:
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main,menu); return true; } //通过getMenuInflater()方法得到MenuInflater对象,再调用inflate()方法给当前活动创建菜单. // inflate()方法接受两个参数,第一个用于指定我们通过哪一个资源文件来创建菜单, // 这里传入R.menu.main.第二个参数用于指定我们的菜单项将添加到哪一个Menu对象中, // 这里用onCreateOptionsMenu()方法传入的menu参数,然后给这个方法返回true, // 表示允许创建的菜单显示,若返回false创建的菜单将无法显示。
要使得菜单真正可用还需要定义菜单响应时间。再FirstActivity中重写onOptionsItemSelected()方法:
@Override public booleanonOptionsItemSelected(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 clickRemove",Toast.LENGTH_SHORT).show(); break; default: } return true; } //在onOptionsItemSelected()方法,调用item.getItemId()来判断 // 我们点击的是哪个菜单项,然后给每个菜单项加入自己的逻辑处理, // 这里弹出一个Toast.
5.使用Intent在Activity之间穿梭
Intent大概可分为显式Intent和隐式Intent
例如:
在com.example.activity包中新建立一个Empty Acitivity并命名为SecondActivity,在SecondActivity中新建立一个button_2,SecondActivity会自动在AndroidManifest中注册,由于SecondActivity不是主活动,因此无需配置<intent-filter>标签中的内容
1.显式Intent:
Intent有多个构造函数的重载,一个是Intent(ContextpackageContext,class<?>cls).这个构造函数接受两个参数,第一个参数context要求提供一个启动活动的上下文,第二个参数class则是指定想要启动的目标活动。由于Activity类中提供了一个startActivity()方法,可专门用于启动活动,接受了Intent函数后就可以启动目标活动了
修改FirstActivity中按钮的事件:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); button1.setOnClickListener(newView.OnClickListener(){ @Override public void onClick(View v){ Intent intent =newIntent(FirstActivity.this,SecondActivity.class); startActivity(intent); } }); //首先构建出了一个Intent,传入FirstActivity.this作为上下文,传入Second-Activity.class // 作为目标活动,即在FirstActivity这个活动的基础上打开SecondActivity这个活动 // 。通过startActivity()方法来执行Intent.
2.隐式Intent
隐式活动不明确之处我们要启动哪一个活动,而是指定了一系列更抽象的action和category信息,然后交由系统去分析这个Intent并找出合适的活动执行。
打开AndroidManifest.xml在<activity>标签下配置<intent-filter>的内容:
<activity android:name=".SecondActivity"> <intent-filter> <actionandroid:name="com.example.activitytest.ACTION_START" /> <categoryandroid:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <!--//<action>标签中我们指明了当前活动可以响应com.example.activitytest.ACTION_START 这个action,而<category>标签包含了一些附加信息,精确指明了能够相应Intent中还可能 带有的category.只有<action>和<category>中的内容同时能够匹配上Intent中 指定的action和category时,这个活动才响应Intent.-->
接下来修改FirstActivity中的按钮点击事件
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); button1.setOnClickListener(newView.OnClickListener() { @Override public void onClick(View v) { Intent intent =new Intent("com.example.activitytest.ACTION_START"); startActivity(intent); } }); //使用Intent的另一个构造函数,直接将action的字符串传了进去,表明我们想要启动 // 能够响应com.example.activitytest.ACTION_START这个action的活动,注意这里 // 没有指定category,因为android.intent.category.DEFAULT是一种默认的category // 在调用startActivity()方法的时候会自动将这个category添加到Intent中
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); button1.setOnClickListener(newView.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); } }); //这里利用了调用Intent中的addCategory()方法来添加一个category,我们指定了一个自定义的 // category,值为com.example.activitytest.MY_CATEGORY
启动后发现报错,原因是没有任何一个活动可以相应我们的Intent,原因是我们新建了一个category但是并没有在SecondActivity的<intent-filter>中申明,接下去添加申明:
<activity android:name=".SecondActivity"> <intent-filter> <actionandroid:name="com.example.activitytest.ACTION_START" /> <categoryandroid:name="android.intent.category.DEFAULT" /> <categoryandroid:name="com.example.activitytest.MY_CATEGORY" /> </intent-filter> </activity>
6.向下一个活动传递数据
例如FirstActivity中有一个字符串,下载想把这个字符串传递到SecondActivity中,可以这么写:
button1.setOnClickListener(newView.OnClickListener() {
@Override
public void onClick(View v) {
String data ="Hello SecondActivity";
Intent intent = newIntent(FirstActivity.this, SecondActivity.class);
intent.putExtra("extra_data", data);
startActivity(intent);
}
});
//这里使用了显式Intent的方式来启动SecondActivity并通过putExtra()方法接受了两个参数,第一个式键,用于后面从Intent中取值,第二个参数才是真正要传递的数据。
然后我们在SecondActivity中将转递的数据取出来,并打印出来,代码如下:
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent=getIntent();
Stringdata=intent.getStringExtra("extra_data");
Log.d("SecondActivity",data);
}
}
//首先通过getIntent()方法获取到用于启动SecondActivity的Intent,然后调用getStringExtra()方法传入相应的键值,从而获得传递的数据。注:这里我们传递的是字符串所以用getStringExtra()方法,若传递的是整型数据则使用getIntExtra()方法,以此类推。
7.返回数据给上一个活动
利用startActivityForResult()方法可实现以上内容,startActivityForResult()方法接收两个参数,第一个参数就是Intent,第二个参数是请求码,用于在之后的回调中判断数据的来源。首先我们修改FirstActivity中按钮的点击事件:
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frist_layout);
Button button1 = (Button)findViewById(R.id.button_1);
button1.setOnClickListener(newView.OnClickListener() {
@Override
public void onClick(View v) {
String data ="Hello SecondActivity";
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);
}
});
//这里我们使用了startActivityForResult()方法来启动SecondActivity,请求码是唯一值就可以了,这里传入了1.
接下来再SecondActivity中给按钮注册点击时间,并再点击事件中添加返回数据的逻辑:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button button2=(Button) findViewById(R.id.button_2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
publicvoid onClick(View v) {
Intent intent=new Intent();
intent.putExtra("data_return","HelloFirstActivity");
setResult(RESULT_OK,intent);
finish();
}
});
//这里构造了一个Intent,仅仅用来传递数据,紧接着把要传递的数据放在Intent中,然后调用了setResult()方法,此方法专门用于向上一个活动返回数据的。setResult()方法接受两个参数,第一个参数用于向上一个活动返回处理结果,一般只用RESULT_OK或RESULT_CANCELED这两个值,第二个参数把带有数据的Intent传递回去,然后调用了finish()方法来销毁当前活动
由于我们使用startActivityForResult()方法来启动SecondActivity的,再SecondActivity被销毁后会回调上一个活动的onActivityResult()方法,因此我们需要再FirstActivity中写这个方法来得到返回的数据:
@Override
protected void onActivityResult(int requestCode,intresultCode, Intent data){
switch(requestCode){
case 1:
if(resultCode==RESULT_OK){
String returnedData = data.getStringExtra("data_return");
Log.d("FirstActivity",returnedData);
}
break;
default:
finish();
}
//onActivityResult()方法带有三个参数,第一个参数requestCode,即我们再启动活动时传入的请求码。第二个参数resultCode,即我们在返回数据时传入的处理结果。第三个结果data即携带着返回数据的Intent.
8.Activity的生命周期
下面引用一张官方文件的生命周期图:
Activity类定义了7个回调办法:除了onRestart()方法,剩余六个分别是两两相对的分为三个期:完整生存期、可见生存期、前台生存期
完整生存期:活动在onCreate()方法和onDestroy()方法之间所经历的,就是完整生存期,一般来说一个活动会在onCreate()方法中完成各种初始化操作,而在onDestroy()方法中完成释放内存的操作
可见生存期:活动在onStart()方法和onStop()方法之间所经历的就是可见生存期。在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们可以通过这两个方法,合理的管理那些对用户可见的资源。比如在onStart()方法中对资源进行加载,而在onStop()方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存
前台生存期:活动在onResume()方法中和onPause()方法之间所经历的就是前台生存期。在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行交互的,我们平时看到和接触最多的也就是这个状态下的活动。
运行过程中相关函数的执行顺序:
单个Activity:
启动一个Activity:oncreat()-> onStart() -> onResume();
若通过点击home键暂时退出该Activity:OnPause() ->onStop();
从最近打开应用中再次打开:onRestart() -> oncreat()-> onStart() -> onResume();
若通过点击后退键或退出键退出该Activity:OnPause() ->onStop()->onDestroy();
此时再一次从最近打开应用中再次打开,则重新加载Activity:oncreat()-> onStart() -> onResume();一个全新的周期
由AAvcivity跳转到B Activity:
启动A Activity:(A)oncreat()-> (A) onStart() -> (A) onResume();
跳转到 B Activity:(A)OnPause() ->(B)oncreat()-> (B) onStart() -> (B) onResume() -> (A) onStop();
再跳转回A Activity:(B)OnPause()-> (A)onResume()->(B) onStop() -> (B)onDestroy();;
特别说明:当刚启动Activity B时,A处于暂停状态,依然可见,当B完全将A遮挡后,A才会执行Stop();
只要A可见,就不会执行stop();
例如:B为透明、半透明、对话框等形式存在时,A没有被完全覆盖,就不会执行stop();
9.活动的启动模式
活动的启动模式主要分为四个分别是:standard模式、SingleTop模式、SingleTask模式和SingleTask模式,可以在AndroidManifest.xml(菜单文件)中通过给<activity>标签指定android:launchMode属性来选择启动模式。
<activity
android:name=".FirstActivity"
android:launchMode="standard"(这是默认模式)or"singleTOP"or"singleTask"or"singleInstance"
android:label="This is FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
一、standard模式:
这是最基础的模式,在这种模式中,当你进入一个活动,这个活动就会创造一个实例,出现在返回栈的最顶层,上一个你离开的活动就会被压在下面,(注:返回栈是Android管理活动的地方,出现在界面上的活动就在最上面,再出现新的就一层层往下压。)当你无限的点击进入下一个界面,你的每一个活动都会在返回栈中往下压。如果你点击返回上一个的活动,最顶层的活动就会消失,第二层的就会上来,如果你要退出这个应用,要把返回栈中的所有活动都取消掉,才能退出。如果你用一个极端的例子,不停地从这个活动进入同一个活动,你点击了十次,就要返回十次才能退出程序,因为你在返回栈中创造了十个相同的实例,尽管活动是一样的。
二、SingleTop模式:
在SingleTop模式中,
会检查在返回栈栈顶是不是你要启动的活动,如果不是的话,会启动这个活动,如果是的话,他就不会启动,直接使用。所以SingleTop不会出现standard中的情况需要点击多次才能退出程序,它只需要点击一次就可以了。
三、SingleTask模式:
这种模式是最智能的模式,系统会在你的返回栈中检查是否有你想启动的活动,有的话就直接推向栈顶,没有的话才回去创建。
四:SingleInstance:
这种模式是最特殊的模式,这种模式是为了让不同的app之间可以共享同一个活动,如果你的app想让别的app调用你的某一个界面,就可以用这种模式,这种模式会为你想共享的界面单独创造出一个单独使用的返回栈,不会与别的返回栈共同使用。
10.活动实践技巧
一、 知晓当前是在哪一个活动
在项目对应的包上新建立一个BaseActivity类,此类不需要在AndroidManifest.xml中注册,创造一个普通的JAVA类即可。然后让BaseActivity继承自AppCompatActivity,并重写onCreate()方法:
ublic class BaseActivity extends AppCompatActivity{
@Override
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
}
}
接下来需要让BaseActivity成为ActivityText(对应项目名)中所有的父类,最后查看Log信息即可。
二、随时随地退出程序
创建一个专门的集合类对所有的活动进行管理,在你的项目中创建一个ActivityCollector类作为活动管理器
public class ActivityCollector { public static List<Activity>activities=new ArrayList<>(); public static void addActivity(Activity activity){ activities.add(activity); } public static void removeActivity(Activity activity){ activities.remove(activity); } public static void finishALL(){ for(Activity activity:activities){ if(!activity.isFinishing()){ activity.finish(); } } activities.clear(); }
//在活动管理器中,我们通过一个List来暂存活动,然后提供了一个addActivity()方法用于向List中添加一个活动,提供了一个removeActivity()方法用于从List中移除活动,最后提供了一个finish()方法用于将List存储的活动全部销毁掉
接下来修改BaseActivity中的代码:
public class BaseActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
//在BaseActivity的onCreate()方法中调用了ActivityCollector的addActivity()方法,表明将当前正在创建的活动添加到活动管理器中,然后再BaseActivity中重写onDestroy()方法,并且调用了ActivityCollector的removeActivity()方法,表明将一个马上要销毁的活动从活动管理器移除。
之后想在什么地方退出程序,只需要调用ActivityCollector.finishAll()方法即可。例如想在ThirdActivity界面想通过点击按钮直接退出程序,可以这么改:
public class ThirdActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("ThirdActivity", "Task id is" + getTaskId());
setContentView(R.layout.third_layout);
Button button3 = (Button) findViewById(R.id.button_3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCollector.finishALL();
}
} );
}