Android从入门到放弃—— 一、Activity详解2

门——Activity(二)

这里我们主要讲几个问题

  1. Activity间数据的传递
  2. 特殊情况的生命周期即数据保存和恢复
  3. manifest的相关配置

Activity间数据的传递

上一章我们讲Activity的跳转的时候,使用了一个类,Intent ,翻译过来是意图的意思,像是一个中间的媒介,告诉系统我们想要干什么。就像是以前提亲,需要一个媒婆。过程大体如下:

  1. 王家大少爷和李家千金已经好了很长一段时间了,李千金让王少爷赶紧的来提亲,那么王少爷要怎么做呢?先找一个媒婆(初始化Intent对象);
  2. 然后告诉她我的要提亲的对象(设置意图,要跳转到BActivity);
  3. 提亲总不能空着手去吧,于是王少爷先跟李千金商量好了,李千金说我要黄金万两(需要数据)去之前跟媒婆交代了要带信回来或者不用了(我们关系好得很,肯定成。);
  4. 然后我就开始准备,准备的东西不一定就满足需求。准备好东西之后给到媒婆(使用Intent的put***方法来设置数据),让媒婆带到。
  5. 李千金收到媒婆带来的彩礼(从intent中取出数据),开始清点,然后家里开始处理(BActivity开始处理)。
  6. 这里就分2种情况了,如果彩礼对了(BActivity正常处理完所有任务)就告诉媒婆这门亲事答应了;如果彩礼不对(BActivity处理发生了异常)就告诉媒婆我女儿不嫁了。
  7. 如果之前要求带信回去那么媒婆就不管成不成都得要回信,如果不需要回信的话媒婆就什么都不用干了。(返回数据到前一个Activity)

下面我们用代码来演示一下这个流程

AActivity的代码


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("AActivity", "ZLog onCreate $savedInstanceState")
        acMainTvJump.setOnClickListener {
            //创建意图(聘请媒婆,告诉它我的对象是BActivity)
            val intent = Intent(this, BActivity::class.java)
            //设置数据(添加彩礼)
            intent.putExtra("name", "BActivity")
            //这种启动方式是不需要返回数据的
            startActivity(intent)

            //这种方式是需要返回数据的,第二个参数为标记位,取回数据的时候需要用到,用它来判断返回的数据是给当前Activity的
            startActivityForResult(intent,0x1001)
        }
    }

    //返回数据后系统会调用此回调,第一个参数为我们启动的时候传递的,用来判断是否是我们要的数据
    //第二个参数是标记返回的是成功还是失败,成功时取值Activity.RESULT_OK
    //第三个参数为Intent对象,里面包含返回的数据
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        Log.d("AActivity", "ZLog onActivityResult ")
        if (requestCode == 0x1001 && resultCode == Activity.RESULT_OK){

        }
    }

AActivity的流程为 初始化Intent,同时设置需要跳转的Activity,然后通过putExtra方法设置数据,这里设置的数据可以是多种类型的。

除了基本数据类型的包装类以外还有几个需要注意,一个是实现了Serializable接口的子类,还有一个是Parcelable子类,这2个都是序列化的操作。另一个特殊的是Bundle,它可以看做是一个专门用来存放数据的地方,大家看它的方法基本类似的。

下面我们看看BActivity怎么处理

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        textView.setOnClickListener {
            startActivity(Intent(this, CActivity::class.java))
        }
        val name = intent.getStringExtra("name")
        Log.d("SecondActivity", "ZLog onCreate : $name")
    }

我们在BActivity中使用intent的get方法来拿到数据,这里传入的参数“name”必须要跟AActivity中put的时候设置的key相同才能取到。如果我们不需要返回给A数据的话就不用做任何操作,如果需要的话就要处理。只需要在BActivity被销毁之前调用setResult方法即可。

/**
     * Call this to set the result that your activity will return to its
     * caller.
     *
     * @param resultCode The result code to propagate back to the originating
     *                   activity, often RESULT_CANCELED or RESULT_OK
     *
     */
public final void setResult(int resultCode)

/**
     * Call this to set the result that your activity will return to its
     * caller.
     *
     * @param resultCode The result code to propagate back to the originating
     *                   activity, often RESULT_CANCELED or RESULT_OK
     * @param data The data to propagate back to the originating activity.
     */
public final void setResult(int resultCode, Intent data)

如果我们需要带回数据就使用第二个方法设置Intent对象,不需要的话就直接使用第一个方法,只告诉AActivity成功与否。

修改一下代码然后查看运行效果

