Android四大组件之Activity(一)

Activity是一种包含用户界面的组件,主要用于和用户进行交互。接下来详细了解一下。

首先,手动创建Activity加深一下我们的理解。

新建Android项目,选择“Add No Activity”,点击Next,项目名输入ActivityTest,包名使用默认值com.example.activity,点击finish,创建成功。

现在项目创建完成。这里我们将Android模式的项目结果手动改为Project模式。创建Activity文件

右击com.example.activity包->New->Empty Activity,弹出一个Activity的对话框,将Activity命名为FirstActivity,并不要勾选generate layout file 和launcher activity,点击finish。

Android程序设计的理念是一个Activity对应一个布局文件,接下来我们创建一个布局文件:
 

右击app/src/main/res->New->Directory,弹出一个新建目录的窗口,这里先创建一个layout的目录。右击layout目录->New->Layout resource file,弹出一个新建布局文件的窗口,布局文件命名为first_layout,根元素默认为LinearLayout,点击OK完成创建。

由于我们选择了LinnerLayout作为根元素,我们现在对这个布局稍作修改,添加一个按钮,如下所示

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

这里添加了button元素,并在button元素里添加了几个属性。现在按钮已经显示出来了,布局已经编写完成,接下来就是要在Activity中加载这个布局。回到FirstActivity,在OnCreate()方法中添加如下代码:

setContentView(R.layout.first_layout)

这里调用了setContentView()方法给当前Activity加载布局,在这个方法中,我们一般会传入一个布局文件的id。

所有的Activity都要在AndroidManifest.xml中进行注册才能生效,此时FirstActivity已经注册过了,可以看到Activity的注册申明要放在<application>标签内,这里是通过<activity>标签对Activity进行注册的。

但现在只是对Activity注册了,程序仍不能运行,因为没有设定主Activity。只需要在<activity>内添加这段代码就行了。

 <intent-filter>
                <action android:name="android intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>

这样,FirstActivity就成了这个程序的主Activity了。点击应用图标后首先显示的就是这个Activity。


Android中的Toast是一种非常好的提醒方式,在程序中可以使用他将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,且不会占用任何屏幕空间。

首先需要定义一个Toast的触发点,我们将上述的按钮作为弹出Toast的触发点,在OnCreate()方法中添加如下代码:

 val bt1:Button=findViewById(R.id.bt1)
        bt1.setOnClickListener { 
            Toast.makeText(this,"You click bt1",Toast.LENGTH_SHORT).show()
        }

在Activity中,可以通过findViewById()方法获取在布局文件中定义的元素,这里我们传入R.id.bt1来得到按钮的实例,这个值是在first_layout.xml中通过android:id属性指定的。Kotlin无法推断findViewById()方法返回的是Button还是其他控件,所以我们需要将bt1变量显示的声明成Button类型。得到按钮的实例之后,通过调用setOnClickListener()方法为按钮注册一个监听器,点击按钮时就会执行监听器中的OnClick()方法。因此弹出Toast的功能要写在OnClick()方法中。

Toast的用法也很简单,通过静态方法makeTest()创建一个Toast对象,然后调用show()函数将Toast函数显示出来。makeTest()方法需要传入三个参数,,第一个参数是Context即Toast的上下文,这里我们填入this即可,第二个参数是Toast显示的文本内容,第三个参数是显示的时长。

如果布局文件的控件过多,那么我们调用findViewById()方法就会比较麻烦,于是就滋生出了第三方开源库,来简化这个方法的调用。目前最新的是viewBinding插件,由于多次失败我放弃了。


如果你的Activity中有大量菜单需要展示,页面设计就会比较尴尬。Android提供了一种方式可以让菜单都显示还不占用任何空间。

首先在res目录下新建一个menu文件夹,右击res->New->Directory,输入文件名“menu”,再建一个菜单文件“main”,右击menu->New->Menu resource file。然后在main.xml中添加如下代码:

<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>

这里我们创建了两个菜单项,其中<item>标签用来创建具体的某个菜单项,然后通过android:id给这个菜单项指定一个唯一的标识符,通过Android:title给这个 菜单项指定一个名称。

接着在FirstActivity中重写onCreateOptionsMenu()方法,重写方法使用Ctrl+O,然后在onCreateOptionsMenu()方法中编写如下代码:

 override fun onCreateOptionsMenu(menu: Menu?): Boolean {
       menuInflater.inflate(R.menu.main,menu)
       return true
    }

这里就涉及到了Kotlin提供的语法糖,Kotlin会自动将代码转换成调用set()和get()方法。

我们在onCreateOptionsMenu()方法中编写的menuInflater就使用了这种语法糖,他实际上是调用了父类的getMenuInflater()方法。getMenuInflater()方法能够得到一个MenuInflater对象,在调用它的inflate()方法,就可以给当前Activity创建菜单了。inflate()方法接收两个参数:第一个用于指定我们提供哪一个资源文件来创建菜单;第二个参数用于指定我们的菜单项将添加到哪个Menu对象中,这里直接使用onCreateOptionsMenu方法中传入的参数,最后给这个方法返回true,表示允许创建的菜单显示出来。

