第01讲 Android开发系列---Activity

时间:2022-04-14 15:55:06

一.  Android系统版本及详细信息

最新数据  https://developer.android.com/about/dashboards/

二.  Android项目初探

1.    使用android studio创建一个工程

Application Name:“My First App”

Company Domain:“example.com”

Project和module联系和区别

一个 Project 可以有多个 Module。目前主流的大型项目是多Module结构的,模块之间彼此可以相互依赖。他们之间应该都是处于同一个项目业务情况下的模块,彼此之间是有不可分割的业务关系的。

Android studio中,一个Project代表一个完整的APP,Module表示APP中的一些依赖库或独立开发的模块。比如可以新建一个library做为module,然后在主APP上点右键 open module setting的Dependencies中添加一个模块依赖。然后主APP中就可以使用module中的类了。

2.    目录结构

Android视图

Project视图

关于gradle.build文件参考: https://developer.android.google.cn/studio/build/index.html

3.    Logcat的使用

日志打印工具

方法

级别

Log.v()

verbose

Log.d()

debug

Log.i()

info

Log.w()

warn

Log.e()

error

     注意:在android studio中创建一个activity 为我们完成了三步:(继承自AppCompatActivity是为了向后兼容)

创建activity、在menifest中注册activity、创建activity对应的layout文件

4.    隐藏标题栏

方法一:

继承自AppCompatActivity:

if (getSupportActionBar() != null){
    getSupportActionBar().hide();
}

方法二: 在style中加入如下代码:    <item name="windowNoTitle">true</item>

5.   
在活动中使用Toast

Toast.makeText(this,"you
clicked",Toast.LENGTH_SHORT).show();

6.   
在活动中添加菜单

(1)添加菜单xml文件:

<menu
xmlns:android="http://schemas.android.com/apk/res/android">

<item

android:id="@+id/add_item"

android:title="add"/>

<item

android:id="@+id/delete_item"

android:title="delete"/>

</menu>

(2)重写onCreateOptionsMenu方法

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.main,menu);

return
true;  //返回true表示显示该菜单

}

(3)重写onOptionsItemSelected

