目录
⑥CollapsingToolbarLayout——可折叠式标题栏
一、AS中的主要组件
1、Activity组件
[1]-Activity的基本使用
1.Activity是什么
Activity
是一种可以包含用户界面的组件,主要用于和用户进行交互。一个应用程序中可以包含零个或多个Activity
。
2.Activity中的onCreate()方法
onCreate()方法:用于在活动(Activity
)的生命周期中进行初始设置。这是每个 Activity
必须重写的方法,它提供了一个机会来初始化活动的数据成员、设置布局、绑定数据到视图等。
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
3.xml布局文件
Android程序的设计讲究逻辑和视图分离,最好每一个Activity都能对应一个布局。布局文件中是使用XML来进行界面编辑的。创建一个first_layout.xml文件,并编写如下代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<!--"vertical"属性表明子视图将垂直排列。-->
<!--match_parent表示布局的宽度和高度将匹配其父容器的大小。-->
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--创建Button按钮,命名为button1-->
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
<!-- wrap_content意味着按钮的高度将根据其内容-->
android:layout_height="wrap_content"
android:text=“Button 1” />
</LinearLayout>
4.给Activity加载布局
在onCreate()方法中加入如下代码即可给当前的Activity加载一个布局:
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//增加的代码↓
setContentView(R.layout.first_layout)
}
}
代码中的R
是一个自动生成的类,它代表资源引用。项目中添加的任何资源都会在R文件中为每个资源生成一个唯一标识符(ID),并将这些ID封装在R
类中。
first_layout
是创建的布局文件名称
5.在AndroidManifest文件中注册
所有的Activity
都要在AndroidManifest.xml
中进行注册才能生效,Activity的注册声明要放在<application>
标签内,并通过<activity>
标签来对Activity进行注册。
设置主Activity:可以通过在<activity>
标签的内部加入<intent-filter>
标签来配置主Activity
。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitytest">
<application …>
<activity android:name=".FirstActivity" android:label="This is FirstActivity">
<!--插入<intent-filter>标签设置主Activity-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
6.在Activity中使用Toast
Toast在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
//通过 findViewById() 方法找到布局文件中定义的 Button 控件,并将其赋值给变量 button1
val button1: Button = findViewById(R.id.button1)
//为 button1 设置一个点击事件监听器。当按钮被点击时,花括号内的代码块将被执行。
button1.setOnClickListener {
//创建一个 Toast 对象,显示消息 "You clicked Button 1",持续时间为短时长(Toast.LENGTH_SHORT)。
//this 关键字在这里指代当前的 Activity 实例。
//最后调用 show() 方法来显示这个提示。
Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
}
}
7.在Activity中使用Menu
提供了一种机制Menu
,可以让界面中的菜单项在默认情况下不显示。只有当用户主动点击了菜单按钮时,才会弹出里面具体的内容,因此它不会占用任何Activity的空间。
一般在res/menu/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>
[2]Activity的生命周期
1.返回栈
系统总是会显示处于栈顶的Activity给用户。
在默认情况下,每当我们启动了一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish()方法去销毁一个Activity时,处于栈顶的Activity就会出栈,前一个入栈的Activity就会重新处于栈顶的位置。
2、Activity状态
①运行状态 ②暂停状态 ③停止状态 ④销毁状态
3、Activity生存期
onCreate() 这个方法在Activity第一次被创建的时候调用。
onStart() 这个方法在Activity由不可见变为可见的时候调用。
onResume() 这个方法在Activity准备好和用户进行交互的时候调用。
onPause() 这个方法在系统准备去启动或者恢复另一个Activity的时候调用。
onStop() 这个方法在Activity完全不可见的时候调用。
onDestroy() 这个方法在Activity被销毁之前调用。
onRestart() 这个方法在Activity由停止状态变为运行状态之前调用,也就是Activity被重新启动了。
4、Activity启动模式
①standard
standard是Activity默认的启动模式,在不进行显式指定的情况下,所有Activity都会自动使用这种启动模式。在standard模式下,每当启动一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的Activity,系统不会在乎这个Activity是否已经在返回栈中存在,每次启动都会创建一个该Activity的新实例。
②singleTop
当Activity的启动模式指定为singleTop,在启动Activity时如果发现返回栈的栈顶已经是该Activity,则认为可以直接使用它,不会再创建新的Activity实例。
③singleTask
当Activity的启动模式指定为singleTask,每次启动该Activity时,系统首先会在返回栈中检查是否存在该Activity的实例,如果发现已经存在则直接使用该实例,并把在这个Activity之上的所有其他Activity统统出栈,如果没有发现就会创建一个新的Activity实例。
④singleInstance
当Activity的启动模式指定为singleInstance,会启用一个新的返回栈来管理这个Activity。
假设我们的程序中有一个Activity是允许其他程序调用的,如果想实现其他程序和我们的程序可以共享这个Activity的实例,就可以使用singleInstance模式。在这种模式下,会有一个单独的返回栈来管理这个Activity,不管是哪个应用程序来访问这个Activity,都共用的同一个返回栈,也就解决了共享Activity实例的问题。
2、Intent组件
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。
①使用显示Intent
显式Intent可以用于明确指定启动某个Activity,在FirstActivity文件中编写如下代码:
//这段代码就表示,将会从FirstActivity跳转到SecondActivity
button1.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
代码解析:
首次实例化了一个intent对象,第一个参数代表FirstActivity上下文,第二个参数SecondActivity::class.java
作为目标Activity,且这段代码的前提是已经在项目的AndroidManifest.xml
文件中定义了SecondActivity
,并且它是一个有效的Activity
类
②使用隐式Intent
隐式Intent并不明确指出想要启动哪一个Activity,而是指定了一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并帮我们找出合适的Activity(合适的就是可以响应这个隐式intent的Activity)去启动。
比如应用程序中需要展示一个网页,只需要调用系统的浏览器来打开这个网页就行了。
要让对应的Activity可以正常响应对应的隐式intent,要在AndroidManifest.xml
文件的<Activity>
标签下配置<intent-filter>
的内容,可以指定当前Activity能够响应的action和category:
<activity
android:name=".SecondActivity"
android:exported="true">
<intent-filter>
<action android:name="com.example.myapplication.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.myapplication.MY_CATEGORY" />
</intent-filter>
</activity>
设置1:
button1.setOnClickListener {
val intent = Intent("com.example.myapplication.ACTION_START")
intent.addCategory("com.example.myapplication.MY_CATEGORY")
startActivity(intent)
}
设置2:
button1.setOnClickListener {
//在点击事件中,首先创建了一个Intent对象。
//Intent.ACTION_VIEW是一个常量,表示这个Intent用于“查看”某个内容。
//在这种情况下,它通常用于打开一个网页或者一个地图位置等。
val intent = Intent(Intent.ACTION_VIEW)
//intent.data设置了Intent的数据,即用户想要查看的内容
//使用Uri.parse()方法将字符串"https://www.baidu.com"转换成Uri对象,作为Intent的数据。
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)
}
补充:每个intent中只能指定一个action,但是可以指定多个category
Uri
是一个类,用于表示一个统一资源标识符,它是一种标准的互联网概念,用于标识网络上的资源。
③使用intent传递数据
假设要将FirstActivity中的字符串传递到SecondActivity中:
在FirstActivity中这样传输:
button1.setOnClickListener {
val data = "Hello SecondActivity"
//创建了一个 Intent 对象,用于从当前活动(隐含的 this 指当前的 Activity)跳转到 SecondActivity。
val intent = Intent(this,SecondActivity::class.java)
//这里putExtra()方法接收两个参数,
//第一个参数是键,用于之后从Intent取值,
//第二个参数才是真正要传的数据
intent.putExtra("extra_data",data)
startActivity(intent)
}
在SecondActivity中这样接收:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.second_layout)
//从 Intent 中提取一个字符串类型的额外数据,"extra_data"是键,获取键值
val extraDate = intent.getStringExtra("extra_data")
//使用 Log.d 方法将接收到的额外数据输出到Logcat。"SecondActivity" 是日志的标签,它用于在Logcat中标识日志的来源。
Log.d("SecondActivity","extra data is $extraDate")
}
3、Fragment组件
Fragment
是一种可以嵌入在Activity
当中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用得非常广泛。每个Fragment都有自己的生命周期,并且可以独立于Activity进行加载、隐藏或显示。Fragment通常用于创建动态、多面板的UI。
Fragment有onCreateView()
方法来加载视图
Fragment的生命周期:
onAttach()
当Fragment和Activity建立关联时调用。onCreateView()
为Fragment创建视图(加载布局)时调用。onActivityCreated()
确保与Fragment相关联的Activity已经创建完毕时调用。onDestroyView()
当与Fragment关联的视图被移除时调用。onDetach()
当Fragment和Activity解除关联时调用。
用例:
新建一个左侧Fragment的布局left_fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Button"
/>
</LinearLayout>
新建一个右侧Fragment的布局right_fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#00ff00"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="24sp"
android:text="This is right fragment"
/>
</LinearLayout>
然后编写LeftFragment和RightFragment
class LeftFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.left_fragment, container, false)
}
}
class RightFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.right_fragment, container, false)
}
}
编写activity_main.xml,在布局中引入两个Fragment
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/leftFrag"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/rightFrag"
android:name="com.example.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
4、BroadcastReceiver组件
BroadcastReceiver
是一个用于接收系统或应用发出的广播(Broadcast)消息的组件。这些广播可以是来自系统的(例如网络状态改变、电池电量低等),也可以是来自其他应用的自定义广播。
标准广播:是一种完全异步执行的广播,在广播发出后,所有的BroadcastReceiver几乎会在同一时刻收到这条广播消息,因此他们之间没有任何先后顺序,不可以被截断。
有序广播:是一种同步执行的广播,在广播发出之后,同一时刻只会有一个BroadcastReceiver能够收到这个广播消息,当这个BroadcastReceiver的逻辑执行完毕之后,广播才会继续传播。此时的BroadcastReceiver是有先后顺序的,可以被截断。
5、ContentProvider组件
ContentProvider
是 Android 框架中的一个类,它允许应用程序或库定义和提供对数据的访问。这种数据可以是文件系统中的文件、数据库中的数据、网络资源,或者任何其他类型的持久性存储。
ContentProvider
使得数据可以在不同的应用程序之间共享,并且可以通过 URI 进行访问。
6、Service组件和线程使用
①Service
Service是Android中实现程序后台运行的解决方案,它非常适合执行那些不需要和用户交互而且还要求长期运行的任务。Service的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,Service仍然能够保持正常运行。
注意:
Service并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在Service的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况。
Service的用法:
(1)定义一个Service
右键项目的任何包路径→New→Service→Service,会弹出窗口。
Exported属性表示是否将这个Service暴露给外部其他程序访问;
Enabled属性表示是否启用这个Service,点击“ Finish”完成创建。
(2)在AndroidManifest文件中注册
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest">
<application …>
...
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
</service>
</application>
</manifest>
(3)重写Service
然后需要重写onCreate()
、onStartCommand()
和onDestroy()
这3个方法。
其中onCreate()方法会在Service创建的时候调用;
onStartCommand()方法会在每次Service启动的时候调用;
onDestroy()方法会在Service销毁的时候调用。
class MyService : Service() {
…
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
}
}
(4)启动和停止Service
Context类中提供了startService()和stopService()这两个方法来启动和停止Service,所以我们在Activity里可以直接调用这两个方法。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startServiceBtn.setOnClickListener {
val intent = Intent(this, MyService::class.java)
startService(intent) // 启动Service
}
stopServiceBtn.setOnClickListener {
val intent = Intent(this, MyService::class.java)
stopService(intent) // 停止Service
}
}
}
②线程
创建线程:
定义一个线程只需要新建一个类继承自Thread,然后重写父类的run()方法
//方法一:
class MyThread : Thread() {
override fun run() {
// 编写具体的逻辑
}
}
//方法二
thread {
// 编写具体的逻辑
}
启动这个线程只需要创建MyThread的实例,然后调用它的start()方法即可,这样run()方法中的代码就会在子线程当中运行了,如下所示:
MyThread().start()
注意:
在 Android 开发中,UI 更新必须在主线程(也称为 UI 线程)上执行。这是因为 Android 的 UI 控制器不是线程安全的,如果从子线程更新 UI,可能会导致不可预测的行为和应用程序崩溃。
二、AS中的UI界面
1、控件
①TextView
TextView
它可以用来显示简单的文本信息
②Button
Button
用于创建按钮的视图组件,用户可以点击按钮来触发事件,如启动一个新活动、提交表单或执行其他操作。
③EditText
EditText
用于输入和编辑文本的 UI 组件,它允许用户在应用程序中键入和修改文本。
EditText
通常用于表单、搜索框和任何需要文本输入的场景。
④ImageView
用于显示图像的 UI 组件,它可以显示来自多种来源的图片,包括资源文件、文件系统、网络等。
⑤ProgressBar
用于向用户显示某个操作正在进行中的控件,它以图形方式表示某个任务的进度
补充:可以通过style属性指定不同的样式
示例:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/Button1"
android:text="Button1"/>
<ProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progressBar"/>
</LinearLayout>
class MainActivity : AppCompatActivity(),View.OnClickListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.Button1)
button.setOnClickListener(this)
}
override fun onClick(v: View?) {
val progressBar = findViewById<ProgressBar>(R.id.progressBar)
when(v?.id){
R.id.Button1 ->{
if (progressBar.visibility==View.VISIBLE){
progressBar.visibility=View.GONE
}else{
progressBar.visibility=View.VISIBLE
}
}
}
}
}
⑥AlertDialog
用于在当前的界面上显示一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力,因此它通常用于提示一些非常重要的内容或者警告信息。
AlertDialog.Builder(this).apply {
setTitle("This is Dialog")
setMessage("Something important.")
setCancelable(false)
setPositiveButton("OK") { dialog, which ->
}
setNegativeButton("Cancel") { dialog, which ->
}
show()
}
⑦ListView
用于在屏幕上以垂直列表的形式展示一组数据项。每个数据项通常被称为一个列表项(list item),可以包含文本、图像或其他自定义视图。ListView 支持用户滚动浏览大量数据,并且允许用户对列表项进行交互操作,如点击、长按等。
⑧RecyclerView
1)RecyclerView说明:
RecyclerView是Android开发中一个强大且灵活的控件,用于在列表和网格中展示大量数据。它相较于传统的ListView,具有更好的性能表现和灵活性。
RecyclerView的特点主要包括:
-
可扩展性:RecyclerView具有强大的可扩展性,可以轻松实现各种复杂的布局和交互效果。
-
ViewHolder模式:RecyclerView使用ViewHolder模式来管理子视图,通过复用已有的视图结构而不是持续创建新的列表项,这大大提高了应用的时间效率和空间效率。同时,它也避免了频繁的findViewById操作,从而提高了性能。
-
LayoutManager:RecyclerView通过LayoutManager来控制布局方式,支持纵向滑动的列表、横向滑动的列表、交错布局的列表和网格布局的列表等。开发者还可以创建自定义的LayoutManager来实现特殊的布局效果。
-
动画效果:RecyclerView支持添加、删除、移动Item时的默认动画效果,并提供了自定义动画的接口,可以根据需求自定义动画效果。
2)使用RecyclerView的基本步骤:
-
添加依赖:首先,你需要在
build.gradle
文件中添加RecyclerView的依赖。通常,你可以使用如下的代码来添加:
implementation 'androidx.recyclerview:recyclerview:1.x.x' // x为具体的版本号
-
在布局文件中添加RecyclerView:在XML布局文件中,添加RecyclerView控件,并为其分配一个唯一的ID。可以通过两种方式添加:
在XML文件中直接添加RecyclerView标签。
在布局设计工具中,找到RecyclerView控件并拖拽到布局中。
-
创建数据模型:创建一个表示列表项的数据模型类。这个类应该包含用于表示列表项的属性。
-
创建适配器Adapter:使用数据模型创建一个适配器。这个适配器应该实现
RecyclerView.Adapter
接口,并提供用于表示列表项的视图和方法。需要重写三个方法:onCreateViewHolder(parent:ViewGroup, viewType:Int): //用来创建ViewHolder的实例。 onBindViewHolder( holder:ViewHolder, position:Int): //用来将数据与ViewHolder的视图进行绑定。 getItemCount(): //返回数据的数量。
例如:
class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() { //首先定义内部类ViewHolder,继承自RecyclerView.ViewHolder,并且在主构造函数中传入View参数 //这个参数通常是RecycleView子项的最外层布局 //这样就可以通过findViewById()方法获取布局中的ImageView和TextView实例 inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val fruitImage: ImageView = view.findViewById(R.id.fruitImage) val fruitName: TextView = view.findViewById(R.id.fruitName) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val fruit = fruitList[position] holder.fruitImage.setImageResource(fruit.imageId) holder.fruitName.text = fruit.name } override fun getItemCount() = fruitList.size }
补充: ViewHolder是一个包含视图的对象,它可以缓存视图的引用,避免在每次滚动或刷新列表时都重新查找和创建视图,从而提高列表的滑动性能和响应速度。
-
设置RecyclerView:在Activity或Fragment中,找到RecyclerView控件,并为其分配适配器。你可以使用
findViewById()
方法获取RecyclerView的实例,然后调用setAdapter()
方法为其设置适配器。 -
加载数据:使用适当的加载数据机制(如异步加载)从服务器或其他数据源获取数据,并将其传递给适配器。这通常会在适配器的构造函数或某个设置数据的方法中完成。
-
显示数据:适配器将使用数据填充RecyclerView,并在用户交互时更新列表项。
2、布局
①LinearLayout
又称作线性布局,这个布局会允许其子元素按照水平(horizontal
)或垂直(vertical
)方向排列
②RelativeLayout
它允许子元素根据其兄弟元素或父容器的位置进行定位。与 LinearLayout
不同,RelativeLayout
不限制子元素只能按照一个特定的方向(水平或垂直)排列,而是允许子元素在屏幕上自由定位,允许相对于控件进行定位。
③FrameLayout
它指定屏幕上的一块空白区域,在该区域填充一个或多个单一对象,如图片、文字、按钮等。这些组件在默认情况下会被固定在屏幕的左上角,后放入的组件会放在前一个组件上进行覆盖填充,形成部分遮挡或全部遮挡。
3、Material Design
当引入Material 库的时候,需要把主题改为:Theme.MaterialComponents.Light.NoActionBar
,否则用到的Material 控件可能遇到崩溃问题
①标题栏
Toolbar
提供了一个水平或垂直的条,其中可以包含各种工具元素,如按钮、菜单、标题等。Toolbar 通常用于应用顶部,作为传统的 ActionBar 的替代品,提供更灵活的自定义选项。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</FrameLayout>
代码释义:
在Toolbar中添加action按钮:
通过<item>
标签定义action按钮
showAsAction
属性可以有以下几种值:
-
never: 菜单项永远不会直接在
Toolbar
中显示,它只会出现在溢出菜单(通常是一个三点图标)中。 -
always: 菜单项始终显示在
Toolbar
中,即使Toolbar
空间不足也不会移动到溢出菜单。 -
ifRoom: 如果
Toolbar
有足够的空间,菜单项将显示在Toolbar
中;如果空间不足,它将自动移动到溢出菜单。 -
withText: 与
ifRoom
类似,但即使在Toolbar
中,菜单项也会显示文本标签。这可以确保用户总是能看到菜单项的文本描述,即使它在Toolbar
中。 -
collapseActionView: 这个值用于具有可折叠
ActionView
(如搜索视图)的菜单项。当菜单项被点击时,ActionView
会展开,并且菜单项本身会从Toolbar
中消失。
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/backup"
android:icon="@drawable/ic_backup"
android:title="Backup"
app:showAsAction="always" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="Delete"
app:showAsAction="ifRoom" />
<item
android:id="@+id/settings"
android:icon="@drawable/ic_settings"
android:title="Settings"
app:showAsAction="never" />
</menu>
在Activity中启用Toolbar和创建的菜单
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.toolbar, menu)
return true
}
}
②滑动菜单
(1)DrawerLayout
滑动菜单,就是将一些菜单选项隐藏起来,而不是放置在主屏幕上,然后可以通过滑动的方式将菜单显示出来。Google在AndroidX库中提供了一个DrawerLayout控件,借助这个控件,实现滑动菜单简单又方便。
DrawerLayout
是一个布局,在布局中允许放入两个直接子控件,
第一个子控件是主屏幕中显示的内容,第二个子控件是滑动菜单中显示的内容。
如下代码中,DrawerLayout中放置了两个直接子控件:第一个子控件是FrameLayout,用于作为主屏幕中显示的内容;第二个子控件是一个TextView,用于作为滑动菜单中显示的内容。
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#FFF"
android:text="This is menu"
android:textSize="30sp" />
</androidx.drawerlayout.widget.DrawerLayout>
(2)NavgationView
NavigationView
是 Android Material Design 库中的一个组件,它提供了一个侧滑菜单的界面,通常与 DrawerLayout
结合使用来实现应用的导航抽屉,支持显示菜单项、头像、以及可选的菜单标题和子标题。
NavgationView的使用:
在使用前,需要准备:menu和headerLayout;
- menu是在NavgationView中显示具体菜单项的
- headerLayout是用来在NavgationView中显示头部布局的。
下面是一个简单的 NavigationView
使用示例:
导航菜单 (res/menu/drawer_menu.xml):
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_home"
android:icon="@drawable/ic_home"
android:title="Home" />
<!-- 其他菜单项 -->
</group>
</menu>
头部布局 (nav_header.xml):
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="User Name"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</LinearLayout>
布局文件 (activity_main.xml):
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主要内容 -->
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
Activity 中设置 NavigationView:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navigationView: NavigationView = findViewById(R.id.nav_view)
navigationView.setNavigationItemSelectedListener { menuItem ->
// 处理菜单项点击事件
true
}
}
}
③悬浮按钮和交互提示
(1)FloatingActionButton
FloatingActionButton(悬浮按钮)是 Material Design 设计语言中的一个组件,它是一种圆形的按钮,通常带有图标,并浮动在屏幕内容的上方。
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
…
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/ic_done" />
</FrameLayout>
…
</androidx.drawerlayout.widget.DrawerLayout>
(2)Snackbar
是Material库提供的提示工具,它比Toast提示工具更灵活,允许在提示中加入可交互的按钮,在点击时可以执行一些额外的逻辑判断和操作
Snackbar
可以包含一个动作按钮,允许用户与之交互,例如撤销一个操作或执行其他任务。Toast
是非交互式的,它只显示消息,用户不能与之交互。
示例:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 创建 Snackbar
val snackbar = Snackbar.make(findViewById(android.R.id.content), "这是一条 Snackbar 消息", Snackbar.LENGTH_LONG)
// 设置动作按钮
snackbar.setAction("撤销") {
Toast.makeText(this, "撤销按钮被点击", Toast.LENGTH_SHORT).show()
}
// 自定义 Snackbar
snackbar.view.setBackgroundColor(Color.GREEN)
snackbar.setActionTextColor(Color.WHITE)
// 添加回调
snackbar.addCallback(object : Snackbar.Callback() {
override fun onShown(sb: Snackbar) {
super.onShown(sb)
// Snackbar 显示后的回调
}
override fun onDismissed(sb: Snackbar, event: Int) {
super.onDismissed(sb, event)
// Snackbar 消失后的回调
}
})
// 显示 Snackbar
snackbar.show()
}
}
(3)CoordinatorLayout
CoordinatorLayout 是 Android Material Design 库中的一个非常灵活的布局容器,它可以和与各种 Material Design 组件协同工作,如 AppBarLayout、CollapsingToolbarLayout、FloatingActionButton 等。CoordinatorLayout 可以处理这些组件之间的交互和动画效果,提供流畅的用户体验。
CoordinatorLayout就像一个加强版本的FrameLayout,它可以直接替换FrameLayout工作,没有其他副作用
④卡片式布局
它可以让页面中的元素看起来就像在卡片中一样,并且还能拥有圆角和投影。
(1)MaterialCardView
MaterialCardView是用于实现卡片式布局效果的重要控件,由Material库提供。
MaterialCardView也相当于一个FrameLayout,只是额外提供了圆角和阴影等效果
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="8dp"
app:cardElevation="4dp"
app:cardUseCompatPadding="true">
<!-- 在这里添加卡片内容,如 TextView、ImageView 等 -->
</com.google.android.material.card.MaterialCardView>
(2)AppBarLayout
AppBarLayout用于实现应用的顶部应用栏(也称为“顶栏”或“导航栏”)。它是一个垂直的布局,可以包含Toolbar等视图,提供了滚动和折叠效果,与 CoordinatorLayout一起使用时,可以实现复杂的交互效果。
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
<!-- 可以添加其他视图,如 CollapsingToolbarLayout 或者 TabLayout -->
</com.google.android.material.appbar.AppBarLayout>
⑤SwipeRefreshLayout——下拉刷新
SwipeRefreshLayout就是用于实现下拉刷新功能的核心类,它是由AndroidX库提供的。
把要实现下拉刷新功能的控件放置到SwipeRefreshLayout中,就可以让这个控件支持下拉刷新的功能。
关键特性:
-
刷新指示器: 当用户执行下拉刷新动作时,会显示一个进度指示器,通常是圆形的,告知用户正在刷新。
-
自定义颜色: 可以自定义刷新指示器的颜色,以符合应用的主题。
-
触发刷新: 可以编程触发刷新事件,比如在获取到新数据后。
-
监听刷新事件: 可以设置监听器来响应用户的刷新操作。
-
动画效果: 刷新时有平滑的动画效果,提升了用户体验。
-
与
RecyclerView
、ListView
等组件的集成: 可以很容易地与这些组件集成,实现列表数据的刷新。
例如:在xml文件中定义
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 内容视图,比如 RecyclerView 或 ListView -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
在kotlin中设置其属性和操作
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
val swipeRefreshLayout = findViewById<SwipeRefreshLayout>(R.id.swipe_refresh_layout)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
// 设置刷新时的颜色方案
swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark)
// 设置刷新监听器
swipeRefreshLayout.setOnRefreshListener {
// 执行刷新操作,比如加载新数据
loadData()
}
// 加载数据
loadData()
}
private fun loadData() {
// 模拟协程开启网络请求或数据加载
GlobalScope.launch {
// 假设这里进行了异步数据加载操作
delay(2000) // 模拟延迟
withContext(Dispatchers.Main) {
// 更新 UI 和停止刷新指示器
swipeRefreshLayout.isRefreshing = false
// 假设这里更新了 RecyclerView 的数据
}
}
}
}
⑥CollapsingToolbarLayout——可折叠式标题栏
它用于创建具有折叠效果的工具栏布局,作用于Toolbar之上。当用户滚动内容时,它可以使得工具栏(如 Toolbar)的标题逐渐折叠并隐藏起来,从而提供一种动态的用户体验。这通常与 AppBarLayout 和 CoordinatorLayout 结合使用,因为它在设计的时候被限定只能作为AppBarLayout的直接子布局才能使用,而AppBarLayout又必须是CoordinatorLayout的子布局。
三、Jetpack
1、ViewModel
专门用于存放与界面相关的数据的。只要是界面上能看得到的数据,它的相关变量都应该存放在ViewModel中,而不是Activity中,这样可以在一定程度上减少Activity中的逻辑。ViewModel的生命周期和Activity不同,它可以保证在手机屏幕发生旋转的时候不会被重新创建,只有当Activity退出的时候才会跟着Activity一起销毁。
所以将与界面相关的变量存放在ViewModel当中,这样旋转手机屏幕,界面上显示的数据也不会丢失。
例如:
//可以使用如下代码定义一个ViewModel,并加入一个counter变量用于计数:
class MainViewModel : ViewModel() {
var counter = 0
}
//然后在Activity中使用如下代码即可获得ViewModel的实例
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
}
}
2、Lifecycles
Lifecycles指的是组件(如 Activity、Fragment 或者自定义的 ViewModel)在其存在期间经历的不同阶段。
Lifecycles可以让任何一个类都能轻松感知到Activity的生命周期,同时又不需要在Activity中编写大量的逻辑处理。
用法:
新建一个MyObserver类,并让它实现LifecycleObserver接口,然后使用方法注解就能感知到Activity的生命周期了:
class MyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun activityStart() {
Log.d("MyObserver", "activityStart")
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun activityStop() {
Log.d("MyObserver", "activityStop")
}
}
在Activity中调用addObserver()方法来观察LifecycleOwner的生命周期:
class MainActivity : AppCompatActivity() {
…
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
…
lifecycle.addObserver(MyObserver())
}
…
}
3、LiveData
LiveData是Jetpack提供的一种响应式编程组件,它可以包含任何类型的数据,并在数据发生变化的时候通知给观察者,LiveData 确保在组件处于活跃状态时才更新 UI,从而避免常见的内存泄漏问题。
LiveData常常和ViewModel一起使用
用法:
定义 LiveData
对象:
class MyViewModel : ViewModel() {
val someData: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
fun setData(data: String) {
someData.value = data
}
}
在 Activity
或 Fragment
中观察 LiveData
:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
viewModel.someData.observe(this, Observer { data ->
// 更新 UI
// 只有在 Activity 处于活跃状态时,才会执行这里的代码
TextView(text).text = data
})
}
}
4、Room
Room 是 Android Jetpack 的一个组件,它提供了一个抽象层,用于在 SQLite 数据库中进行流畅且一致的数据访问。
Room主要包含三个主要组件:实体(Entity)、数据访问对象(Dao)和数据库(Database)。
-
实体(Entity):Room中的实体类用于定义数据库中的表结构。每个实体类代表数据库中的一张表,其中的属性对应表中的列。
-
数据访问对象(Dao):Room中的数据访问对象类用于定义与数据库交互的方法。每个Dao类包含一组用于增删改查数据的方法。
-
数据库(Database):Room中的数据库类用于创建和管理数据库。它是一个抽象类,需要继承并指定所使用的实体类和版本号。通过数据库类,可以获取到对应的Dao对象,从而进行数据库操作。
用法:
定义 Entity(实体):
@Entity
data class User(
@PrimaryKey val userId: Int,
@ColumnInfo(name = "name") val name: String
)
定义 DAO(数据访问对象):
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAllUsers(): LiveData<List<User>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Delete
suspend fun deleteUser(user: User)
}
定义 Database:
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
初始化和使用 RoomDatabase:
val database: AppDatabase = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"database-name"
).build()
// 获取 UserDao
val userDao = database.userDao()
// 观察所有用户
val allUsers: LiveData<List<User>> = userDao.getAllUsers()
// 在 Activity 或 Fragment 中观察 LiveData
allUsers.observe(this, Observer { users ->
// 更新 UI
})
5、WorkManager
它提供了一个用于执行异步任务的 API,特别是那些需要在后台运行的任务。WorkManager 能够保证任务在指定的约束条件下执行,即使应用退出或设备重启,任务也会被执行。
WorkManager的基本用法主要分为以下3步:
- 定义一个后台任务,并实现具体的任务逻辑。
- 配置该后台任务的运行条件和约束信息,并构建后台任务请求。
- 将该后台任务请求传入WorkManager的enqueue()方法中,系统会在合适的时间运行
第一步要定义一个后台任务,这里创建一个SimpleWorker类,代码如下所示:
class SimpleWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
Log.d("SimpleWorker", "do work in SimpleWorker")
return Result.success()
}
}
第二步,配置后台任务的运行条件和约束信息,代码如下所示:
val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
.setInitialDelay(5, TimeUnit.MINUTES)
.build()
最后一步,将构建出的后台任务请求传入WorkManager的enqueue()方法中,系统就会在合适的时间去运行了,代码如下所示:
WorkManager.getInstance(context).enqueue(request)
四、数据存储
1、文件存储
文件存储是Android中最基本的数据存储方式,它不对存储的内容进行任何格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合存储一些简单的文本数据或二进制数据。
将数据储存到文件中:
Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。
所有的文件会默认存储到/data/data/<package name>/files/目录下。示例写法如下:
fun save(inputText: String) {
try {
val output = openFileOutput("data", Context.MODE_PRIVATE)
val writer = BufferedWriter(OutputStreamWriter(output))
writer.use {
it.write(inputText)
}
} catch (e: IOException) {
e.printStackTrace()
}
}
从文件中读取数据:
Context类中还提供了一个openFileInput()方法,用于从文件中读取数据。
它会自动到/data/data/<package name>/files/目录下加载文件,并返回一个FileInputStream对象,得到这个对象之后,再通过流的方式就可以将数据读取出来了。示例写法如下:
从文件中读取数据
fun load(): String {
val content = StringBuilder()
try {
val input = openFileInput("data")
val reader = BufferedReader(InputStreamReader(input))
reader.use {
reader.forEachLine {
content.append(it)
}
}
} catch (e: IOException) {
e.printStackTrace()
}
return content.toString()
}
2、SharePreference存储
SharedPreferences是以键值对(key-value)的形式存储数据,并且数据以 XML 文件的形式存储在设备上。当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。
关键特性:
- 简单易用: 提供了简单的 API 来存储和检索数据。
- 键值对存储: 数据以键值对的形式存储,支持
String
、int
、float
、long
和boolean
类型。 - 自动持久化: 数据会自动保存到磁盘上,无需手动操作。
- 监听变化: 可以注册监听器来监听
SharedPreferences
的变化。 - 文件存储: 数据存储在私有的 XML 文件中,通常位于
/data/data/<package_name>/shared_prefs/
目录下。
向SharedPreferences文件中存储数据:
- 调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象。
- 向SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用putBoolean()方法,添加一个字符串则使用putString()方法,以此类推。
- 调用apply()方法将添加的数据提交,从而完成数据存储操作。
val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
editor.putString("name", "Tom")
editor.putInt("age", 28)
editor.putBoolean("married", false)
editor.apply()
从SharedPreferences中读取数据:
SharedPreferences对象中提供了一系列的get方法,用于对存储的数据进行读取,每种get方法都对应了SharedPreferences.Editor中的一种put方法。比如读取一个布尔型数据就使用getBoolean()方法,读取一个字符串就使用getString()方法。
val prefs = getSharedPreferences("data", Context.MODE_PRIVATE)
val name = prefs.getString("name", "")
val age = prefs.getInt("age", 0)
val married = prefs.getBoolean("married", false)
3、SQLite数据库存储
①SQLiteDatabase存储
②Room存储
五、网络通信
1、WebView
WebView
是 Android 中用于显示网页内容的 UI 组件。它允许你将完整的网络浏览器功能集成到你的 Android 应用中,使用户能够在应用内浏览和交互网页。
用法:
设置控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
调用:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView.settings.setJavaScriptEnabled(true)
webView.webViewClient = WebViewClient()
webView.loadUrl("https://www.baidu.com")
}
}
2、HttpURLConnection
HttpURLConnection是 Android 中用于执行 HTTP 通信的低级 API。它允许开发者发送 HTTP 请求并接收响应,支持 HTTP 协议的多种特性,如请求方法(GET、POST、PUT、DELETE 等)、请求头、查询参数和请求体。
HttpURLConnection的使用相对底层,需要手动处理许多细节。在实际开发中更倾向于使用更高级的网络库,如 Retrofit、OkHttp
关键特性:
-
标准 HTTP 方法: 支持 HTTP 的标准请求方法。
-
请求头: 可以自定义请求头信息。
-
响应码: 可以获取服务器响应的 HTTP 状态码。
-
响应数据: 可以读取服务器响应的数据。
-
超时设置: 可以设置连接超时和读取超时。
-
重定向: 支持自动处理 HTTP 重定向。
-
缓存: 支持缓存控制。
-
多部分请求: 支持发送多部分请求(multipart/form-data)。
使用HttpURLConnection发送 GET 请求的示例:
URL url = new URL("http://www.example.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法,默认为 GET
connection.setRequestMethod("GET");
// 添加请求头
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "My App 1.0");
// 设置连接超时和读取超时
connection.setConnectTimeout(5000); // 5秒
connection.setReadTimeout(5000); // 5秒
// 发送请求并获取响应码
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// 读取响应数据
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
// 打印响应数据
Log.d("HTTP Response", response.toString());
} else {
// 处理错误响应
Log.e("HTTP Error", "Response Code: " + responseCode);
}
// 断开连接
connection.disconnect();
3、OkHttp
它提供了一个简单易用的 API,用于发出 HTTP 请求和处理响应。OkHttp
支持同步和异步请求,自动处理重试和 cookie,支持 HTTPS,并且可以轻松集成到 Android 应用中。
关键特性:
-
HTTP/2 支持:
OkHttp
支持 HTTP/2 协议,可以提高连接的效率。 -
自动重试: 网络请求失败时,
OkHttp
可以自动重试。 -
连接池:
OkHttp
使用连接池减少请求延迟。 -
拦截器:
OkHttp
支持拦截器,允许开发者拦截请求和响应,进行自定义处理。 -
HTTPS 支持:
OkHttp
支持 HTTPS,可以配置 SSL 证书。 -
同步和异步请求:
OkHttp
支持同步和异步请求,便于开发者根据需要选择。 -
JSON 和 XML 支持:
OkHttp
可以方便地与 JSON 和 XML 数据格式配合使用。 -
文件下载和上传:
OkHttp
支持大文件的上传和下载。 -
WebSockets:
OkHttp
支持 WebSockets。 -
拦截器: 可以自定义拦截器,例如添加身份验证、修改请求头等。
使用OkHttp获取GET请求的示例:
fun sendGetRequest() {
val client = OkHttpClient()
val request = Request.Builder()
.url("http://www.example.com")
.build()
// 异步请求
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
e.printStackTrace()
// 处理请求失败
}
override fun onResponse(call: okhttp3.Call, response: Response) {
if (!response.isSuccessful) {
// 处理错误响应
return
}
// 处理响应数据
val responseData = response.body?.string()
responseData?.let { data ->
Log.d("HTTP Response", data)
}
}
})
}
4、Retrofit
OkHttp侧重的是底层通信的实现,而Retrofit侧重的是上层接口的封装,它将 HTTP API 转换成 Java 接口,利用编译时注解处理来自动处理网络请求和数据解析,使得网络请求代码更加简洁和安全。
关键特性:
-
类型安全:
Retrofit
利用 Java 泛型和注解,确保类型安全。 -
数据转换: 支持多种数据转换库,如 Gson、Moshi、Jackson 等,自动处理 JSON、XML 等数据格式的序列化和反序列化。
-
同步和异步请求: 支持同步和异步请求,便于开发者根据需要选择。
-
请求构建: 通过注解和构建器模式,方便地构建各种类型的 HTTP 请求。
-
支持多种 HTTP 操作: 支持 GET、POST、PUT、DELETE 等多种 HTTP 操作。
-
拦截器: 支持自定义拦截器,用于处理认证、日志记录、请求重试等。
-
支持 WebSockets: 可以创建 WebSockets 连接。
-
支持 RxJava: 可以与 RxJava 集成,方便地使用响应式编程。
示例:
定义服务接口:
interface MyApiService {
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String): Call<List<Repo>>
}
创建 Retrofit
实例:
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
使用服务接口发送请求:
val service = retrofit.create(MyApiService::class.java)
service.listRepos("octocat").enqueue(object : Callback<List<Repo>> {
override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {
if (response.isSuccessful) {
// 处理返回的仓库列表
val repos = response.body()
// 使用 repos 更新 UI 或 做其他处理
} else {
// 处理错误响应
}
}
override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
// 处理请求失败
}
})
六、MVVM架构
MVVM(Model-View-ViewModel)架构是一种设计模式,用于构建 UI 应用程序,将视图(UI)从业务逻辑和数据模型中分离出来。
以下是 MVVM 架构中各个组件的简要说明:
-
Model(模型):
- 代表应用的数据结构和业务逻辑。
- 可以包含网络请求、数据库访问等操作。
-
View(视图):
- 指的是用户界面,如 Activity、Fragment 或者自定义 View。
- 负责显示数据和接收用户输入。
-
ViewModel(视图模型):
- 作为 Model 和 View 之间的桥梁,包含 UI 相关的数据和逻辑。
- 通常使用
ViewModel
类来实现,它可以存储和管理界面相关的数据,并且与视图进行交互。
-
Data Binding(数据绑定):
- 一种技术,允许 UI 自动获取后端数据的变化并更新。
- 通过在布局文件中声明绑定,可以减少在 Activity 或 Fragment 中编写样板代码。
-
LiveData(动态数据):
- 一个可观察的数据存储器,用于 UI 相关的数据。
- 当数据变化时,它可以通知观察者(如 ViewModel 或 View)。
-
Repository(仓库):
- 作为数据访问层,提供统一的接口来访问数据。
- 封装了数据来源的细节,如本地数据库、网络请求等。
-
Lifecycle(生命周期):
- ViewModel 的生命周期与 Activity 或 Fragment 的视图状态解耦。
- 允许 ViewModel 在配置更改(如屏幕旋转)时保持数据。
-
Navigation(导航):
- 用于处理应用中的导航逻辑,如 Fragment 切换、Activity 启动等。