Bootstrap

关于Android的学习心得

目录

一、AS中的主要组件

1、Activity组件

2、Intent组件

3、Fragment组件

4、BroadcastReceiver组件

5、ContentProvider组件

6、Service组件和线程使用

①Service

②线程

二、AS中的UI界面

1、控件

①TextView

②Button

③EditText

④ImageView

⑤ProgressBar

⑥AlertDialog

⑦ListView

⑧RecyclerView

2、布局

①LinearLayout

②RelativeLayout

③FrameLayout

3、Material Design

①标题栏

Toolbar

②滑动菜单

(1)DrawerLayout

(2)NavgationView

③悬浮按钮和交互提示

(1)FloatingActionButton

(2)Snackbar

(3)CoordinatorLayout

④卡片式布局

(1)MaterialCardView

(2)AppBarLayout

⑤SwipeRefreshLayout——下拉刷新

⑥CollapsingToolbarLayout——可折叠式标题栏

三、Jetpack

1、ViewModel

2、Lifecycles

3、LiveData

4、Room

5、WorkManager

1、文件存储

2、SharePreference存储

3、SQLite数据库存储

①SQLiteDatabase存储

②Room存储

五、网络通信

1、WebView

2、HttpURLConnection

3、OkHttp

4、Retrofit

六、MVVM架构


一、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的生命周期:

  1. onAttach() 当Fragment和Activity建立关联时调用。
  2. onCreateView() 为Fragment创建视图(加载布局)时调用。
  3. onActivityCreated()确保与Fragment相关联的Activity已经创建完毕时调用。
  4. onDestroyView()当与Fragment关联的视图被移除时调用。
  5. 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的特点主要包括:

  1. 可扩展性:RecyclerView具有强大的可扩展性,可以轻松实现各种复杂的布局和交互效果。

  2. ViewHolder模式:RecyclerView使用ViewHolder模式来管理子视图,通过复用已有的视图结构而不是持续创建新的列表项,这大大提高了应用的时间效率和空间效率。同时,它也避免了频繁的findViewById操作,从而提高了性能。

  3. LayoutManager:RecyclerView通过LayoutManager来控制布局方式,支持纵向滑动的列表、横向滑动的列表、交错布局的列表和网格布局的列表等。开发者还可以创建自定义的LayoutManager来实现特殊的布局效果。

  4. 动画效果:RecyclerView支持添加、删除、移动Item时的默认动画效果,并提供了自定义动画的接口,可以根据需求自定义动画效果。

2)使用RecyclerView的基本步骤:

  1. 添加依赖:首先,你需要在build.gradle文件中添加RecyclerView的依赖。通常,你可以使用如下的代码来添加:

implementation 'androidx.recyclerview:recyclerview:1.x.x' // x为具体的版本号

  1. 在布局文件中添加RecyclerView:在XML布局文件中,添加RecyclerView控件,并为其分配一个唯一的ID。可以通过两种方式添加:

    在XML文件中直接添加RecyclerView标签。

    在布局设计工具中,找到RecyclerView控件并拖拽到布局中。

  2. 创建数据模型:创建一个表示列表项的数据模型类。这个类应该包含用于表示列表项的属性。

  3. 创建适配器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是一个包含视图的对象,它可以缓存视图的引用,避免在每次滚动或刷新列表时都重新查找和创建视图,从而提高列表的滑动性能和响应速度。

  4. 设置RecyclerView:在Activity或Fragment中,找到RecyclerView控件,并为其分配适配器。你可以使用findViewById()方法获取RecyclerView的实例,然后调用 setAdapter() 方法为其设置适配器。

  5. 加载数据:使用适当的加载数据机制(如异步加载)从服务器或其他数据源获取数据,并将其传递给适配器。这通常会在适配器的构造函数或某个设置数据的方法中完成。

  6. 显示数据:适配器将使用数据填充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属性可以有以下几种值:

  1. never: 菜单项永远不会直接在 Toolbar 中显示,它只会出现在溢出菜单(通常是一个三点图标)中。

  2. always: 菜单项始终显示在 Toolbar 中,即使 Toolbar 空间不足也不会移动到溢出菜单。

  3. ifRoom: 如果 Toolbar 有足够的空间,菜单项将显示在 Toolbar 中;如果空间不足,它将自动移动到溢出菜单。

  4. withText: 与 ifRoom 类似,但即使在 Toolbar 中,菜单项也会显示文本标签。这可以确保用户总是能看到菜单项的文本描述,即使它在 Toolbar 中。

  5. 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;

  1. menu是在NavgationView中显示具体菜单项的
  2. 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中,就可以让这个控件支持下拉刷新的功能。

关键特性:

  1. 刷新指示器: 当用户执行下拉刷新动作时,会显示一个进度指示器,通常是圆形的,告知用户正在刷新。

  2. 自定义颜色: 可以自定义刷新指示器的颜色,以符合应用的主题。

  3. 触发刷新: 可以编程触发刷新事件,比如在获取到新数据后。

  4. 监听刷新事件: 可以设置监听器来响应用户的刷新操作。

  5. 动画效果: 刷新时有平滑的动画效果,提升了用户体验。

  6. RecyclerViewListView 等组件的集成: 可以很容易地与这些组件集成,实现列表数据的刷新。