public boolean onOptionsItemSelected(MenuItem item){

 switch(item.getItemId()){

case R.id.add_item:

Toast.makeText(this,"add clicked",Toast.LENGTH_SHORT).show();

break;

case R.id.delete_item:

Toast.makeText(this,"delete
clicked",Toast.LENGTH_SHORT).show();

break;

}

return true;

}

更多详情参见考:https://developer.android.google.cn/guide/topics/ui/menus

7.   
销毁活动

finish();   //第一讲到此+java预备知识

三.  Android事件处理

Android提供了两套事件处理机制:监听、回调

1.   
基于监听的事件处理

事件监听的处理模型中,主要涉及三类对象:

Event Source(事件源)  通常是各个组件,如按钮、窗口、菜单等;

Event(事件):事件封装了界面组件上发生的特定事情。

Event Listener(事件监听器):负责监听事件源所发生的事件,并对各种事件作出相应的相应。

事件监听涉及的三个问题:

事件源:    
任何组件都可作为事件源。

事件监听器:由程序员实现,关键是实现处理方法。

注册监听器:调用事件源的setXxxListener方法即可。

另外:对于键盘事件、触摸屏事件等,此时程序需要获取事件发生的详细信息。如,键盘事件获取是哪个键触发的,触摸屏事件需要获取事件发生的位置的,Android会将事件信息封装成XxxEvent对象,并将该对象作为参数传递给事件处理器。

所谓事件监听器,其实就是实现了特定接口的java类的实例。实现事件监听器,通常有如下几种形式:

l  内部类

l  匿名内部类

l  外部类

l  Activity本身作为事件监听器

l  直接绑定到标签

其他事件如下表所示:

void

setOnClickListener(View.OnClickListener l)

Register a callback to be invoked when this view is
clicked.

void

setOnContextClickListener(View.OnContextClickListener l)

Register a callback to be invoked when this view is
context clicked.

void

setOnCreateContextMenuListener(View.OnCreateContextMenuListener l)

Register a callback to be invoked when the context
menu for this view is being built.

void

setOnDragListener(View.OnDragListener l)

Register a drag event listener callback object for
this View.

void

setOnFocusChangeListener(View.OnFocusChangeListener l)

Register a callback to be invoked when focus of
this view changed.

void

setOnGenericMotionListener(View.OnGenericMotionListener l)

Register a callback to be invoked when a generic
motion event is sent to this view.

void

setOnHoverListener(View.OnHoverListener l)

Register a callback to be invoked when a hover
event is sent to this view.

void

setOnKeyListener(View.OnKeyListener l)

Register a callback to be invoked when a hardware
key is pressed in this view.

void

setOnLongClickListener(View.OnLongClickListener l)

Register a callback to be invoked when this view is
clicked and held.

void

setOnScrollChangeListener(View.OnScrollChangeListener l)

Register a callback to be invoked when the scroll X
or Y positions of this view change.

void

setOnTouchListener(View.OnTouchListener l)

Register a callback to be invoked when a touch
event is sent to this view.

2.   
基于回调的事件处理

当用户在GUI组件上激发某事件时,组件自己的特定方法负责处理该事件。方法是自定义控件使其继承GUI组件类,在自定义控件中重写事件处理方法。

Demo

l  基于回调的事件传播

几乎所有基于回调的事件处理方法都有一个boolean类型的返回值,用于标志该处理方法是否已完全处理该事件。

返回true,表明已处理完,不会传播出去。

返回false,表明未处理完,会传播出去。

几种方法调用的先后顺序: 组件的监听器à组件自身的回调方法à组件所在Activity的回调方法。其中任何一个事件处理方法返回了true,那么该事件将不会继续向外传播。

四.  启动活动的方法:

1.   
显式Intent   demo

//第一个参数Context是启动活动的上下文,第二个参数是制定要启动的目标活动。

Intent intent=new
Intent(MainActivity.this,SecondActivity.class);

startActivity(intent);

2.   
隐式Intent   demo

不明确指定启动哪一个活动,而是指定一系列action 和category,由系统分析启动哪一个活动。只有<action>和<category>中的内容同时能匹配上Intent中指定的action和category时,这个活动才能响应该Intent。

每个Intent只能指定一个action,但可以指定多个category。如果没有匹配的活动可以启动程序将崩溃;如果有多个匹配的活动,系统将提供列表让用户选择启动哪个活动。

显示引用不能启动其他进程的Activity对象,因为无法获取其他进程的Activity对象的字节码,而隐式启动则可以通过配置Intent Filter启动其他进程的Activity对象,因此在应用内,我们一般都是使用显示启动的方式启动Activity,而如果需要启动其他应用的Activity时,一般使用隐式启动的方式。

l  更多隐式Intent的用法

调用系统的浏览器打开一个网页。

Intent intent=new
Intent(Intent.ACTION_VIEW);

intent.setData(Uri.parse("http://www.baidu.com"));

Uri.parse() 将字符串解析成一个Uri对象。setData() 方法接收一个Uri对象,指定当前Intent正在操作的数据。

只有<data>标签中指定的内容和Intent中携带的data完全一致时,当前活动才能相应该Intent.

<data>  使用一个或多个指定数据 URI 各个方面(schemehostportpath 等)和 MIME 类型的属性,声明接受的数据类型。

https://developer.android.google.cn/guide/components/intents-common#Browser

五.  活动之间传递数据

1.   
向下一个活动传递数据

Intent intent=new Intent(MainActivity.this,SecondActivity.class);

intent.putExtra("extraData_Key","I am data");

startActivity(intent);

在下一个活动中将数据取出:

Intent intent=getIntent();

String data=intent.getStringExtra("extraData_Key");

使用Bundle

两个Activity交换数据通过Intent完成,只需将交换的数据放入Intent中即可。

putExtra(String
key, Xxx  value)     向Intent中按key-value对的形式存入数据

Xxx getXxxExtra(String
key)         从Intent中按key取出指定类型的数据

putExtras(Bundle
data)           向Intent中存入携带数据的Bundle

Bundle  getExtras()             从Intent中取出携带数据的Bundle

向Bundle对象中存取数据:

putXxx(String
key, Xxx data)   向Bundle中放入Xxx类型的数据

Xxx  getXxx(String key)       从Bundle中取出Xxx类型的数据

putSerializable(String
key, Serializable data)   向Bundle中放入一个可序列化的对象

Serializable  getSerializable(String key)      从Bundle中取出可序列化对象

2.   
返回数据给上一个活动

1)    
启动活动时使用onActivityResult方法:

startActivityForResult(intent,1);

2)    
重写onActivityResult()方法,对返回的数据进行处理:

protected
void onActivityResult(int requestCode, int resultCode, @Nullable Intent data)

{

switch (requestCode){

case 1:

if(resultCode==RESULT_OK) {

String
returnedData=data.getStringExtra("data_return");

Log.d(TAG,
"onActivityResult: "+returnedData);

}

break;

}

}

3)    
在第二个活动中返回数据:

setResult(RESULT_OK,intent);

finish();

六.  活动的生命周期

1.   
返回栈(Back Stack)

每启动新的活动,就会覆盖在原来的活动上,按Back键会销毁上面的活动,下面的活动就会重新显示出来。

Android使用Task来管理活动,一个Task就是一组存放在栈里的活动的集合,这个栈被称为返回栈(Back Stack)。参见activity返回栈

2.   
活动状态

l  运行状态

l  暂停状态

l  停止状态

l  销毁状态

3.   
活动的生存期

4.   
体验活动的生命周期  (Demo:  ActivityLifeCycle)