//这里是AActivity
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("AActivity", "ZLog onCreate $savedInstanceState")
        acMainTvJump.setOnClickListener {
            //创建意图(聘请媒婆,告诉它我的对象是BActivity)
            val intent = Intent(this, BActivity::class.java)
            //设置数据(添加彩礼)
            intent.putExtra("name", "嫁吗")
            //这种启动方式是不需要返回数据的
//            startActivity(intent)

            //这种方式是需要返回数据的,第二个参数为标记位,取回数据的时候需要用到,用它来判断返回的数据是给当前Activity的
            startActivityForResult(intent, 0x1001)
        }
    }

    //返回数据后系统会调用此回调,第一个参数为我们启动的时候传递的,用来判断是否是我们要的数据
    //第二个参数是标记返回的是成功还是失败,成功时取值Activity.RESULT_OK
    //第三个参数为Intent对象,里面包含返回的数据
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        Log.d("AActivity", "ZLog Intent onActivityResult $requestCode $resultCode")
        if (requestCode == 0x1001 && resultCode == Activity.RESULT_OK && data != null) {
            val result = data.getStringExtra("result")
            Log.d("AActivity", "ZLog Intent onActivityResult 取出BActivity返回的数据:$result")
        }
    }
//这里是BActivity
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        textView.setOnClickListener {
//            startActivity(Intent(this, CActivity::class.java))
            val intent = Intent()
            intent.putExtra("result", "嫁")
            setResult(Activity.RESULT_OK, intent)
            finish()
        }
        val name = intent.getStringExtra("name")
        Log.d("SecondActivity", "ZLog Intent 取出AActivity传递过来的数据: $name")
    }

至此我们就完成了Activity之间的数据传递

特殊情况的生命周期即数据保存和恢复

之前我们讲过activity的生命周期,其中主要的流程为6个回调。现在我们考虑一种情况,我们做了一个视频类的APP,在基本界面的时候是竖屏播放的,用户点击全屏后变成横屏了,但是播放的进度不能变。带着这个问题我们来用代码看看实际的横竖屏切换时发生了什么。

再次修改代码,去掉不需要的部分,添加打印信息


class AActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("AActivity", "ZLog onCreate $savedInstanceState")
    }

    override fun onStart() {
        super.onStart()
        Log.d("AActivity", "ZLog onStart ")
    }

    override fun onResume() {
        super.onResume()
        Log.d("AActivity", "ZLog onResume ")
    }

    override fun onRestart() {
        super.onRestart()
        Log.d("AActivity", "ZLog onRestart ")
    }

    override fun onPause() {
        super.onPause()
        Log.d("AActivity", "ZLog onPause ")
    }

    override fun onStop() {
        super.onStop()
        Log.d("AActivity", "ZLog onStop ")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("AActivity", "ZLog onDestroy ")
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        Log.d("AActivity", "ZLog onSaveInstanceState ")
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        Log.d("AActivity", "ZLog onRestoreInstanceState ")
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        Log.d("AActivity", "ZLog onConfigurationChanged $newConfig")
    }
}

效果图:

可以看到切换横竖屏后打印的log一下多了很多

切换后的调用顺序是onPause——onStop——onSaveInstanceState——onDestroy——onCreate——onStart——onRestoreInstanceState——onResume

可以看到原本的Activity已经被销毁了,只是在销毁之前调用了一个onSaveInstanceState方法。

这个方法的参数我们看到就是一个Bundle,刚刚我们说了它就是一个专门用来存放数据的,由此我们就可以在这里保存一些数据而不至于数据丢失。恢复数据的地方其实有2个,细心的观察log,第一次onCreate调用的时候参数的bundle为空,第二次不为空,而且切换后有一个onRestoreInstanceState方法。所以我们可以在这2个地方来恢复数据。

实际操作一下看看:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("AActivity", "ZLog onCreate")
        if (savedInstanceState != null) {
            processValue = savedInstanceState.getInt("process")
        }
        Log.d("AActivity", "ZLog onCreate Bundle processValue:$processValue")
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putInt("process", 50)
        Log.d("AActivity", "ZLog Bundle onSaveInstanceState ")
    }

初始processValue为0,在保存的时候我们存入了一个50,在onCreate中判断如果Bundle不为空我们就把这个值取出来

可以看到第二次调用onCreate的时候已经去到了这个50了。

manifest的相关配置

这里想要讲的主要是manifest中activity标签的相关配置对activity的影响。

1、screenOrientation:刚刚我们的activity之所以能进行横竖屏的切换就是通过这个属性来进行配置的

screenOrientation的取值有以下几种,配以表格说明:

取值

说明

fullSensor

显示的方向(4个方向)是由设备的方向传感器来决定的,除了它允许屏幕有4个显示方向之外,其他与设置为“sensor”时情况类似,不管什么样的设备,通常都会这么做。例如,某些设备通常不使用纵向倒转或横向反转,但是使用这个设置,还是会发生这样的反转。这个值在API Level 9中引入。

