第二章 先从看得到的入手——探究活动

2.1活动是什么

  活动(Activity)是一种可以包含用户界面的组件,主要用于和用户进行交互。一个应用程序中可以包含零个或者多个活动。

2.2活动的基本用法

2.2.1手动创建活动

  创建一个新项目,可以命名为【First_Activity】注意Activity选择【No Acrtivity】,因为我们要手动创建活动;

  创建完成后【com.example.activitytest】目录是空的。

  右键【com.example.activitytest】包->New->Activity->Empty Activity,在弹出的对话框中不要勾选【Generate Layout File】和【Launcher Activity】两个选项。(Generate Layout File表示为活动自动创建一个对应布局;Launcher Activity 表示会将活动设为当前项目主活动)

  你需要知道,项目中任何活动都要重写Activity的【OnCreate()】方法,你新建的活动Android Studio会为你自动重写一个默认的。

2.2.2创建和加载布局

  右键【app/src/main/res】目录->New->Directory,会弹出一个新建目录的窗口,这里先创建一个名为【Layout】的目录;

  然后右键【Layout】目录->New->Layout resource file,又会弹出一个新建布局资源文件的窗口,我们将这个布局文件命名为【first_layout】,根元素就默认选择为【LinearLayout

  完成后会看到【first_layout】的布局编辑器;你可以使用code即代码模式编辑,也可以使用design即可视化模式进行编辑;

  试着添加一个button;@id是引用一个既有id,@+id则是定义一个新id。

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

    <Button
        android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button_1"
        />
</LinearLayout>

 

  这样一个布局就创建好了,可以在【First_Activity】的【onCreate】方法中加载这个布局:setContentView(R.layout.first_layout);

2.2.3在AndroidManifest文件中注册

  活动的注册要放在【application】标签内,这里通过【activity】标签来对活动进行注册。使用【android:name】来指定注册的活动。

  光注册还不行,还需要为程序配置主活动,这样程序运行起来才知道首先启动哪个活动。配置方法就是在【activity】标签内加入【intent-filter】标签并在这个标签内添加两句声明即可。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.activitytest">

    <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/Theme.ActivityTest">
        <activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

2.2.4在活动中使用Toast

  需要定义一个Toast出发点,可以将触发点绑定在button的点击处理上:

  在【onCreate】方法中添加button1的对象和点击处理函数;

        Button button1 = (Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(FirstActivity.this,"you clicked button 1",Toast.LENGTH_SHORT).show();
            }
        });

2.2.5在活动中使用menu

menu即Android提供的一种菜单实现方式。

  在【res】目录下新建一个【menu】文件夹,右键【res】->New->Directory,输入文件夹名【Menu】,点击【OK】

  在【Menu】目录下新建一个【main】菜单文件,右键【menu】->New->Menu resource file;

  文件名输入【main】,点击【OK】创建完成,然后再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/remover_item"
        android:title="Remove"/>
</menu>

  这里创建了两个菜单项,id是唯一标识符,title是名称。

  接着回到【FirstActivity】中重写【onCreateOptionsMenu】方法,重写方法可以使用【Ctrl】+O快捷键。添加如下代码:

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main,menu);
        return true;
    }

  仅仅显示出来菜单是不够的,还需要定义它的响应事件,需要在【FirstActivity】中重写【onOptionsItemSelected】方法:

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch(item.getItemId()){
            case R.id.add_item:
                Toast.makeText(this,"you clicked Add",Toast.LENGTH_SHORT).show();
                break;
            case R.id.remover_item:
                Toast.makeText(this,"you clicked remove",Toast.LENGTH_SHORT).show();
                break;
            default:
        }
        return true;
    }

重新运行程序你会发现标题栏右侧多了三个点,这个就是菜单按钮了。

  调用finish()方法就可以销毁当前活动了:

   

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               finish();
            }
        });

2.3使用Intent在活动之间穿梭

 

Intent是Android程序中各组件之间进行交互的一种重要方式,不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。

 

  Intent一般可被用于启动活动、启动服务、发送广播等场景。

 

  Intent大致可以分为两种:显示Intent和隐式Intent。

 

2.3.1使用显示Intent

  使用:先新建一个活动SecondActivity,然后在FirstActivity的点击按钮中添加如下代码:

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });

2.3.2使用隐式Intent

  隐式Intent并不明确的指出想要启动哪一个活动,而是指定一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并帮我们找出合适的活动去启动。

  使用:通过<activity>标签下配置<intent-filter>的内容,可以指定当前活动能够响应的action和category,打开AndroidManifest.xml,添加如下代码:

        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.activitytest.ActionStart" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
         </activity>

  这里指明,当前活动可以响应【com.example.activitytest.ActionStart】这个action,而【category】标签则包含了一些附加信息,只有<action>和<category>中的内容同时匹配上Intent中指定的【action】和【category】时,这个活动才能响应该Intent。修改FirstActivity中的按钮点击事件,代码如下:

           public void onClick(View v) {
                Intent intent = new Intent("com.example.activitytest.ActionStart");
                startActivity(intent);
            }

  这样该Intent中指定的【action】和【category】与second活动就匹配了;重新运行程序,点击按钮,就能成功启动SecondActivity了。

  每个Intent中只能指定一个【action】,却能指定多个【category】。使用【addCategory】方法,代码如下:

            public void onClick(View v) {
                Intent intent = new Intent("com.example.activitytest.ActionStart");
                intent.addCategory("com.example.activitytest.MyCategory")
                startActivity(intent);
            }

