GDPU Android移动应用 数据存储

又是学到了数据持久化。

登录界面

题外话:有无动画大佬带带呀,前端移动端可免( •̀ .̫ •́ ),合作可私信哦。

1.用户登陆和“记住我”功能

该内容拥有两个Activity活动视图:

(1)LoginActivity(LoginActivity.kt、activity_login.xml):登陆和注册Activity,具备用户登录和记住帐户密码的功能;

(2)MainActivity(MainActivity.kt、activity_main.xml):登陆成功后显示的主页面,显示“登陆成功!”(TextView)。

在登陆活动LoginActivity中,可基于“实验4界面设计1”的登陆Activity进行开发设计,包含的控件有:

(1)“用户名”和“密码”的输入框(EditText)和对应的文本说明(TextView),

(2)“注册”按钮(Button)

(3)“登陆”按钮(Button)

(4)“记住我”勾选框(CheckBox)

实现以下功能(需求):

(1)普通登陆成功:用户可以输入用户名和密码,然后点击“登陆”,如果用户名和密码正确,即可登录,跳转到MainActivity中。

(2)普通登陆失败:用户可以输入用户名和密码,然后点击“登陆”,如果用户名或密码不正确,弹出警告对话窗(参考4.2.6节),提醒用户重新输入。

(3)“记住我“登陆成功:用户可以输入用户名和密码,勾选“记住我”勾选框后,然后点击“登陆”,如果用户名和密码正确,即可登录,此时会跳转到MainActivity。同时,根据勾选“记住我”作为条件判断,记住当前正确验证的用户名和密码信息。那么,在下一次打开应用的LoginActivity时,会自动填写用户名和密码信息。

(4)“记住我“登陆失败:用户可以输入用户名和密码,勾选“记住我”勾选框后,然后点击“登陆”,如果用户名或密码不正确,弹出警告对话窗,提醒用户重新输入。同时,不会根据“记住我”勾选框进行用户名和密码信息的保存。

(5)用户名和密码信息保存:通过SharedPreferences方式进行保存,文件名命名为“remember”。

(6)用户注册:用户可在“用户名”和“密码”的输入框进行输入,点击“注册”即可注册帐户。注册时,需要检查用户名是否重复,如果重复,则弹出警告对话框提醒;如果不重复,则弹出Toast显示“注册成功”提醒注册成功。

(7)用户注册信息保存:成功注册的帐户,以文件存储方式,将文件名命名为“accounts”来保存用户名和密码信息,用户名和密码信息按以下格式进行保存:

用户名1,密码1

用户名2,密码2

用户名3,密码3

解释:用户名和密码以英文逗号隔开,每一行保存一个帐户信息。

这道题的主活动就是一行文本很简单,所以主活动改为下一题的,即可以先写一个登录界面,然后直接进入主界面进行添加图书。

LoginActivity.kt