当然仅仅让菜单显示出来还不行,关键要让菜单可以用起来,因此还要再定义菜单响应事件。在FirstActivity中重写onOptionsItemSelected()方法,如图:

 override fun onOptionsItemSelected(item: MenuItem): Boolean {
      when(item.itemId){
          R.id.add_item->Toast.makeText(this,"you click add",Toast.LENGTH_SHORT).show()
          R.id.remove_item->Toast.makeText(this,"you click remove",Toast.LENGTH_SHORT).show()
      }
        return true
    }

在onOptionsItemSelected() 方法中,我们通过调用item.itemid来判断点击的是哪个菜单项。这里也应用了刚刚说到的语法糖,kotlin实际上在背后调用的是item的getItemId()方法。这里我们将item.itemid传入when语句中,然后给每个菜单项加入自己的逻辑处理,这里我们弹出toast。

可以看到,菜单里的菜单项默认是不会显示的,只有点击菜单按钮才会弹出里面具体的内容,因此他不会占用任何Activity的空间。


现在我们已经会创建Activity了,那么如何销毁呢?只需按下Back键即可销毁了,如果你想通过代码来销毁的话也可以,调用finish()方法就可以销毁当前Activity了。

修改按钮监听器账号的代码

 bt1.setOnClickListener {
            finish()
        }

这时点击以下按钮,当前activity就被销毁了。


一个应用不会只有一个Activity,,那么如何在多个Activity中跳转呢?

Intent是Androi程序中各组件之间进行交互的一种重要方式,他不仅可以执行当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可用于启动Activity、启动Service以及发送广播等场景。

Intent分为显式Intent和隐式Intent,首先介绍的是显示Intent。Intent有多个构造函数的重载,其中一个是Intent(Context packageContext,class<?>cls),如下

我们再次创建一个Activity,命名为SecondActivity。修改FirstActivity中按钮的点击事件:

 bt1.setOnClickListener {
            val intent= Intent(this,SecondActivity::class.java)
            startActivity(intent)
        }

我们首先构建了一个Intent对象,第一次参数传入this也就是FirstActivity作为上下文,第二个参数传入SecondActivity::class.java作为目标Activity,这样我们的意图也就明显了,就是在FirstActivity的基础上打开SecondActivity。接下来再通过startActivity()方法执行这个Intent就行了。

相比于显式Intent,隐式Intent并不明确指出要启动哪个Activity,而是指定了一系列更加抽象的category和action等信息,然后交由系统去分析这个Intent,并帮我们找出可以响应这个隐式Intent的Activity启动。

可以在<activity>标签下配置<intent-fliter>的内容,

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

在<action>标签中我们指明了当前Activity可以响应com.example.activitytest.ACTION_START这个action,而<category>标签则包含了一些附加信息,更明确的指明了当前Activity能够响应的Intent中还可能带有的category。只有这两项中的内容同时匹配到intent中指定的action和category时,这个activity才能响应该Intent。

修改FirstActivity中按钮的点击事件,可以看到,我们使用了Intent的另一个构造函数,直接将action的字符串传了进去,表明我们想要启动这个action的Activity。现在重新运行也同样可以启动SecondActivity了:

bt1.setOnClickListener {
            val intent=Intent("com.example.activitytest.ACTION_START")
            startActivity(intent)
        }

 每个Intent中只能指定一个action,但能指定多个category,目前我们的Intent中只有一个默认的category,那么我们再次增加一个:

 bt1.setOnClickListener {
            val intent=Intent("com.example.activitytest.ACTION_START")
            intent.addCategory("com.example.activitytest.MY_CATEGORY")
            startActivity(intent)
        }

可以调用intent中的addCategory()方法来添加一个category,这里我们指定了一个自定义的category。

现在重新运行程序,在界面点击一下按钮会发现程序崩溃了,在Logcat处查看日志,可以发现上面显示没有一个Activity可以响应我们的intent,因为我们新增了一个category,而<intent-fliter>标签中没有没有声明可以响应这个category,所以就出现了这种情况。现在我们再在<intent-fliter>标签添加一个category的声明(),再次运行,一切就正常了。

隐式Intent不仅可以启动自己程序内的Activity,还可以启动其他程序的Activity,这就使多个应用程序之间的功能共享成为可能。比如你的程序中需要展示一个网页,可以调用系统的浏览器打开这个网页,修改代码:

 bt1.setOnClickListener {
            val intent=Intent(Intent.ACTION_VIEW)
            intent.data= Uri.parse("https://www.baidu.com")
            startActivity(intent)
        }

这里我们首先定义了intent的action,这是一个Android系统内置的一个动作,其常量值为android.intent.action.VIEW.然后通过Uri.parse()方法将一个网址字符串解析成一个Uri对象,在调用Intent的setData()方法将这个对象传递进去。重新运行代码,点击按钮就可以看到打开了系统浏览器。

