Android 四大组件之一的Activity

Android开发工具: Android Stuido
这篇文章主要是讲解Activity的,但是如果不讲Layout布局文件的话很难带入,所以会先简单讲一下布局文件,让大家有一个基本概念

什么是Activity,什么是Layout文件

  • 对于手机用户来说,关心的只是当我手指按在屏幕某个位置之后手机会发生什么,如页面跳转,播放音乐,聊天等等;而对于APP开发者来说,最基本的关心也应当是用户的关心。怎么实现这份关心呢?Android把它分成了两个相互依赖的部分:Activity和Layout。
  • Layout(布局)文件
    • 在Android中,布局文件是.xml的文件,存在地址是:项目根目录\app\src\main\res\layout。是我们看到的APP页面的基本展示效果,
  • Activity(活动)
    • 是继承自Activity的.java文件(也可以是.kt文件),存放的地址是:项目根目录\src\main\java\创建项目时定义的包。一个活动对应一个布局文件(也可以不对应,但是没有意义),具体的关联方式就是在Activity中声明对Layout文件的引用,所有的逻辑操作都是在这里完成的,比如与后台数据的交互以及将数据展示在布局文件上等。
  • 命名规范:因为一般一个Layout对应一个Activity嘛,所以他们的命名也是非常有规律的:Activity的名字一般叫做“+++Activity”,如TestActivity,而与之对应的layout文件一般被命名为“activity_+++”,如activity_test。

Layout文件介绍:

  • 我们看到的一个个页面就是一个个布局文件的展示效果再加上相对应的Activity的数据放入,布局文件最基本的构成就是布局方式(也就是页面展示逻辑),他有五种布局方式,还有就是自定义的布局方式,我这里会主要说一下两种最常用的,也就是LinearLayoutRElativeLayout
  • 每个Layout文件的根标签都是布局方式的设定,以用来明确在其内部(也就是该页面)的最基本的样式效果,而且不同的布局方式在操作其内子标签的时候有些属性所达到的效果是不一样的。

  • LinearLayout(线性)布局

    • LinearLayout布局的根标签就是LinearLayout,特点是内部子标签默认会按照由左到右或是由上到下的顺序排放
    • LinearLayout布局又分为两类,一类是水平线性布局,一类是垂直线性布局。
    • 水平线性布局的属性android:orientation的值是horizontal;垂直线性布局的android:orientation的值是vertical。
    • 如下可以非常直观的看出线性布局的两个方式的不同(我是用了三个标签,标签颜色都不一样):
    • 代码一:垂直布局:

      activity_main.xml:
      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          tools:context="com.example.createnew.MainActivity"
          android:orientation="vertical"> <!--就是这个属性规定的水平或垂直线性布局-->
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="线性布局标签一"
      
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="线性布局标签二"
              android:textColor="#574483"/>
      
          <Button
              android:onClick="btn"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="跳转水平布局按钮"/>
      </LinearLayout>
      
      MainActivity.java:
      public class MainActivity extends AppCompatActivity { //AppCompatActivity是Activity的子类兼容到v7版本
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main); //布局文件引用
          }
      
          public void btn(View v){ //跳转水平布局按钮单击事件
              Intent intent = new Intent(MainActivity.this, TestActivity.class);
              startActivity(intent);
          }
      }
      
    • 效果:
      垂直线性布局
  • 代码二:水平布局:

    activity_test.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="horizontal">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="水平布局标签一"
                android:textColor="#227486"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="水平布局标签二"
                android:textColor="#574483"/>
    
            <Button
                android:onClick="btn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="跳转垂直布局按钮"/>
        </LinearLayout>
    
        TestActivity.java
        public class TestActivity extends AppCompatActivity {
    
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_test);
            }
    
            public void btn(View v){
                Intent intent = new Intent(TestActivity.this, MainActivity.class);
                startActivity(intent);
            }
        }
    
    • 效果:
      水平线性布局
  • RelativeLayout(相对布局)布局

    • 相对布局的根标签就是RelativeLayout,基本特点是如果不设置子标签相对的位置,默认所有的子标签都是放在左上角的

      布局文件,activity_relative_demo.xml:
      <?xml version="1.0" encoding="utf-8"?>
      <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent">
      
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="相对布局标签一"/>
      
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello World 标签二"/>
      
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="剧场版 标签三"/>
      </RelativeLayout>
      
      Activity文件RelativeDemoActivity.java:
      public class RelativeDemoActivity extends AppCompatActivity {
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_relative_demo);
          }
      }
      
    • 效果:
      全部的标签都在左上角
    • Relative布局方式的特点从效果很容易就看出来了,他做的更多的是两个标签的相对位置,有些时候线性布局不好操作,然后转用相对布局就会觉得特别轻松。
  • 除了上述的线性布局和相对布局,Android还有三种已定义好的布局方式,分别是:FrameLayout(帧布局),TableLayout(表格布局), 绝对布局(在API2.0被废了,可以用,但一般都是用其他布局代替)。