5.   
活动被回收了怎么办

由于内存不足等原因,处于停止状态的活动有可能被系统回收。当该活动重新可见时,该活动会被重新创建。

onSaveInstanceState()方法在活动被回收之前调用,解决活动被回收时保存临时数据的问题。

1) 活动回收前保存数据

protected
void
onSaveInstanceState(Bundle outState) {

outState.putString("data_key","I am data");  //保存数据

super.onSaveInstanceState(outState);//必须调用父类的方法

}

2) 活动恢复时恢复数据

在onCreate方法中恢复:

protected
void
onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState); // Always call the
superclass first

if (savedInstanceState != null) {

String
str=savedInstanceState.getString("data_key");//得到数据

Log.d("MainActivity", "活动恢复,数据为:"+str);

} else {

Log.d("MainActivity", "这是活动正常启动");

}

//……

}

在onRestoreInstanceState方法中恢复:

protected
void
onRestoreInstanceState(Bundle savedInstanceState) {

super.onRestoreInstanceState(savedInstanceState);//可选

String
str=savedInstanceState.getString("data_key");//得到数据

Log.d("MainActivity", "恢复数据为:"+str);

}

注意:Always call the superclass implementation
of onRestoreInstanceState() so the default implementation can restore the state
of the view hierarchy.

开发者可根据具体情况选择在哪个方法中进行恢复。

3) onSaveInstanceState() 何时被执行

当某个activity变得“容易”被系统回收时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键(手机下方的返回键)的时候。(why)分为以下几种情况:

从第一个界面跳转到第二个界面,第一个界面就会执行onSaveInstanceState

按下home键,运行多个其他程序,这时系统不确定会不会将该activity销毁,所以会执行onSaveInstanceState方法保存值。

关闭手机屏幕时

屏幕方向切换时,例如从竖屏切换到横屏时。(前提是androidMenifest.xml中对应activity标签没有配置<activity android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"></activity>)

4) onRestoreInstanceState() 何时被执行

Activity被系统回收后,当重新恢复时会调用onRestoreInstanceState(),并且是在onStart()之后。The
system calls onRestoreInstanceState() only if there is a saved
state to restore, so you do not need to check whether the Bundle is
null。

而系统创建一个新的Activity实例或重新创建一个以前的实例时都会调用onCreate(),因此在onCreate()中需要判断Bundle是否为null。

5) 模拟当前应用被系统回收

进入开发者选项,选中不保留活动。

6) 为什么我们的UI界面的值不用我们自己保存也可以自动保存,状态恢复呢?

开发者只需要为这些控件指定一个唯一的ID,剩余的事情就可以自动完成了。如果没有为控件指定ID,则这个控件就不会进行自动的数据保存和恢复操作。

更多参考:https://developer.android.google.cn/guide/components/activities/activity-lifecycle.html#saras

另外,onSavedInstanceState()只适合保存少量可序列化的数据。use
a combination of ViewModel objects, the onSaveInstanceState() method,
and/or local storage to
persist the UI state across such application and activity instance transitions.

6.   
活动的加载模式(Launch Mode)

Often, the
way Android manages tasks and the back stack by placing all activities started
in succession in the same task and in a "last in, first out" stack.

You can
interrupt the normal behavior, with attributes in the <activity> manifest
element and with flags in the intent that you pass to startActivity().

Launch
modes allow you to define how a new instance of an activity is associated with
the current task.

<activity android:name=".SecondActivity" android:launchMode="singleTop"/>

standard (the default mode)

无论当前栈顶是哪一个Activity都会开始一个新的Activity。

singleTop

如果需要创建的Activity已经处于任务(Task)栈顶时,复用该Activity。

singleTask

需要创建的Activity已经处于任务(Task)栈时,弹出此Activity上的所有其他Activity,复用该Activity。

singleInstance

启动一个新的任务(Task)栈,且该任务栈中只有这唯一一个Activity。整个系统中创建了该Activity,将不再重新创建。

当从返回栈回退时,如果当前回退栈为空了,才会显示另一回退栈的活动。

Demo四种启动模式演示

onNewIntent方法的使用

除了standard模式,其他三种方式都可能存在复用Activity的情况。通过Intent启到一个Activity,如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但新的Intent请求可能要处理新的数据,而这时没有机会调用onCreate方法,怎么办呢?系统为我们准备了onNewIntent方法。

一般,通过在onCreate和onNewIntent方法中调用同一个处理数据的方法,使得不管是创建新的Activity实例,还是复用原来的Activity实例,处理数据的方式保持一致。如下所示:

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

processExtraData();

}

protected void onNewIntent(Intent intent) {

super.onNewIntent(intent);

setIntent(intent);//must store the new intent unless getIntent() will return the old one

processExtraData();

}

private void processExtraData(){

Intent intent = getIntent();

//use the data received here

}