我们 还可在<intent-fliter>标签中配置一个<data>标签,用于更精确的响应当前Activity能够响应的数据。<data>标签中主要可配置以下几项:

  • android:scheme。用于指定用户的协议部分
  • android:host。用于指定数据的主机名部分
  • android:port。用于指定数据的端口部分,一般在主机名之后
  • android:path。用于指定主机名和端口之后的部分
  • android:mimeType。用于指定可以处理的数据类型,允许使用通配符的方式进行指定

只有当<data>标签中指定的内容和intent中携带的data完全一致时,当前Activity才能响应Intent。不过data中一般不会指定过多内容,如上述的浏览器事例中,只需要指定android:scheme为hhtps,就可以响应所有HTTPS协议的intent了。

我们再次新建一个Activity,命名一个按钮为bt3,在AndroidMainifest.xml中修改这个Activity的注册信息:

 <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="https"/>
            </intent-filter>

我们在ThirdActivity的<intent-fliter>中配置了当前Activity能够响应的action,而category指定了默认的值。另外在<data>标签中我们通过android:scheme指定了数据的协议必须是https协议,这样这个Activity就能和浏览器一样打开一个网页了。另外由于AndroidStudio认为所有能够响应ACTION_VIEW的Activity都应该加上BROWSABLE的category,否则就会发出警告。 

打开程序点击bt1系统会自动弹出一个列表,显示了目前能够响应这个intent的所有程序。

除了https协议外,我们还可以指定很多其他协议,比如geo表示地理位置,tel表示拨打电话。


在启动Activity时向下一级传递数据的思路很简单,intent提供了一系列putExtra()方法的重载,可以把数据暂存在intent,在启动另一个Activity时,把这些数据从intent中取出就可以了。

比如你想将FirstActivity中的字符串传递到SecondActivity可以这样写:

 bt1.setOnClickListener {
          val data="Hello SecondActivity"
            val intent=Intent(this,SecondActivity::class.java)
            intent.putExtra("extra_data",data)
            startActivity(intent)
        }

这里我们使用显式Intent的方法,并通过putExtra()方法传递了一个字符串。这里putExtra()方法接收俩个参数,第一个参数是键,用于之后从Intent处取值,第二个参数才是真正要传递的参数。

在SecondActivity中取出数据并打印出来

val extradata=intent.getStringExtra("extra_data")
        Log.d("SecondActivity","extra data is $extradata")

 这里的intent实际上调用的是父类的getIntent()方法,该方法会获取用于启动SecondActivity的Intent,然后调用getStringExtra()方法(另:传入整型数据则调用getIntExtra()方法,传入布尔型数据则调用getBOOleanExtra()方法,以此类推)并传入键值,就可以得到传递的数据了。


既然可以传递数据到下一个Activity,那么同样可以返回数据到上一个Activity。Activity类中有一个用于启动Activity的startActivityForResult()方法,他期望在Activity销毁的时候能够返回一个结果给上一个Activity。

startActivityForResult()方法接收两个参数:第一个参数还是Intent,第二个参数是请求码,用于在之后的回调中判断数据的来源。

修改FirstActivity中的按钮事件为:

 val intent=Intent(this,SecondActivity::class.java)
            startActivityForResult(intent,1)

这里我们使用了startActivityForResult()方法来启动SecondActivity,请求码只要是一个唯一值即可,这里传入1。

接下来我们在SecondActivity给按钮注册点击事件,并在点击事件中添加返回数据的逻辑:

bt2.setOnClickListener { 
            val intent=Intent()
            intent.putExtra("data_return","hello FirstActivity")
            setResult(RESULT_OK,intent)
            finish()
        }

我们还是构建了个Intent,不过这个Intent仅用于传递数据。紧接着把要传送的数据存放在Intent中,然后调用setResult()方法向上一个Activity返回数据,setResult()接收俩个参数:第一个参数向上一个Activity返回处理结果;第二个参数则把带有数据的Intent传递回去。最后调用了finish()方法来销毁当前Activity。

由于我们是使用startActivityForResult()启动SecondActivity的,在SecondActivity被销毁之前会回调上一个Activity的OnActivityResult()方法,因此我们需要在FirstActivity中重写这个方法来得到返回的数据:

  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when(requestCode){
            1 ->if (resultCode== RESULT_OK){
                val returnedData=data?.getStringExtra("data_return")
                Log.d("FirstActivity","return data is $returnedData")
            }
        }
    }

OnActivityResult()方法带有三个参数:第一个参数requestCode即我们启动Activity时传入的请求码;第二个参数resultCode即返回数据时传入的处理结果;第三个参数data即携带着返回数据的Intent。由于一个Activity中可能会有多次调用多个请求码,所以首先需要判断的就是请求码值的数据来源,之后在判断结果是否成功,最后将data数据打印出来。

现在SecondActivity的数据已经通过按钮返回给FirstActivity了,那如果不通过点击按钮数据能返回吗,还是可以的,我们可以在SecondActivity中重写OnBackPressed()来解决问题:

 override fun onBackPressed() {
        val intent=Intent()
        intent.putExtra("data_return","hello FirstActivity")
        setResult(RESULT_OK,intent)
        finish()
    }

这样,当用户按下back键就会执行这个代码。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值