behind

与前一个Activity相同

fullUser

如果用户锁定了基于传感器的旋转,其行为与 user 相同,否则,其行为与 fullSensor 相同,允许所有 4 种可能的屏幕方向。 API 级别 18 中的新增配置。

landscape

强制横屏显示

locked

将方向锁定在其当前的任意旋转方向。API 级别 18 中的新增配置。

nosensor

旋转设备时候,界面不会跟着旋转。初始化界面方向由系统控制

protrait

强制竖屏显示

sensor

根据物理传感器方向转动,用户90度、180度、270度旋转手机方向,activity都更着变化

sensorLandscape

横屏旋转,一般横屏游戏会这样设置

sensorPortrait

竖屏旋转

unspecified

默认值,由系统决定,不同手机可能不一致

user

用户当前设置的方向

 

2、configChanges:这个标记的作用是如果系统发生一些配置上的变化不会从新初始化activity,而是调用onConfigurationChanged来重新加载新的配置。

举个例子

<activity
            android:name=".AActivity"
            android:screenOrientation="fullSensor"
            android:configChanges="orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
class AActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("AActivity", "ZLog onCreate")
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        Log.d("AActivity", "ZLog onConfigurationChanged $newConfig")
    }


}

这里我们加上configChanges="orientation"表示如果发生屏幕切换后不从销毁界面

我们看到切换后onCreate并没有从新调用,而是调用了onConfigChanged。

取值

说明

locale

用户所在区域发生变化,一般是用户切换了语言时,切换后的语言会显示出来

orientation

屏幕方向改变了---横竖屏切换

fontScale

字体比例发生了变化----选择了不同的全局字体

screenSize

屏幕大小改变了,基本不会遇到

Keyboard

键盘发生了改变----例如用户用了外部的键盘

keyboardHidden

键盘的可用性发生了改变

mcc

国际移动用户识别码所属国家代号是改变了,sim被侦测到了,去更新mcc    MCC是移动用户所属国家代号

mnc

国际移动用户识别码的移动网号码是改变了, sim被侦测到了,去更新mnc    MNC是移动网号码,最多由两位数字组成,用于识别移动用户所归属的移动通信网

navigation

导航发生了变化-----通常也不会发生

screenLayout

屏幕的显示发生了变化------不同的显示被激活

smallestScreenSize

屏幕的物理大小改变了,如:连接到一个外部的屏幕上

uiMode

用户的模式发生了变化

 

其取值是可以添加多个的,用 | 隔开,例如

android:configChanges="orientation|locale|layoutDirection|fontScale"

其中需要注意的是,如果在设置中切换语言,单独添加locale是无效的,必须配合layoutDirection一起使用才能生效。

3、windowSoftInputMode:这个配置的作用是设置当前界面输入法相关属性。

android:screenOrientation="fullSensor"

取值

说明

adjustNothing

布局不做任何调整

adjustPan

Window内容布局整体压缩,展示软件盘的地方不会展示内容布局

adjustResize 

如果Window内容布局可以滚动,尽量将输入框展示在软件盘之上,不能保证软件盘没有遮盖其他内容布局

adjustUnspecified

根据情况做adjustPan或者adjustResize

stateAlwaysHidden

不论初次还是从别的Activity导航回来都不展示软件盘

stateAlwaysVisible

不管是初次进入还是导航到别的Activity重新回来,都展示软件盘

stateHidden

初次键入不展示软件盘

stateUnchanged

前一个Activity里的状态一样

stateUnspecified

 未指定软键盘状态

stateVisible

初次进入界面展示软键盘

 

下面我们主要看几个例子的效果

修改一下布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".AActivity">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框1" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框2" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框3" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框4" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框5" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框6" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框7" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框8" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框9" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入框10" />

</LinearLayout>

1、android:windowSoftInputMode="stateHidden|adjustPan"

我们设置了adjustPan,可以看到界面会上移,但是标题栏也被强制上移了。下面再试一下adjustResize 

android:windowSoftInputMode="stateHidden|adjustResize"

我们可以看到界面并没有上移,而是直接被输入法挡住了…… 这里就需要再补充一下了,这个属性想要界面上移,界面的布局必须是要可以移动的,例如ScrollView。所以这里我们改一下布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".AActivity">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框1" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框2" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框3" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框4" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框5" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框6" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框7" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框8" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框9" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入框10" />

        </LinearLayout>
    </ScrollView>


</LinearLayout>

我们在外面包了一层ScrollView,再来看看效果。

可以看到输入框并没有被遮挡,而且标题栏也没有被挤出去。

好了,我们基本上常用的几个属性就这些了,以后如果有用到其他的我们再说详细的介绍。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值