package com.example.t8import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.CheckBox
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialogclass LoginActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_login)  // 修正了布局文件名称val userNameEditText: EditText = findViewById(R.id.userName)val passWordEditText: EditText = findViewById(R.id.passWord)val rememberMeCheckBox: CheckBox = findViewById(R.id.rememberMe)// 获取 SharedPreferencesval prefs = getPreferences(Context.MODE_PRIVATE)val isRememberMe = prefs.getBoolean("remember_me", false)// 如果记住密码,设置已保存的用户名和密码if (isRememberMe) {val username = prefs.getString("username", "") ?: ""val password = prefs.getString("password", "") ?: ""userNameEditText.setText(username)passWordEditText.setText(password)rememberMeCheckBox.isChecked = true}// 注册按钮点击事件val registerButton: Button = findViewById(R.id.register)registerButton.setOnClickListener {val username = userNameEditText.text.toString()val password = passWordEditText.text.toString()if (prefs.contains(username)) {AlertDialog.Builder(this).setTitle("注册失败").setMessage("用户名已存在,请重新输入!").setPositiveButton("确定", null).show()} else if (username.isEmpty()) {Toast.makeText(this, "用户名不能为空", Toast.LENGTH_SHORT).show()} else if (password.isEmpty()) {Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show()} else {val editor = prefs.edit()editor.putString(username, password)editor.apply()Toast.makeText(this, "注册成功", Toast.LENGTH_SHORT).show()userNameEditText.text.clear()passWordEditText.text.clear()}}// 登录按钮点击事件val loginButton: Button = findViewById(R.id.login)loginButton.setOnClickListener {val username = userNameEditText.text.toString()val password = passWordEditText.text.toString()if (prefs.getString(username, "") == password) {val editor = prefs.edit()if (rememberMeCheckBox.isChecked) {editor.putBoolean("remember_me", true)editor.putString("username", username)editor.putString("password", password)} else {editor.clear()}editor.apply()// 登录成功,跳转到 MainActivityval intent = Intent(this, MainActivity::class.java)startActivity(intent)finish()} else if (username.isEmpty()) {Toast.makeText(this, "用户名不能为空", Toast.LENGTH_SHORT).show()} else if (password.isEmpty()) {Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show()} else {AlertDialog.Builder(this).setTitle("登录失败").setMessage("用户名或密码错误").setPositiveButton("确定", null).show()}}}
}

activity_login.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><!-- First LinearLayout (for username and password input) --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="80dp"android:orientation="horizontal"android:gravity="bottom"><TextViewandroid:id="@+id/textName"android:layout_width="100dp"android:layout_height="wrap_content"android:text="@string/username"android:textSize="23sp"android:layout_gravity="center"android:layout_marginStart="16dp" /><EditTextandroid:id="@+id/userName"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:hint="@string/enter_username"android:textSize="23sp"android:layout_gravity="center_vertical"android:layout_marginEnd="16dp" /></LinearLayout><!-- Second LinearLayout (for password input) --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="80dp"android:orientation="horizontal"android:gravity="bottom"><TextViewandroid:id="@+id/textPwd"android:layout_width="100dp"android:layout_height="wrap_content"android:text="@string/password"android:textSize="23sp"android:gravity="center"android:layout_marginStart="16dp" /><EditTextandroid:id="@+id/passWord"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:hint="请输入密码"android:textSize="23sp"android:gravity="center_vertical"android:layout_marginEnd="16dp"android:inputType="textPassword" /></LinearLayout><!-- Third LinearLayout (for remember me checkbox) --><LinearLayoutandroid:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><CheckBoxandroid:id="@+id/rememberMe"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="17dp"android:layout_marginStart="25dp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="22sp"android:layout_marginTop="20dp"android:text="@string/remember_me" /></LinearLayout><!-- Fourth LinearLayout (for login and register buttons) --><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="16dp"android:layout_gravity="center"><Buttonandroid:id="@+id/login"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/login"android:textSize="24sp"android:layout_margin="10dp" /><Buttonandroid:id="@+id/register"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/register"android:textSize="24sp"android:layout_margin="10dp" /></LinearLayout></LinearLayout>

strings.xml

<resources><string name="app_name">T8</string><string name="username">用户名:</string><string name="enter_username">请输入用户名</string><string name="password">密码:</string><string name="enter_password">请输入密码</string><string name="login">登录</string><string name="register">注册</string><string name="remember_me">记住我</string>
</resources>

登录后,怎样查看SharedPreferences存储的remember与文件存储的accounts,找到androidstudio的工具栏,点击 View -> Tool Windows -> Device File Explorer 打开,然后找到/data/data/<your.app.package.name>/shared_prefs/与/data/data/<your.app.package.name>/files/,双击即可。

remember.xml文件 

accounts文件

图书馆界面

本题主要为SQLite数据库进行数据存储,也是特别重要的一种存储方式,SQLite数据库在很多设备的应用还是比较多的。