Activity介绍

  • 常识1. 四大组件都必须在清单文件进行配置

    • 我们通常说的清单文件就是AndroidMainfest.xml,其存在位置是:项目名\app\src\main\AndroidManifest.xml,具体配置的位置就是Application标签里面;
    • 用AndroidStudio创建一个项目后,会自动生成一个Activity,就是MainActivity.java,在清单文件的配置如下:

      <activity
              android:name=".MainActivity"
              android:label="@string/app_name"
              android:theme="@style/AppTheme.NoActionBar">
              <intent-filter>
                  <action android:name="android.intent.action.MAIN" />
                  <category android:name="android.intent.category.LAUNCHER" />
              </intent-filter>
          </activity>
      

      name属性表示的是该配置对应的Activity地址,这里的”.MainActivity”的”.”是因为Activity存在的包是默认的。在该Activity配置中,还有一个意图过滤器标签” intent-filter”,里面再有两个标签表示的是当项目运行在设备上时第一个加载的页面,也就是APP的入口;当然一个APP可以有多个入口Activity,不过好像市面上没看到过。
      以后我们每写一个Activity,都需要在清单文件中进行配置,如果忘记配置的话在跳转相应的Activity的时候会爆
      Caused by: android.content.ActivityNotFoundException:
      Unable to find explicit activity class
      {com.example.createnew/com.example.createnew.RelativeDemoActivity};
      have you declared this activity in your AndroidManifest.xml?异常;
      一般使用最简单的配置就可以了(就是只有name属性)。如上面创建的TestActivity和RelaiveDemoActivity:

      <activity android:name=".TestActivity"></activity>
      <activity android:name=".RelativeDemoActivity"></activity>
      
  • 常识2. 四大组件之间通过Intent(意图)关联:

    • Activity之间的跳转和传递数据也是通过Intent来实现的,意图分为两种:显示意图和隐式意图,前面的页面跳转效果就是显示意图做的。
    • 隐式意图:通过制定一组动作或是数据来实现页面间跳转:

      <!--隐式意图配置Activity-->
      <activity android:name=".ImplicitIntentActivity">
          <intent-filter>
              <action android:name="com.example.test.implicitIntent"></action>
              <category android:name="android.intent.category.DEFAULT"></category>
              <data android:scheme="test"
                  android:mimeType="aa/bb"/>
          </intent-filter>
      

      隐式意图方式跳转页面就是在清单文件中配置的时候加上意图过滤器,意图过滤器有三个子标签:action,category, data;data可以没有,在跳转页面的时候就不用考虑:

      ImplicitIntentActivity.Java:
      public class ImplicitIntentActivity extends AppCompatActivity {
      
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
      
              setContentView(R.layout.activity_implicit_intent);
              Intent intent = getIntent();
      //        String name = intent.getStringExtra("name");
      //        Toast.makeText(ImplicitIntentActivity.this, name, Toast.LENGTH_SHORT).show();
          }
      }
      
      从MainActivity隐式跳转ImplicitIntentActivity的Intent:
      //隐式意图跳转页面
      public void btn_implicit(View v){
          Intent intent = new Intent();
          intent.setAction("com.example.test.implicitIntent"); //和配置的意图过滤器中action的name值对应
          intent.addCategory("android.intent.category.DEFAULT"); //和category的name值对应
          //intent.setType("aa/bb");
          //设置数据约束,如果data和Type都有设置的话,后调用的会把前面调用的覆盖,所以只能使用setDataAndType一起设置。
          //intent.setData(Uri.parse("test:" + 110));
          intent.setDataAndType(Uri.parse("test:" + 123), "aa/bb");
          startActivity(intent);
      }
      
    • 显示意图: 通过上下文和需要跳转的Activity.class就可以了

      从MainActivity显示跳转ImplicitIntentActivity的Intent:
      Intent Intent = new Intent(MainActivity.this, ImplicitIntentActivity.class);
      startActivity(intent);
      
    • 页面间传递数据(不区分显示还是隐式意图):

      • 页面跳转间有两种Intent的传递参数:单向传递和页面关闭的返回数据。
      • 单向传递指Activity1跳转到Activity2的时候传递1的数据给2,而当2关闭时不回返数据给1。

        1. putExtra(String, Object):该方法几乎封装了所有基本数据类型的传递,甚至是列表也可以传递,该方法重写量非常大;该方法有两个参数,有点类似Map, 第一个参数是key(String类型), 第二个参数可以是数字、String…;

          用法:Activity1:
          intennt intent = new Intent(Activity1.this, Activity2.class);
          intent.putExtra("name","zhangsan");
          intent.putExtra("age",21);
          Activity2获取数据:
          Intent intent = getIntent();//获取跳转到该页面的Intent;
          String name = intent.getStringExtra("name"); //传递过来的数据是String就用String接收
          int age = intent.getIntExtra("age");//传递过来的数据是int就用Inte接收...
          
        2. putExtras(Bundle): 传递的是一个Bundle对象,该类底层是用Map封装的,所以用法也很像。

          用法:Activity1:
          Bundle bundle = new Bundle();
          bundle.putString("name", "zhangsan");
          bundle.putInt("age", 21);
          intent.putExtras(bundle);
          startActivity(intent);
          Activity2获取数据:
          Intent intent = getIntent();
          Bundle bundle = intent.getExtras();
          String name = bundle.getString("name");
          int age = bundle.getInt("age");
          
      • 页面关闭的返回数据指先是从Activity1 跳转到Activity2,当2关闭的时候会传递数据给Activity1。

        • 在前面,我们都是使用startActivity(intent)的方式开启意图并跳转页面,而如过想接收回转的数据,就必须在Activity1页面使用startActivityForResult(int requestCode, Intent intent)方法开启意图;还有就是在Activity1中必须覆写方法onActivityResult();

          用法:
          Activity1:
          public void btn_implicit(View v){
              Intent intent = new Intent(MainActivity.this,RelativeDemoActivity.class);
              startActivityForResult(intent, 0); //使用这种方式跳转Activity2
          }
          /**
          * 覆写 onActivityresult方法,处理返回的数据
          /
           @Override
          protected void onActivityResult(int requestCode, int resultCode, Intent data) {
              super.onActivityResult(requestCode, resultCode, data);
          // requestCode是startActivityForResult(intent, 0)中定义的0,resultCode是下面的setResult(1, intent)设置的1
              if(requestCode == 0 && resultCode == 1){
                  String name = data.getStringExtra("name");
                  Toast.makeText(MainActivity.this, name, Toast.LENGTH_SHORT).show();
              }
          }
          
          Activity2:
          public void btn(View v){
              Intent intent = new Intent();
              intent.putExtra("name", "Hello World");
              setResult(1, intent); //设置返回的意图,并设置返回的相应码
              finish(); //结束当前Activity
          }
          
    • 日常开发中使用显示意图:
      • 我们手机自带很多APP,如打电话,发邮件等,如果我们需要他们的功能会使用隐式意图的方式去调用,而如果是我们自己开发,最好是建议不暴露这些方式给其他APP使用。
  • Activity的生命周期
    这里写图片描述

上图就是Activity的生命周期图,可以看出:
1.一个ActivityRunning之前会执行的方法有:onCreate() -> onStart()), ->onResume();
2.根据实际测试,当按下后退键的时候,调用的方法是:onPause() -> onStop() -> onDestroy();
3. 当按下后退键再打开APP调用的步骤与1一样。
4. 当按下HOME键回退页面的时候调用的方式是:onPause() -> onStop(),这个时候没有调用onDestroy()方法,
5. 当按下HOME键再打开APP调用的方法步骤是:onRestart() -> onStart() -> onResume();(和1 不同的是没有调用onCreate方法而是调用onRestart方法)。

  • 禁止后退键:

    覆写onKeyDown方法:
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if(keyCode == KeyEvent.KEYCODE_BACK){
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
    
  • 横竖屏切换的生命周期变化:

    onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> onResume()
    横竖屏切换就相当于把Activity关闭再开启,当然也可以固定住,就是在清单文件中相应的Activity配置里加上:
    android:screenOrientation=”portrait”//竖屏
    android:screenOrientation=”landscape”//横屏

  • Activity的栈及四中启动模式:

    • 一般来说,一个APP就是一个栈,也采用后进先出的方式放置每一个Activity,打开Activity叫进栈,关闭Activity叫出栈,当前操作的Activity永远处于栈顶的位置。
    • 启动模式是说Activity以何种方式进栈或出栈:
      • 需要在清单文件相对应的Activity中配置:android:launchMode=”standard”;
      • 该属性有四个值:singleInstance;singleTask;singleTop; standard。
      • standard:
        • 和栈的方式一模一样:打开一个Activity就进一次栈,关闭就出一次栈。(不管新开启的页面前面有没有开启)
      • singleTop:
        • 如果Activity配置成了这种启动模式,任务栈会检查任务栈的栈顶Activity是谁,如果栈顶Activity是他本身,会直接复用栈顶的Activity,不会再次入栈
        • 作用:避免一个糟糕的用户体验,如果这个界面已经被打开且在任务栈的栈顶,就不会重复开启了
      • singleTask
        • 如果Activity配置成这种启动模式,当开启时,会检查该任务栈例是否有该实例存在,如果有,就直接复用已存在的实例Activity,并且把这个Activity上面的所有别的Activity都清空。
      • singleInstance
        • 使用这种启动模式的Activity,会自己单独创建一个任务栈,且这个任务栈中只有一个这种Activity,系统自动生成的任务栈中没有该Activity;当调用这个Activity时,这个额外的任务栈会排在系统创建的前面。

猜你喜欢

转载自blog.csdn.net/qq_37108755/article/details/76540323