运行起来再次点击按钮就崩溃,因为没有活动可以匹配这个Intent,可以在SecondActivity的<intent-filter>标签中新增一个【category】声明,代码如下:

        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.activitytest.ActionStart" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="com.example.activitytest.MyCategory"/>
            </intent-filter>
         </activity>

这样就又正常了。

2.3.3更多隐式Intent用法。

  使用隐式Intent,我们不仅可以启动自己程序内的活动,还可以启动其他程序的活动,屌不屌?比如你想在点击按钮后迁移到一个网页(百度),你可以添加如下代码:

            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setData(Uri.parse("http://baidu.com"));
                startActivity(intent);
            }

启动程序,点击按钮试试,开心不?

 

  事实上只是因为百度页面的活动的<data>标签响应了Intent的http协议,你完全可以自己建一个活动,让它也能相应打开网页的Intent

  你试试将SecondActivity的注册信息修改如下:

        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="http"/>
            </intent-filter>
         </activity>

你发现再次点击按钮,你可以选择你建的这个项目作为响应程序了,并且可以迁移到SecondActivity活动中。

 

<data>标签不仅可以配置协议部分(scheme),还可以配置以下内容:【host】【port】【path】【nimeType

并且除了【http】协议,也可以指定其他协议,比如以下代码可以在点击按钮后调用系统的拨号界面:

            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_DIAL);
                intent.setData(Uri.parse("tel:10086"));
                startActivity(intent);
            }

 

2.3.4向下一个活动传递数据

很简单,Intent提供【putExtra】方法的重载,使你可以将数据存放在Intent中,直到启动下一个活动,在下一个活动中取出这些数据。存放代码如下:

            public void onClick(View v) {
                Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
                String data = "Hello SecondActivity";
                intent.putExtra("extra_data",data);
                startActivity(intent);
            }

第一个参数是键,用于后面从Intent中取值,第二个参数是要传递的数据。

取出数据代码如下:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second_layout);
        Intent intent = getIntent();
        String data = intent.getStringExtra("extra_data");
    }

 

2.3.5返回数据给上一个活动

即调用【startActivityForResult】方法,该方法可以在活动销毁的时候返回一个结果给上一个活动;该方法接受两个参数,第一个参数是【Intent】,第二个参数是【请求码】。

 

2.4活动的生命周期

2.4.1返回栈

  活动既是画页,放在返回栈中;

2.4.2活动状态

  运行状态:栈顶

  暂停状态:不在栈顶,仍可见(即上面的活动没有占满全屏)

  停止状态:不在栈顶,也不可见;

  销毁状态:从栈中移除。

2.4.3活动的生存期

  onCreate()。相当于load。

  onStart()。在活动由不可见变为可见状态时调用,相当于back后的fore。

  onResume()。在活动准备好和用户交互是调用,这时活动一定是栈顶并处于运行状态。

  onPause()。在系统准备启动或恢复另一个活动时调用。暂停状态

  onStop()。活动完全不可见时调用。停止状态

  onDestroy()。活动销毁前调用,之后活动变为销毁状态

  onRestart()。在活动又停止状态变为运行状态前调用,也就是活动由不可见重新到栈顶了。

以上七个方法除了【onRestart,其他都是两两相对的,从而又可以把活动分为三种生存期:

  完整生存期:活动从被创建到被销毁,即【onCreate】到【onDestroy】,一般活动在【onCreate】完成初始化操作,并在【onDestroy】中释放资源。

  可见生存期:从【onStart】到【onStop】,可以通过这两个方法合理的管理那些对用户可见的资源。比如在【onStart】加载一些资源,在【onStop】释放,从而减小内存占用。

  前台生存期:从【onResume】到【onPause】,这期间活动处于运行状态,可以和用户进行交互。

2.4.5活动被回收了怎么办

主要针对停止状态的活动由于系统内存不足被系统回收的情况,Activity提供了一个【onSaveInstanceState()】方法,这个方法在活动被回收前被调用,可以通过该方法保存数据。

该方法会携带一个【Bundle】类型的参数,【Bundle】提供一系列方法用于保存数据,比如可以使用【putString】保存字符串。使用【putInt()】保存整数。

保存到数据在onCreate中就可以取出来,你发现omCreate方法参数正是【Bundle】了吗。

2.5活动的启动模式

  四种,可以在【AndroidManifest.xml】中通过给<activity>标签指定【android:launchMode】来选择启动模式。

  standrad:默认启动模式,可以在栈中有多个实例,每次启动都是新创建一个该活动。

  singleTop:在启动时如果发现栈顶已经是该活动,就直接使用,不在新创建。

  singleTask:栈中只有一个该活动实例,栈中没有才创建。如果有了就将该活动上面的活动都出栈。replace。

  singleInstance:该活动独享一个返回栈,实现程序间活动实例共享。就是lib。

2.6活动的最佳实践

  这一节教你一些设计思想,比如给所有活动一个base类,用以添加共同逻辑(比如添加活动迁移打印等)。

  比如写一个管理类,用以管理所有活动,

  

posted @ 2020-12-03 00:57  吉尔加斯  阅读(79)  评论(0编辑  收藏  举报