实现一个“网上图书馆”。

需要保存的信息:书本信息和书本所属类别。每本书有其所属的类别。

create table Book (
id integer primary key autoincrement,
author text,
price real,
pages integer,
name text

category_id integer

)

create table Category (
id integer primary key autoincrement
category_name text,
category_code integer)

使用SqlLite数据库创建数据库library,并创建Book和Category这两个表。往Category里插入2条默认书类别数据(如:经济类, 1)。

界面设计:

创建一个MainActivity,对应的布局为activity_main。

添加一个RecyclerView,用于显示所有的图书条目信息,每个条目显示书的名字、作者、类别和价格。

添加一个“添加书目”的按钮,用于跳转进入AddBookActivity。

④ 在AddBookActivity中,可以添加一本书的信息到数据库中。提示:AddBookActivity需要的组件包括:EditText、TextView、Button或Spinner。

提醒:可以在RecyclerView的适配器里面进行数据库的查询操作。

 MainActivity

package com.example.t8import android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerViewclass MainActivity : AppCompatActivity() {private lateinit var recyclerView: RecyclerViewprivate lateinit var bookListAdapter: BookListAdapter // 书籍列表的适配器private lateinit var emptyView: TextView // 空视图override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 设置 RecyclerView 的布局管理器和适配器recyclerView = findViewById(R.id.recyclerView)recyclerView.layoutManager = LinearLayoutManager(this)bookListAdapter = BookListAdapter(getAllBooks())recyclerView.adapter = bookListAdapteremptyView = findViewById(R.id.emptyTextView)// 设置添加书籍按钮的点击事件val addBookButton: Button = findViewById(R.id.addBookButton)addBookButton.setOnClickListener {val intent = Intent(this, AddBookActivity::class.java)startActivityForResult(intent, ADD_BOOK_REQUEST_CODE)}updateBookList()}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == ADD_BOOK_REQUEST_CODE && resultCode == RESULT_OK) {// 如果是从添加书籍界面返回且成功添加了书籍,则更新书籍列表updateBookList()}}private fun updateBookList() {val books = getAllBooks()bookListAdapter.updateBooks(books)checkViewVisibility()}@SuppressLint("Range")private fun getAllBooks(): List<Book> {val databaseHelper = DatabaseHelper(this, "library.db", 3)val db = databaseHelper.readableDatabaseval query = "SELECT * FROM Book"val cursor = db.rawQuery(query, null)val books = mutableListOf<Book>()while (cursor.moveToNext()) {val id = cursor.getInt(cursor.getColumnIndex("id"))val name = cursor.getString(cursor.getColumnIndex("name"))val author = cursor.getString(cursor.getColumnIndex("author"))val price = cursor.getDouble(cursor.getColumnIndex("price"))val pages = cursor.getInt(cursor.getColumnIndex("pages"))val categoryId = cursor.getInt(cursor.getColumnIndex("category_id"))val book = Book(id, name, author, price, pages, categoryId)books.add(book)}cursor.close()return books}private fun checkViewVisibility() {val isEmpty = bookListAdapter.itemCount == 0emptyView.visibility = if (isEmpty) View.VISIBLE else View.GONE// 控制其他视图的显示/隐藏findViewById<TextView>(R.id.BookName).visibility = if (!isEmpty) View.VISIBLE else View.GONEfindViewById<TextView>(R.id.BookAuthor).visibility = if (!isEmpty) View.VISIBLE else View.GONEfindViewById<TextView>(R.id.BookPrice).visibility = if (!isEmpty) View.VISIBLE else View.GONEfindViewById<TextView>(R.id.BookCategory).visibility = if (!isEmpty) View.VISIBLE else View.GONE}companion object {private const val ADD_BOOK_REQUEST_CODE = 1}
}

activity_main

