• 《第一行代码》阅读笔记(四)——探究活动


    活动是什么?

    ——第一行代码
    活动( Activity )是最容易吸引用户的地方,它是一种可以包含用户界面的组件,主要用于和用户进行交互。一个应用程序中可以包含零个或多个活动,但不包含任何活动的应用程序很少见,谁也不想让自己的应用永远无法被用户看到吧?其实在上一章中,你已经和活动打过交道了,并且对活动也有了初步的认识。不过上一章我们的重点是创建你的第一个Android项目,对活动的介绍并不多,在本章中我将对活动进行详细的介绍。

    详细介绍活动

    书中花费了一些篇幅来描述如何自己创建活动,其中包括AndroidManifest.xml的活动编辑、新建布局和将布局和活动联系起来,有兴趣可以看看。在开发中一般都会自动生成,但是理解这一过程可以有效的帮助我们理解Android开发。

    setContentView()

    ——第一行代码
    可以看到,这里调用了setContentView() 方法来给当前的活动加载一个布局,而在setContentView()方法中,我们一般都会传人一个布局文件的id。在第1章介绍项目资源的时候我曾提到过,项目中添加的任何资源都会在R文件中生成一个相应的资源id,因此我们刚才创建的first_ layout. xml布局的id现在应该是已经添加到R文件中了。在代码中去引用布局文件的方法你也已经学过了,只需要调用R. layout. first_ layout 就可以得到first_ layout.xml布局的id,然后将这个值传人setContentView( )方法即可。

    声明主活动

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

    Toast

                Toast.makeText(MainActivity.this, btnHome.getText().toString(), Toast.LENGTH_SHORT).show();
    

    Toast类是一个小的提示框,他的方法makeText有三个参数,就像上面展示的一样。第一个参数是一个context,而活动本身就是一个上下文,所以直接传入MainActivity。第二个参数是一个string,就是你想要输出的语句。第三个参数是Toast类中的一个常量,short表示展示时间短,long表示时间长。

    看到这里不知道其他小伙伴有没有和笔者一样的疑惑,因为在传入context的时候,有的时候直接传入MainActivity,有的时候传入this,有时候传如MainActivity.this,有的时候传入getContext(),这些到底有什么区别呢?

    上网浏览后发现一篇文章,讲的很好,在这里分享给大家MainActivity.this 和this,至于其他的区别,笔者目前还没有办法解决。

    Menu

    第一步:新建main菜单
    首先在res目录下新建一个menu 文件夹,右击res目录→New→Directory,输入文件夹名menu,点击OK。接着在这个文件夹下再新建一个名叫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/remove_item"
            android:title="Remove"/>
    </menu>
    

    第三步:修改MainActivity

    @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, 创建的菜单将无法显示。

    第四步:添加响应事件

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

    ——第一行代码
    在onOptionsItemSelected()方法中,通过调用item. getItemId()来判断我们点击的是哪一个菜单项,然后给每个菜单项加入自己的逻辑处理,这里我们就活学活用,弹出一个刚刚学会的Toast。

    除了右上角的菜单,我们还可以设置上下文菜单,功能类似微信中的长按弹出的菜单。方法如下

     @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            super.onCreateContextMenu(menu, v, menuInfo);
        }
    
        @Override
        public boolean onContextItemSelected(@NonNull MenuItem item) {
            return super.onContextItemSelected(item);
        }
    

    使用方法非常相似。

    销毁一个活动

    核心就是一个finsh(),功能就和返回键一样

    Intent

    有的知识就不重复记录了,笔者在这里着重说一下自己的感受。先上一段书上的描述

    ——第一行代码
    Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送广播等场景,由于服务、广播等概念你暂时还未涉及,那么本章我们的目光无疑就锁定在了启动活动上面。

    而我的理解就是页面之间的跳转,因为安卓已经放弃了页面而是使用了活动。开发过网页项目的,笔者认为intent可以理解为网页之间的转发和重定向。

    而Intent又分为两种——显式Intent和隐式Intent。

    显式Intent

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

    这是Intent的一个构造函数,第一个参数就是当前活动的上下文环境, 第二个参数就是需要跳转的类的class文件。然后再使用startActivity()用来启动活动,参数就是intent。

    隐式Intent

    不知道大家还记不记得之前讲过的AndroidManifest.xml文件中的Intent-filter标签,里面有两个属性,就是action和category,在实例化Intent的时候可以通过传入action和category来实现和页面的匹配。书上讲的很清楚了,照着做就行。而我感觉这个隐式的作用在于可以把相似的Activity标记同样的action,然后在实现调整的时候直接跳转这个action,然后再根据category来具体决定跳转到那个具体的活动。

    其他用途

    可以跳转其他应用,就比如第三方支付这类的。
    书中讲解了一些例子,都不是很难,但是笔者在实验过程中出现了一些问题,无法正常运行,不知道怎么回事。但是感觉又不是很重要,所以跳过。

    向下一个活动传递数据

    第一步:传出

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

    主要函数是putExtra(),第一个参数是键,第二个才是需要传递的值。这个很好理解。

    第二步:接收

    Intent intent = getIntent();
    String data = intent.getStringExtra("extra_ data");
    Log.d("SecondActivity",data);
    

    ——第一行代码
    首先可以通过getIntent()方法获取到用于启动SecondActivity的Intent,然后调用getStringExtra()方法, 传入相应的键值,就可以得到传递的数据了。这里由于我们传递的是字符串,所以使用getStringExtra()方 法来获取传递的数据。如果传递的是整型数据,则使用getIntExtra()方法; 如果传递的是布尔型数据,则使用getBooleanExtra()方法,以此类推。

    其实也很简单明了,这里主要说一下这个"extra_ data"就像map中的key,而data就像value。

    返回数据给上一个活动

    这个在实际开发中使用不是很多,如果有需要,可以再次使用intent携带数据跳转回来

    第一步:修改传出方法
    实现这个功能的关键在于使用startActivityForResult(intent,1);来替代startActivity(intent);
    其中1表示请求码,只要唯一即可。

    第二步:添加返回数据
    在通过startActivityForResult调出的Activity中添加一按钮的点击事件,包括以下代码。

    String data = "Hello FirstActivity";
    Intent intent = new Intent();
    intent. putExtra("return_ data", data);
    setResult(RESULT_OK,intent);
    finish();
    

    ——第一行代码
    可以看到,我们还是构建了一个Intent,只不过这个Intent仅仅是用于传递数据而已,它没有指定任何的“意图”。紧接着把要传递的数据存放在Intent中,然后调用了setResult()方法。这个方法非常重要,是专门用于向上一个活动返回数据的。setResult() 方法接收两个参数,第一个参数用于向上一个活动返回处理结果,一般只使用RESULT_ OK或RESULT_ CANCELED这两个值,第二个参数则把带有数据的Intent 传递回去,然后调用了finish()方法来销毁当前活动。

    因为在事件的结尾结束了当前事件,这样会返回调用这个活动的活动,因为是startActivityForResult函数调用的,所有会自动执行onActivityResult函数,如果想要获取数据,就必须重写这个函数。

    @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            switch (requestCode) {
                case 1:
                    if (resultCode == RESULT_OK) {
                        String returnedData = data.getStringExtra("data_ return");
                        Log.d("FirstActivity", returnedData);
                    }
                    break;
                default:
            }
        }
    

    其实也很好理解,requestCode就是我们startActivityForResult传递的1,用来辨识。resultCode就是setResult传入的值,在调用系统app时返回时RESULT_CANCELED如字面意思代表取消,RESULT_OK代表成功。没什么特别的意思。data就是一个携带值的Intent。

    这是通过点击按钮,finish()函数实现的功能。通过back按钮依然可以实现是不过需要重写另一个函数,onBackPressed()。

    活动的生命周期

    非常的重要,但是又非常的抽象,需要反复理解。

    返回栈

    其实就是一个栈,多个活动会进入一个栈,然后这个栈里面的活动就是一个任务。其实栈的特点就是先进先出,不了解的可以直接搜索栈。

    活动状态

    ——第一行代码
    1.运行状态
    当一个活动位于返回栈的栈项时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。
    2.暂停状态
    当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。你可能会觉得既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域,你很快就会在后面看到这种活动。处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。
    3.停止状态
    当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
    4.销毁状态
    当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。

    像这些专有名词,笔者一般都是保留书上的描述,因为书本中的语言一般都比较精准。然后再说一说笔者自己的理解。

    1. 运行状态,一般就是在栈顶的活动,其实就是咱们可以看见的最上面的活动。
    2. 暂停状态,这个其实很好理解,就是一个没有覆盖整个屏幕的弹窗等待你操作,然后下面的那个活动就是暂停状态。不过随着安卓版本的更迭,我觉得这个状态的描述应该已经不准确了,因为很多程序都有小窗模式,而底下的那个活动仍然可以运动。
    3. 停止状态,应该就类似于后台的程序吧。
    4. 销毁状态,就是已经关闭了的活动。

    活动的生存期

    这里笔者选择和书籍相反的顺序,先制作demo,然后再讲解知识。所以大家请看

    活动周期demo

    第一步:新建DialogActivity和NomalActivity

    第二步:修改布局
    activity_dialog.xml

    <?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">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="This is a dialog activity" />
    
    </LinearLayout>
    

    activity_nomal.xml

    <?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">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="This is a normal activity" />
    
    </LinearLayout>
    

    第三步:修改主活动布局

    增加两个按钮

    <?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">
    
        <Button
            android:id="@+id/button_normal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="NormalActivity"
            android:textAllCaps="false" />
    
        <Button
            android:id="@+id/button_dialog"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="DialogActivity"
            android:textAllCaps="false" />
    
    
    </LinearLayout>
    

    第四步:修改主活动类
    绑定事件,并重写上面提到的七个函数,然后观察生命周期

    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Log.i(TAG, "onCreate");
    
            Button buttonNormal = (Button) findViewById(R.id.button_normal);
            Button buttonDialog = (Button) findViewById(R.id.button_dialog);
    
            buttonNormal.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent dialogIntent = new Intent(MainActivity.this, NormalActivity.class);
                    startActivity(dialogIntent);
                }
            });
    
            buttonDialog.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent normalIntent = new Intent(MainActivity.this, DialogActivity.class);
                    startActivity(normalIntent);
                }
            });
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            Log.i(TAG,"onStart");
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            Log.i(TAG,"onStop");
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.i(TAG,"onDestroy");
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            Log.i(TAG,"onPause");
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            Log.i(TAG,"onResume");
        }
    
        @Override
        protected void onRestart() {
            super.onRestart();
            Log.i(TAG,"onRestart");
        }
    }
    

    第五步:修改AndroidManifest.xml
    这里注意了,如果按书上的来写,会直接闪退。笔者查阅了一下资料,找到了答案。
    Android主题设置为@android:style/Theme.Dialog报错解决办法

    <activity
                android:name=".DialogActivity"
                android:theme="@style/Theme.AppCompat.Dialog">
    </activity>
    

    如果你和笔者一样使用的是魅族手机,还需要处理以下问题魅族手机,无法log.d的解决办法

    首先说下demo的结果

    1. 打开程序
      按照顺序显示onCreate、onStart和onResume
    2. 点击Normal按钮
      按照顺序显示onPause和onStop
    3. 返回
      按照顺序显示onRestart、onStart和onResume
    4. 点击Dialog按钮
      按照顺序显示onPause
    5. 返回
      按照顺序显示onResume
    6. 退出
      按照顺序显示onPause、onStop和onDestroy

    简单介绍一下笔者对他们的理解

    1. onCreate():这个方法只会在活动第一次被创建的时候调用,应该完成一些初始化的功能。主要用于加载布局、绑定事件等。
    2. onStart():这个方法主要是在界面由非打开状态转变成打开状态时调用,即由其他状态转化成运行状态时。例如刚打开或者从别的页面返回时,既由不可见变为可见时调用。特点就是此时的活动一定可见。
    3. onResume():这个方法主要是在界面准备启动或者恢复时调用,即由暂停状态转化成运行状态时。例如打开一个非完全覆盖屏幕的对话框式的活动返回时,此时该活动虽然可见,但是不在活动栈的最顶层,不是运行状态。即由可见不可运行变为可以运行时调用。特点就是此时的活动一定处于运行状态。
    4. onPause():这个方法在系统准备去启动或者恢复另一个活动的时候调用,由运行状态转化成暂停状态时使用。也就是运行这个方法后,活动任然可见,但是不能运行。
    5. onStop():这个方法在活动完全不可见的时候调用,由运行状态转化成停止状态时使用。也就是运行这个方法后,活动变得不可见。
    6. onDestroy():这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。简单理解就是back或者finish之后就会调用,Destroy的活动想要再被调用就要Create。
    7. onRestart():活动被重新启动时调用。笔者的理解就是当这个活动已经被创建,然后被另外一个活动完全覆盖,即触发onStop()方法后,如果返回这个活动,在onStart()之前需要使用onRestart()

    以上7个方法中除了onRestart()方法,其他都是两两相对的,从而又可以将活动分为3种生存期。

    完整生存期:【 onCreate()~ onDestroy()】之间。从出生到死亡
    可见生存期:【 onStart()~ onStop()】之间。活动对于用户总是可见的!一般使用onStart进行资源的加载,onStop对部分资源进行释放。
    前台生存期:【 onResume()~ onPause()】之间。活动总是处于运行状态的。

    生命周期示意图

    Bundle

    其实onCreate()一直带有一个Bundle参数,不知道是干啥的,现在终于知道了。是用于活动在被回收的情况下仍然可以实现数据的储存。实现这个功能的方法是savedInstanceState()和putString()和getString()。

    方法十分简单,在活动中重写savedInstanceState()方法并用putString()以key-value的方式存入数据。在需要的时候直接通过onCreate()带有的Bundle参数使用getString()获取。

    Bundle还可以和Intent的联用

    ——第一行代码
    不知道你有没有察觉,使用Bundle来保存和取出数据是不是有些似曾相识呢?没错!我们在使用Intent传递数据时也是用的类似的方法。这里跟你提醒一点,Intent 还可以结合Bundle一起用于传递数据,首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标活动之后先从Intent中取出Bundle,再从Bundle中一一取出数据。
    具体的代码我就不写了,要学会举一反三哦。

    Bundle经常被使用用来传递复杂类型,那么活动之间如何传递ArrayList呢?

    活动之间如何传递ArrayList

    活动的启动模式

    启动模式一共有4种,分别是standard、 singleTop、singleTask 和singleInstance,可以在AndroidManifest.xml中通过给标签指定android : launchMode属性来选择启动模式。

    1. standard
      默认的启动方式,不会去判断栈里面是否有重复,所以一个活动可以重复打开。
    2. singleTop
      对默认模式进行了一个优化,当活动正在栈顶的时候,如果再打开就不会重复实例化,打开多少个也都是栈顶的之前的活动。但是当这个活动不在栈顶,还是可以重复打开的。
    3. singleTask
      之前不是解释过多个任务在一起的栈叫Task么,所有很好理解,就是整个任务只有一个。这种启动模式很多好的解决了之前的多个实例化的问题。但需要实例的活动已经存在在栈中的时候,就没必要新建了,而是原来已经存在的直接出栈。
    4. singleInstance
      这个比较复杂,就是创新的使用了一个返回栈的概念。书上的例子笔者确实看明白了,但是作者说的共享活动和这个有什么关系笔者还是没想通。所以还是在以后用到的时候再详细了解吧。
  • 相关阅读:
    avrdude: stk500_getsync(): not in sync: resp=0x00
    PCB封装技术
    C/C++基础问题归集
    mf210v 端口的映射
    alsamixer 在音频子系统的使用
    rp2836 网卡以及串口与接插件位置关系
    RP2837 IN1-IN2 对应关系 2路DI
    RP2837 OUT1-OUT2 对应关系 2路DO
    RP2836 板卡信息标识
    RP2836 OUT0-OUT7 对应关系
  • 原文地址:https://www.cnblogs.com/zllk/p/13363651.html
Copyright © 2020-2023  润新知