例如:在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
    }
}

ActivityFragment 中观察 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 文件的形式存储在设备上。当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。

关键特性:

  1. 简单易用: 提供了简单的 API 来存储和检索数据。
  2. 键值对存储: 数据以键值对的形式存储,支持 Stringintfloatlong 和 boolean 类型。
  3. 自动持久化: 数据会自动保存到磁盘上,无需手动操作。
  4. 监听变化: 可以注册监听器来监听 SharedPreferences 的变化。
  5. 文件存储: 数据存储在私有的 XML 文件中,通常位于 /data/data/<package_name>/shared_prefs/ 目录下。

向SharedPreferences文件中存储数据:

  1. 调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象。
  2. 向SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用putBoolean()方法,添加一个字符串则使用putString()方法,以此类推。
  3. 调用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

关键特性:

  1. 标准 HTTP 方法: 支持 HTTP 的标准请求方法。

  2. 请求头: 可以自定义请求头信息。

  3. 响应码: 可以获取服务器响应的 HTTP 状态码。

  4. 响应数据: 可以读取服务器响应的数据。

  5. 超时设置: 可以设置连接超时和读取超时。

  6. 重定向: 支持自动处理 HTTP 重定向。

  7. 缓存: 支持缓存控制。

  8. 多部分请求: 支持发送多部分请求(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 应用中。

关键特性:

  1. HTTP/2 支持: OkHttp 支持 HTTP/2 协议,可以提高连接的效率。

  2. 自动重试: 网络请求失败时,OkHttp 可以自动重试。

  3. 连接池: OkHttp 使用连接池减少请求延迟。

  4. 拦截器: OkHttp 支持拦截器,允许开发者拦截请求和响应,进行自定义处理。

  5. HTTPS 支持: OkHttp 支持 HTTPS,可以配置 SSL 证书。

  6. 同步和异步请求: OkHttp 支持同步和异步请求,便于开发者根据需要选择。

  7. JSON 和 XML 支持: OkHttp 可以方便地与 JSON 和 XML 数据格式配合使用。

  8. 文件下载和上传: OkHttp 支持大文件的上传和下载。

  9. WebSockets: OkHttp 支持 WebSockets。

  10. 拦截器: 可以自定义拦截器,例如添加身份验证、修改请求头等。

使用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 接口,利用编译时注解处理来自动处理网络请求和数据解析,使得网络请求代码更加简洁和安全。

关键特性:

  1. 类型安全: Retrofit 利用 Java 泛型和注解,确保类型安全。

  2. 数据转换: 支持多种数据转换库,如 Gson、Moshi、Jackson 等,自动处理 JSON、XML 等数据格式的序列化和反序列化。

  3. 同步和异步请求: 支持同步和异步请求,便于开发者根据需要选择。

  4. 请求构建: 通过注解和构建器模式,方便地构建各种类型的 HTTP 请求。

  5. 支持多种 HTTP 操作: 支持 GET、POST、PUT、DELETE 等多种 HTTP 操作。

  6. 拦截器: 支持自定义拦截器,用于处理认证、日志记录、请求重试等。

  7. 支持 WebSockets: 可以创建 WebSockets 连接。

  8. 支持 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 架构中各个组件的简要说明:

  1. Model(模型):

    • 代表应用的数据结构和业务逻辑。
    • 可以包含网络请求、数据库访问等操作。
  2. View(视图):

    • 指的是用户界面,如 Activity、Fragment 或者自定义 View。
    • 负责显示数据和接收用户输入。
  3. ViewModel(视图模型):

    • 作为 Model 和 View 之间的桥梁,包含 UI 相关的数据和逻辑。
    • 通常使用 ViewModel 类来实现,它可以存储和管理界面相关的数据,并且与视图进行交互。
  4. Data Binding(数据绑定):

    • 一种技术,允许 UI 自动获取后端数据的变化并更新。
    • 通过在布局文件中声明绑定,可以减少在 Activity 或 Fragment 中编写样板代码。
  5. LiveData(动态数据):

    • 一个可观察的数据存储器,用于 UI 相关的数据。
    • 当数据变化时,它可以通知观察者(如 ViewModel 或 View)。
  6. Repository(仓库):

    • 作为数据访问层,提供统一的接口来访问数据。
    • 封装了数据来源的细节,如本地数据库、网络请求等。
  7. Lifecycle(生命周期):

    • ViewModel 的生命周期与 Activity 或 Fragment 的视图状态解耦。
    • 允许 ViewModel 在配置更改(如屏幕旋转)时保持数据。
  8. Navigation(导航):

    • 用于处理应用中的导航逻辑,如 Fragment 切换、Activity 启动等。

;