<?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"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"android:padding="16dp"tools:context=".MainActivity"><Buttonandroid:id="@+id/addBookButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="15dp"android:text="添加图书" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="16dp"><TextViewandroid:id="@+id/BookName"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:textColor="@android:color/black"android:textSize="26sp"android:textStyle="bold"android:text="书名" /><TextViewandroid:id="@+id/BookAuthor"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:textColor="@android:color/black"android:textSize="26sp"android:textStyle="bold"android:text="作者"android:visibility="gone" /><TextViewandroid:id="@+id/BookPrice"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:textColor="@android:color/black"android:textSize="26sp"android:textStyle="bold"android:text="价格"android:visibility="gone" /><TextViewandroid:id="@+id/BookCategory"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:textColor="@android:color/black"android:textSize="26sp"android:textStyle="bold"android:text="分类"android:visibility="gone" /></LinearLayout><TextViewandroid:id="@+id/emptyTextView"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:layout_marginTop="300dp"android:visibility="gone"android:text="暂无图书!"android:textSize="20sp" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>

 Book

package com.example.t8data class Book(val id: Int,val name: String,val author: String,val price: Double,val pages: Int,val categoryId: Int
)

BookListAdapter

package com.example.t8import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerViewclass BookListAdapter(private var bookList: List<Book>) :RecyclerView.Adapter<BookListAdapter.BookViewHolder>() {class BookViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {val nameTextView: TextView = itemView.findViewById(R.id.nameTextView)val authorTextView: TextView = itemView.findViewById(R.id.authorTextView)val priceTextView: TextView = itemView.findViewById(R.id.priceTextView)val categoryTextView: TextView = itemView.findViewById(R.id.categoryTextView)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.item_book, parent, false)return BookViewHolder(view)}override fun onBindViewHolder(holder: BookViewHolder, position: Int) {val book = bookList[position]holder.nameTextView.text = book.nameholder.authorTextView.text = book.authorholder.priceTextView.text = book.price.toString()holder.categoryTextView.text = book.categoryId.toString()}override fun getItemCount(): Int {return bookList.size}@SuppressLint("NotifyDataSetChanged")fun updateBooks(newBookList: List<Book>) {bookList = newBookListnotifyDataSetChanged()}
}

Category

package com.example.t8data class Category(val id: Int, val categoryName: String, val categoryCode: Int)

CategoryAdapter

class CategoryAdapter(context: Context, private val categories: List<Category>) :ArrayAdapter<Category>(context, android.R.layout.simple_spinner_item, categories) {override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {val view = super.getView(position, convertView, parent)val textView = view.findViewById<TextView>(android.R.id.text1)textView.text = categories[position].categoryName  // 设置下拉框中显示的文本为类别名称return view}override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {val view = super.getDropDownView(position, convertView, parent)val textView = view.findViewById<TextView>(android.R.id.text1)textView.text = categories[position].categoryName  // 设置下拉框中下拉选项的文本为类别名称return view}override fun getItem(position: Int): Category? {return categories[position] // 返回指定位置的Category对象}
}

 item_book.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="wrap_content"android:orientation="vertical"android:padding="16dp"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="16dp"><!-- 书名 --><TextViewandroid:id="@+id/nameTextView"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:textSize="18sp"android:textColor="@android:color/darker_gray" /><!-- 作者 --><TextViewandroid:id="@+id/authorTextView"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:textSize="18sp"android:textColor="@android:color/darker_gray" /><!-- 价格 --><TextViewandroid:id="@+id/priceTextView"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:textSize="18sp"android:textColor="@android:color/darker_gray" /><!-- 分类 --><TextViewandroid:id="@+id/categoryTextView"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:textSize="18sp"android:textColor="@android:color/darker_gray" /></LinearLayout>
</LinearLayout>

AddBookActivity

package com.example.t8import android.content.ContentValues
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.*class AddBookActivity : AppCompatActivity() {private lateinit var databaseHelper: DatabaseHelperprivate lateinit var spinnerCategory: Spinnerprivate lateinit var categories: List<Category> // 保存类别的列表override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_add_book)// 初始化数据库帮助类databaseHelper = DatabaseHelper(this, "library.db", 3)insertInitialCategories()// 初始化类别选择下拉框spinnerCategory = findViewById(R.id.spinnerCategory)// 获取所有类别并赋值给 categoriescategories = databaseHelper.getAllCategories()// 获取类别名称列表,并将其填充到下拉框中val categoryNames = categories.map { it.categoryName }val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, categoryNames)adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)spinnerCategory.adapter = adapter// 设置添加书籍按钮的点击事件val btnAddBook: Button = findViewById(R.id.btnAddBook)btnAddBook.setOnClickListener {addBook()}}private fun addBook() {val editTextName: EditText = findViewById(R.id.editTextName)val editTextAuthor: EditText = findViewById(R.id.editTextAuthor)val editTextPrice: EditText = findViewById(R.id.editTextPrice)val editTextPages: EditText = findViewById(R.id.editTextPages)spinnerCategory = findViewById(R.id.spinnerCategory)val name = editTextName.text.toString().trim()val author = editTextAuthor.text.toString().trim()val price = editTextPrice.text.toString().toDoubleOrNull() ?: 0.0val pages = editTextPages.text.toString().trim()val selectedCategoryName = spinnerCategory.selectedItem as String// 根据选择的类别名称,查找对应的 Category 对象val selectedCategory = categories.find { it.categoryName == selectedCategoryName }// 确保选中的类别不为 nullselectedCategory?.let {val db = databaseHelper.writableDatabaseval values = ContentValues().apply {put("name", name)put("author", author)put("pages", pages)put("price", price)put("category_id", it.id) // 使用 selectedCategory 的 id 字段}// 插入新的书籍记录val newRowId = db.insert("Book", null, values)if (newRowId != -1L) {// 插入成功后,可以添加提示信息或者返回上一界面等操作setResult(RESULT_OK)finish()} else {// 插入失败的处理Toast.makeText(this, "添加书籍失败", Toast.LENGTH_SHORT).show()}} ?: run {// 如果没有选择类别,提示用户Toast.makeText(this, "请选择类别", Toast.LENGTH_SHORT).show()}}// 插入初始类别数据的方法private fun insertInitialCategories() {// 检查数据库中是否已经有类别数据if (databaseHelper.getAllCategories().isEmpty()) {databaseHelper.insertCategory("经济类", 1)databaseHelper.insertCategory("武侠类", 2)databaseHelper.insertCategory("医药类", 3)// 可以根据需要添加更多类别}}
}

 DatabaseHelper

package com.example.t8import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toastclass DatabaseHelper(private val context: Context, name: String, version: Int) : SQLiteOpenHelper(context, name, null, version) {// 书本表private val createBook = """CREATE TABLE Book (id INTEGER PRIMARY KEY AUTOINCREMENT,author TEXT,price REAL,pages INTEGER,name TEXT,category_id INTEGER)"""// 类别表private val createCategory = """CREATE TABLE Category (id INTEGER PRIMARY KEY AUTOINCREMENT,category_name TEXT,category_code INTEGER)"""// 创建数据库时创建表格override fun onCreate(db: SQLiteDatabase) {db.execSQL(createBook)db.execSQL(createCategory)Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()}// 更新数据库时删除旧表并创建新表override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {db.execSQL("DROP TABLE IF EXISTS Book")db.execSQL("DROP TABLE IF EXISTS Category")onCreate(db)}// 插入类别fun insertCategory(categoryName: String, categoryCode: Int) {val db = this.writableDatabaseval contentValues = ContentValues().apply {put("category_name", categoryName)put("category_code", categoryCode)}db.insert("Category", null, contentValues)db.close()}// 获取所有类别@SuppressLint("Range")fun getAllCategories(): List<Category> {val categoryList = mutableListOf<Category>()val db = this.readableDatabaseval cursor = db.rawQuery("SELECT * FROM Category", null)cursor.use {if (it.moveToFirst()) {do {val id = it.getInt(it.getColumnIndex("id"))val categoryName = it.getString(it.getColumnIndex("category_name"))val categoryCode = it.getInt(it.getColumnIndex("category_code"))val category = Category(id, categoryName, categoryCode)categoryList.add(category)} while (it.moveToNext())}}cursor.close()return categoryList}
}

activity_add_book.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"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"android:padding="16dp"tools:context=".AddBookActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="80dp"android:gravity="bottom"android:orientation="horizontal"><!-- 书名 --><TextViewandroid:id="@+id/textViewName"android:layout_width="100dp"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginStart="16dp"android:text="书名"android:textSize="23sp" /><EditTextandroid:id="@+id/editTextName"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="start|center_vertical"android:layout_marginEnd="16dp"android:layout_weight="1"android:hint="请输入书名"android:inputType="text"android:textSize="23sp" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="80dp"android:gravity="bottom"android:orientation="horizontal"><!-- 作者 --><TextViewandroid:id="@+id/textViewAuthor"android:layout_width="100dp"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginStart="16dp"android:text="作者"android:textSize="23sp" /><EditTextandroid:id="@+id/editTextAuthor"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="start|center_vertical"android:layout_marginEnd="16dp"android:layout_weight="1"android:hint="请输入作者"android:inputType="text"android:textSize="23sp" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="80dp"android:gravity="bottom"android:orientation="horizontal"><!-- 价格 --><TextViewandroid:id="@+id/textViewPrice"android:layout_width="100dp"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginStart="16dp"android:text="价格"android:textSize="23sp" /><EditTextandroid:id="@+id/editTextPrice"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="start|center_vertical"android:layout_marginEnd="16dp"android:layout_weight="1"android:hint="请输入价格"android:inputType="numberDecimal"android:textSize="23sp" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="bottom"android:orientation="horizontal"><!-- 页数 --><TextViewandroid:id="@+id/textViewPages"android:layout_width="100dp"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginStart="16dp"android:text="页数"android:textSize="23sp" /><EditTextandroid:id="@+id/editTextPages"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="start|center_vertical"android:layout_marginEnd="16dp"android:layout_weight="1"android:hint="请输入页数"android:inputType="number"android:textSize="23sp" /></LinearLayout><LinearLayoutandroid:id="@+id/layoutCategory"android:layout_width="match_parent"android:layout_height="58dp"android:layout_marginTop="8dp"android:background="?android:attr/selectableItemBackground"android:gravity="center_vertical"android:orientation="horizontal"android:padding="8dp"><Spinnerandroid:id="@+id/spinnerCategory"android:layout_width="match_parent"android:layout_height="48dp"android:textSize="23sp" /></LinearLayout><!-- 添加书目按钮 --><Buttonandroid:id="@+id/btnAddBook"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:layout_marginTop="16dp"android:text="添加书目" /></LinearLayout>

 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.t8"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.T8"><activityandroid:name=".MainActivity"android:exported="false" /><activityandroid:name=".AddBookActivity"android:exported="false" /><activityandroid:name=".LoginActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

 

然后怎么查看sqlite数据库存储的数据,与上题类似,进到 /data/data/<your.app.package.name>/databases/找到db文件即可。

在这里你会遇到一个问题,怎么双击打不开了,叉掉后进入本地文件夹的存储去了。是的,直接点是打不开的,当你进到文件夹发现这个library文件时拷贝一份,放到你想放的文件夹路径,然后记住这个存放的文件夹路径。接着可以用一些数据库可视化界面管理工具进行连接查看,这里用的是navicat。打开navicat,新建sqlite数据库的连接。

在数据库文件选取你刚刚存放的路径即可,然后就可以查看了。

实验心得

那天,他等到了不下雨,却没有等到天晴。 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/480626.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

麒麟性能评估优化

cpu性能 Vmstat输出结果详解如下: r 列表示运行和等待cpu时间片的进程数,这个值如果长期大于系统CPU的个数,说 明CPU不足,需要增加CPU; b 列表示在等待资源的进程数,比如正在等待I/O、或者内存交换等; us 列显示了用户进程消耗的CPU 时间百分比。us的值比较高时,说明用…

Python基础学习-12匿名函数lambda和map、filter

目录 1、匿名函数&#xff1a; lambda 2、Lambda的参数类型 3、map、 filter 4、本节总结 1、匿名函数&#xff1a; lambda 1&#xff09;语法&#xff1a; lambda arg1, arg2, …, argN : expression using arg 2&#xff09; lambda是一个表达式&#xff0c;而不是一个语…

uniapp定义new plus.nativeObj.View实现APP端全局弹窗

为什么要用new plus.nativeObj.View在APP端实现弹窗&#xff1f;因为uni.showModal在APP端太难看了。 AppPopupView弹窗函数参数定义 参数一:弹窗信息(所有属性可不填&#xff0c;会有默认值) 1.title:"", //标题 2.content:"", //内容 3.confirmBoxCo…

Qt读写Usb设备的数据

Qt读写Usb设备的数据 问题:要读取usb设备进行通讯&#xff0c;qt好像没有对应的库支持。解决&#xff1a;libusbwindow下载 :Linux下载: QtUsb 开源的第三方库库里面的函数说明&#xff1a;window版本&#xff1a;Linux中也提供的直接下载测试代码&#xff1a;库下载&#xff1…

opengl 三角形

最后效果&#xff1a; OpenGL version: 4.1 Metal 不知道为啥必须使用VAO 才行。 #include <glad/glad.h> #include <GLFW/glfw3.h>#include <iostream> #include <vector>void framebuffer_size_callback(GLFWwindow *window, int width, int heigh…

【C语言篇】探索 C 语言结构体:从基础语法到数据组织的初体验

我的个人主页 我的专栏&#xff1a;C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 目录 什么是结构体结构体的定义与使用结构体内存布局嵌套结构体与指针结构体数组的操作结构体与函数结构体内存对齐机制位域与结构体的结合动态内存分…

mfc110u.dll是什么意思,mfc110u.dll丢失解决方法大全详解

mfc110u.dll是Microsoft Foundation Classes (MFC)库的一个特定版本&#xff08;版本11.0&#xff09;的Unicode动态链接库文件。MFC是Microsoft为C开发者设计的一个应用程序框架&#xff0c;主要用于简化Windows应用程序的开发工作。这个框架封装了很多Windows API函数&#x…

python代码示例(读取excel文件,自动播放音频)

目录 python 操作excel 表结构 安装第三方库 代码 自动播放音频 介绍 安装第三方库 代码 python 操作excel 表结构 求出100班同学的平均分 安装第三方库 因为这里的表结构是.xlsx文件,需要使用openpyxl库 如果是.xls格式文件,需要使用xlrd库 pip install openpyxl /…

NSSCTF web刷题

1 虽然找到了flag,但是我要怎么去改他的代码,让他直接输出flag呢? (好像是要得到他的json代码,这题不让看) 2 wllm应该就是他的密码,进入许可了 意思是服务器可以执行通过POST的请求方式传入参数为wllm的命令&#xff0c;那这就是典型的命令执行&#xff0c;当然&#xff0c…

springboot项目报错问题总结

springboot循环依赖问题处理 发现问题 Error starting ApplicationContext. To display the conditions report re-run your application with debug enabled. 2024-11-27 21:30:58.695 [f8cd6df4693e404aa607363bbe3dcf00] [main] ERROR o.s.boot.SpringApplication - - App…

简单线性DP

数字三角形--简单线性DP 题目链接&#xff1a;数字三角形 解题代码&#xff1a; import java.io.BufferedReader; import java.io.InputStreamReader;public class Main {static int N510;static int INF (int) -1e9;static String[] q;static int[][]fnew int[N][N];static …

强化学习导论 -章9 基于函数逼近的同轨策略预测

基于函数逼近的同轨策略预测 我们前面已经完成了基于表格的学习任务&#xff0c;基于表格的就是每个s是独立学习的&#xff0c;基本上不考虑泛化的能力&#xff0c;但是也对于每个任务状态学习的非常好。考虑到状态空间越来越大&#xff0c;我们必须考虑到函数逼近的情况。 1…

架构-微服务-服务治理

文章目录 前言一、服务治理介绍1. 什么是服务治理2. 常见的注册中心 二、nacos简介三、nacos实战入门1. 搭建nacos环境2. 将商品微服务注册到nacos3. 将订单微服务注册到nacos 四、实现服务调用的负载均衡1. 什么是负载均衡2. 自定义实现负载均衡3. 基于Ribbon实现负载均衡 五、…

Vue使用Mockjs插件实现模拟数据

官方文档&#xff1a;Mock.js 一.引言 在前端开发过程中&#xff0c;我们经常会遇到后端接口尚未完成&#xff0c;但前端需要进行页面构建和功能测试的情况。这时候&#xff0c;Mockjs就如同救星一般出现了。Mockjs 是一款能够模拟生成随机数据&#xff0c;拦截 Ajax 请求并返…

docker 创建Dockerfile

一、定义 1.用Dockerfile 创建镜像。 2.设置自启动方式二&#xff1a; 3.容器自启动 4.glm4 容器部署案例 二、实现 1.用Dockerfile 创建镜像。 创建空文件夹&#xff1a; mkdir /myfile cd /myfile pwd2.编写Dockerfile FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-deve…

基于深度学习的手势识别算法

基于深度学习的手势识别算法 概述算法原理核心逻辑效果演示使用方式参考文献 概述 本文基于论文 [Simple Baselines for Human Pose Estimation and Tracking[1]](ECCV 2018 Open Access Repository (thecvf.com)) 实现手部姿态估计。 手部姿态估计是从图像或视频帧集中找到手…

2024-11-25 二叉树的定义

一、基本概念 1.二叉树是n(n>0)个结点的有限集合: ① 或者为空二叉树&#xff0c;即n0。 ②或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一棵二叉树。 特点&#xff1a; ①每个结点至多只有两棵子树。 ②左右子树不能颠倒&am…

网络中出现二层环路会产生什么问题?

在企业局域网中&#xff0c;二层交换机通过简单高效的方式转发数据包&#xff0c;构建了通信的基石。然而&#xff0c;当网络中出现二层环路时&#xff0c;这一切可能迅速崩溃。从广播风暴到MAC地址漂移&#xff0c;再到网络延迟和瘫痪&#xff0c;二层环路问题带来的影响既深远…

安卓悬浮窗应用外无法穿透事件问题

现象&#xff1a; 应用内悬浮窗如何设置了 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE在自己应用内事件穿透正常&#xff0c;但到应用外就无法点击。 原因&#xff1a; 解决方法&#xff1a; layoutParams.alpha 0.8f …

8. SpringCloud Alibaba Nacos 注册中心 + 配置中心 Nacos “分类配置” 超超详细使用+配置解析

8. SpringCloud Alibaba Nacos 注册中心 配置中心 Nacos “分类配置” 超超详细使用配置解析 文章目录 8. SpringCloud Alibaba Nacos 注册中心 配置中心 Nacos “分类配置” 超超详细使用配置解析前言1. Spring Cloud Alibaba Nacos 概述1.2 安装 Spring Cloud Alibaba Naco…