第7章 Android文件与本地数据库(SQLite)

Slides:



Advertisements
Similar presentations
第二章 简单的 SQL 语句. 本章要点  创建一个简单的表  SQL 语句介绍  最简单的查询语句 select  带有限制条件的查询  查询结果的排序显示.
Advertisements

Data type P64 ‘’ 转义字符 P67 P68 EXE,选出某个教师的学生中最新的一 个,要姓名, ID (,LIMIT ) EXISTS,NOT EXISTS P409 Q,EXISTS 和 in 的区别( 1000 ,查询结果)
Android ADT + Android SDK Eclipse Eclipse 編輯器 JAVA JAVA 開發工具.
数据存储.
第13章 繪圖與多媒體 13-1 顯示圖檔-行動相簿 13-2 音樂播放-音樂播放器 13-3 影片播放-視訊播放器
MVC Servlet与MVC设计模式.
数据库概述 简而言之,数据库(DataBase)就是一个存储数据的仓库。为了方便数据的存储和管理,它将数据按照特定的规律存储在磁盘上。通过数据库管理系统,可以有效的组织和管理存储在数据库中的数据。如今,已经存在了Oracle、SQL Server、MySQL等诸多优秀的数据库。在这一章中将讲解的内容包括。
實驗五:多媒體播放器選單介面.
Part 2 開發Android應用程式的流程
位置與地圖應用 此投影片為講解Android如何取得定位經緯度和使用Google Map地圖.
第二章 JAVA语言基础.
Google App Engine Google 應用服務引擎.
Android + Web Service 建國科技大學 資管系 饒瑞佶 2017/3 V1.
Android App 系統開發教學 Luna 陳雯琳 2014/12/18
建立Android新專案 建國科技大學 資管系 饒瑞佶 2010/10.
厦门大学数据库实验室 报告人:谢荣东 导师:林子雨 2014年8月30日
實驗四:單位轉換程式.
Chapter 13 Android 實戰演練.
Android + JUnit 單元測試 建國科技大學資管系 饒瑞佶 2012/8/19V4.
實驗十三:顯示目前經緯度位置.
Ch06 再談選單元件 物件導向系統實務.
課程名稱:資料庫系統 授課老師:李春雄 博士
使用Android控制Arduino 史先强
Chapter 12 T-SQL 語法與 ASP.NET.
第10章 App微信分享的实现 倚动实验室.
Android資料庫處理 Android智慧型手機程式設計 程式設計與應用班 建國科技大學 資管系 饒瑞佶 2012/4 V1
胡鹏 王慧锋 TEL: 数据库系统原理课程设计 实验环节1 胡鹏 王慧锋 TEL:
第一个Android程序 本讲大纲: 1、创建Android应用程序 2、Android项目结构说明 3、运行Android应用程序
第8章 Android内容提供者(ContentProvider)应用
Chapter 6 Advanced UI Design.
崑山科技大學資訊管理系 伺服網頁程式設計 系統開發細部流程 教師:游峰碩.
電子商務網站建制技術與實習(II) 助教:江宜政 吳昇洋.
2018/11/23 面向对象与多线程综合实验-数据库操作 教师:段鹏飞.
Chapter 7 Android應用元件 Android應用元件可以幫助我們獲得系統資源訊息(ActivityManager)、提供系統服務(Service)、搜尋系統服務(SearchManager)、監聽Intent訊息(Broadcast Receiver)以及資料共享(ContentProvider和ContentResolver)。
Android介面設計 Android智慧型手機程式設計 建國科技大學 資管系 饒瑞佶 2012/4 V1 2012/8 V2
Chapter 6 進階UI設計.
ContentProvider與資料共享
ANDROID PROGRAMMING2.
第3章 MySQL教學範本 主從式資料庫系統 - CH3.
CH7 佈局、按鈕與文字編輯元件.
Android + Service 建國科技大學 資管系 饒瑞佶.
MySQL数据库基础与实例教程 之 MySQL表结构的管理 郭水泉.
第10章 儲存偏好設定、檔案與資料庫 10-1 存取偏好設定 10-2 檔案存取 10-3 關聯式資料庫與SQLite
實驗十四:顯示與控制地圖.
王豐緒 銘傳大學資訊工程學系 問題:JAVA 物件檔輸出入.
建立Android新專案 Android智慧型手機程式設計 程式設計與應用班 建國科技大學 資管系 饒瑞佶 2012/4 V1
实验4:PL-SQL编程 1.实验目的 2.实验原理 PL/SQL是一种过程化语言,属于第三代语言,本实验在与熟悉使用PL/SQL编程.
第6章 建立Android使用介面 6-1 介面元件的基礎 6-2 Android的事件處理 6-3 按鈕元件 6-4 文字元件
實驗十一:待辦事項程式 (儲存在手機上).
主编:钟元生 赵圣鲁.
第11章 ListView延迟加载效果 授课老师:高成珍 QQ号: QQ群: 、
3.2 Mysql 命令行 1 查看数据库 SHOW DATABASES; 2 创建一个数据库test1 CREATE DATABASE test1; 3 选择你所创建的数据库 USE test1; (按回车键出现Database changed 时说明操作成功!) 4 查看现在的数据库中存在什么表.
Ch17 SharedPreference與檔案應用
Chapter 5 Basic UI Design.
Android視窗介面 建國科技大學 資管系 饒瑞佶 2010/10.
實驗九:延續實驗八, 製作一個完整音樂播放器
Location Based Services - LBS
SQL查询语句 蔡海洋.
進階UI元件:Spinner與接合器 靜宜大學資管系 楊子青
第二章 Java语法基础.
第二章 Java基本语法 讲师:复凡.
RecyclerView and CardView
Android Speech To Text(STT)
用Intent啟動程式中的其他Activity、運用WebView顯示網頁 靜宜大學資管系 楊子青
第2章 Java语言基础.
第4章 数据查询.
SQLite資料庫 靜宜大學資管系 楊子青.
第二章 Java基础语法 北京传智播客教育
進階UI元件:Spinner與接合器 靜宜大學資管系 楊子青
Summary
Presentation transcript:

第7章 Android文件与本地数据库(SQLite) QQ号:1281147324 QQ群:489059718(Android编程-清华版) 287966120(公共版) 网络资源:http://www.xs360.cn/book

主要内容

本章实例 文件操作 SQLite SharePreferences

7.1 文件存储 android是基于java语言的,在java中提供了一套完整的输入输出流操作体系,与文件相关的有FileInputStream,FileOutputStream等,通过这些类可以非常方便地访问磁盘上的文件内容。同样的android也支持这种方式来访问手机上的文件。Android手机中的文件有两个存储位置:内置存储空间和外部SD卡,针对不同位置的文件的存储的有所不同,下面分别对它们的操作。

7.1.1 手机内置存储空间文件的存取 Context提供了如下两个方法来打开本应用程序的数据文件夹里的文件IO流。 Diagram FileInputStream openFileInput(String name):打开应用程序的数据文件夹下的name文件对应输入流。 FileOutputStream openFileOutput(String name, int mode):打开应用程序的数据文件夹下的name文件对应输出流。 Context提供了如下两个方法来打开本应用程序的数据文件夹里的文件IO流。 第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。 第二参数用于指定操作模式,有四种模式。

7.1.1 手机内置存储空间文件的存取 Context.MODE_PRIVATE=0:默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中,可以使用Context.MODE_APPEND 。 Context.MODE_APPEND=32768:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件; Context.MODE_WORLD_READABLE =1:表示当前文件可以被其他应用读取; Context.MODE_WORLD_WRITEABLE =2:表示当前文件可以被其他应用写入。

7.1.1 手机内置存储空间文件的存取 如果希望文件被其他应用读和写,可以传入: Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE 应用程序的数据文件默认保存在/data/data/ <package name>/files目录下,文件的后缀名随意。

7.1.1 手机内置存储空间文件的存取 在手机内置存储空间中创建文件、写入内容的一般步骤: (1)调用openFileOutput()方法,传入文件的名称和操作的模式,该方法将会返回一个文件输出流; (2)调用write()方法,向这个文件输出流中写入内容; (3)调用close()方法,关闭文件输出流。 读取手机内置存储空间中文件的一般步骤: (1)调用openFileInput()方法,传入需要读取数据的文件名,该方法将会返回一个文件输入流对象; (2)调用read()方法读取文件的内容; (3)调用close()方法,关闭文件输入流。

7.1.1 手机内置存储空间文件的存取 1、当我们手机上不存在该文件时,我们先写后读与先读后写有区别吗?程序会不会出错? (具体做法:把手机上的content.txt文件删除,然后分别进行先写后读与先读后写操作,观察效果)。 2、不同操作模式的区别,当我们多次执行写入操作时, 文件里的内容是被覆盖还是不断的在文件末尾附加新数据? (具体做法:修改openFileOutput()方法的第二个参数)。

7.1.1 案例 单击write按钮,将第一个文本编辑框(writeText)的内容写入到指定的文件中(content.txt),单击read按钮, 从指定的文件中(content.txt)读取数据并显示在第二个文本框(readText)上。

7.1.1 案例 程序清单:FileTest\res\layout\activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText android:id="@+id/writeText" android:layout_height="wrap_content" android:minLines="2" android:hint="@string/hint"/> <Button android:id="@+id/write" android:layout_width="wrap_content" android:text="@string/write"/> →设置文本输入框最少为两行 →设置文本输入框的提示信息

7.1.1 案例 程序清单:FileTest\res\layout\activity_main.xml <EditText android:id="@+id/readText" android:layout_width="match_parent" android:layout_height="wrap_content" android:editable="false" android:hint="@string/readhint"/> <Button android:id="@+id/read" android:layout_width="wrap_content" android:text="@string/read"/> </LinearLayout>

7.1.1 案例 在MainActivity.java中分别为写入内容和读取内容按钮添加事件处理. 程序清单:FileTest\src\iet\jxufe\cn\android\MainActivity.java public class MainActivity extends Activity { private Button read, write; private EditText readText, writeText; private String fileName="content.txt"; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); read = (Button) findViewById(R.id.read); write = (Button) findViewById(R.id.write); readText = (EditText) findViewById(R.id.readText); writeText = (EditText) findViewById(R.id.writeText); read.setOnClickListener(new OnClickListener() { public void onClick(View v) { readText.setText(read()); } }); →设置保存的文件名 →获取读取内容按钮 →获取写入内容按钮 →添加事件处理 →将读取的内容显示在文本编辑框上

7.1.1 案例 write.setOnClickListener(new OnClickListener() { 程序清单:FileTest\src\iet\jxufe\cn\android\MainActivity.java write.setOnClickListener(new OnClickListener() { public void onClick(View v) { write(writeText.getText().toString()); } }); public void write(String content) { try { FileOutputStream fos = openFileOutput(fileName, Context.MODE_APPEND); →当第二个参数为Context.MODE_PRIVATE时,后写入的内容会覆盖原有内容。 PrintStream ps = new PrintStream(fos); ps.print(content); } catch (Exception e) { e.printStackTrace(); } } →将文本编辑框的内容写入文件 →该方法将字符串内容写入文件

7.1.1 案例 public String read() { 程序清单:FileTest/src/iet/jxufe/cn/android/MainActivity.java public String read() { StringBuilder sbBuilder = new StringBuilder(""); try { FileInputStream is = openFileInput(fileName); byte[] buffer = new byte[64]; int hasRead; while ((hasRead = is.read(buffer)) != -1) { sbBuilder.append(new String(buffer, 0, hasRead)); } } catch (Exception e) { e.printStackTrace(); return sbBuilder.toString(); } } →该方法用于读取文件信息,并以字符串返回 →获取文件输入流 →定义缓冲区的大小 →记录每次读取的字节数

7.1.2 SD卡文件存取 对于手机而言,内存非常宝贵,也是比较小的。内存空间会影响到手机的运行速度,通常不建议将数据保存到手机内存中,特别是一些比较大的资源如图片、音频、视频等。那么这些资源存放在哪里呢?存放在外存上,几乎所有的Android设备,都会配有外存设备,最常见的就是SD卡。可以把SD卡看作是移动硬盘或U盘。 读取SD卡上的文件和读取手机上的文件类似,都是通过文件操作流的方式读取的,Android中没有提供单独的SD卡文件操作类,直接使用Java中的文件操作即可,关键是如何确定文件的位置。因为SD卡的可移动性,因此,在访问之前,需要验证手机的SD卡的状态。

mksdcard 2048M D:\sdcard.img 要想在模拟器中使用SD卡,首先需要创建一张SD卡(当然不是真的SD卡,只是一个镜像文件)。创建SD卡可以在Eclipse创建模拟器时随同创建,也可以使用Android提供的命令在命令行进行创建。 打开命令行窗口进入android SDK安装路径的tools目录下,输入以下命令在D盘创建一张容量为2G的SD卡,文件后缀名可以随便取,建议使用.img,生成的文件为镜像文件。如果你在环境变量中添加了Android tools目录,则可直接输入相应的命令即可。 mksdcard 2048M D:\sdcard.img

7.1.2 SD卡文件存取 读、写SD卡上的文件步骤如下: 1、调用Environment的getExternalStorageState()方法判断手机上是否插入了SD卡,并且应用程序具有读写SD卡的权限。 Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。 2、调用Environment的getExternalStorageDirectory()方法来获取外部存储器,也就是SD卡的目录。 3、使用FileInputStream、FileOutputStream、FileReader、FileWriter读、写SD卡里的文件。

7.1.2 SD卡文件存取 注意: 文件读取之前,应确认手机上已插入SD卡,对于模拟器来说,可以通过mksdcard命令来创建虚拟存储卡; 为了读、写SD卡上的数据,必须在应用程序的清单文件(AndroidManifest.xml)中添加读、写SD卡的权限。 在SDCard中创建与删除文件权限 <uses-permission android:name=”android:permission.MOUNT_UNMOUNT_FILESYSTEMS”/> 往SDCard写入数据权限 <uses-permission android:name=”android:permission.WRITE_EXTERNAL_STORAGE”/>

7.1.2 案例 下面仍然以上面的程序为例,只是这次将数据写入到SD卡上的文件,程序界面布局一致,在此不再列出。关键代码区别在于,在读写之前需先判断手机上是否存在SD卡,然后运用java的输入输出流技术进行读写操作,关键代码如下: 程序清单:SDCardFileTest\src\iet\jxufe\cn\android\MainActivity.java public class MainActivity extends Activity { private Button read, write; private EditText readText, writeText; private String fileName = "content.txt"; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); read = (Button) findViewById(R.id.read); write = (Button) findViewById(R.id.write); readText = (EditText) findViewById(R.id.readText); writeText = (EditText) findViewById(R.id.writeText); read.setOnClickListener(new OnClickListener() { public void onClick(View v) { readText.setText(read()); } });

7.1.2 案例 程序清单:SDCardFileTest\src\iet\jxufe\cn\android\MainActivity.java write.setOnClickListener(new OnClickListener() { public void onClick(View v) { write(writeText.getText().toString()); } }); } public void write(String content) { try { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { File sdCardDir = Environment.getExternalStorageDirectory(); File destFile = new File(sdCardDir.getCanonicalPath() + File.separator + fileName); System.out.println("*********"+destFile.getAbsolutePath()); RandomAccessFile raf = new RandomAccessFile(destFile, "rw"); raf.seek(destFile.length()); raf.write(content.getBytes()); raf.close(); } } catch (Exception e) { e.printStackTrace(); } } →向文件中写入内容 →判断手机中SD卡状态 →获取SD卡目录 →根据路径和文件名创建文件 →把指针定位到文件末尾 →在文件末尾追加新的内容 →关闭文件

7.1.2 案例 程序清单:SDCardFileTest\src\iet\jxufe\cn\android\MainActivity.java public String read() { StringBuilder sbBuilder = new StringBuilder(""); try { if(Environment.getExternalStorageState(). equals(Environment.MEDIA_MOUNTED)){ File sdCard=Environment.getExternalStorageDirectory(); File destFile = new File(sdCard.getCanonicalPath() + File.separator + fileName); FileInputStream fis=new FileInputStream(destFile); byte[] buffer = new byte[64]; int hasRead; while ((hasRead = fis.read(buffer)) != -1) { sbBuilder.append(new String(buffer, 0, hasRead)); } return sbBuilder.toString(); } } catch (Exception e) { e.printStackTrace();} return null; } →从文件中读取内容 →判断手机中是否存在可用的SD卡 →判断手机中是否存在可用的SD卡 →创建文件输入流 →定义临时缓存区大小 →记录每次读取的字节大小 →不断循环直到文件末尾 →在字符串后追加内容

注意事项 Java的RandomAccessFile类支持随机访问文件内容,主要是通过seek()方法来设定文件指针的位置,每次读写内容时,都是从该指针处开始进行读取的,从而实现了随机访问该文件内容的功能。该类还有一个特点,就是既可以读也可以写,创建时需指定它的模式。详细用法可查看Java帮助文档。 程序中raf.seek(destFile.length())用于将文件的指针定位到文件的末尾,从而实现将新内容附加到文件的目的。如果没有这句代码,多次向文件中写入内容时,后写的内容会替换前面的内容。读取操作时,采用的是简单的文件输入输出流,每次都是读取整个文件内容。

7.1.2 SD卡文件存取 SD卡中文件保存在/mnt/sdcard目录下。

7.2 SharedPreferences 应用程序使用SharedPreferences接口可以快速而高效的以键值对的形式保存数据,非常类似于Bundle; 信息以XML文件的形式存储在Android设备上; Sharedpreferences经常用于存储应用程序的设置例如用户设置、主题以及其他应用程序属性。它也可以保存登录信息例如用户名、密码、自动登录标志以及记住用户标志等; Sharedpreferences里的数据可被该应用的所有组件所访问。

7.2.1 SharedPreferences存储位置和格式 SharedPreferences接口本身只提供了读取数据的功能并没有提供写入数据的功能,如果需要实现写入功能,则需通过SharedPreferences的内部接口Editor来实现,SharedPreferences调用edit()方法即可获取对应的Editor对象。 SharedPreferences本身是一个接口,不能直接实例化,只能通过Context提供的getSharedpreferences(String name, int mode)方法来获取SharedPreferences实例,第一个参数表示保存信息的文件名,不需要后缀;第二个参数表示Sharedpreferences的访问权限,和前面读取应用程序中的文件类似,包括只能被本应用程序读、写,能被其他应用程序读、能被其他应用程序写。

7.2.1 案例 该程序实现保存用户登录信息的功能,用户第一次登录时可设置是否记住密码和是否自动登录。如果用户勾选记住密码复选框,则下次登录时,会直接显示用户名和密码,用户只需单击登录即可。 第一次登录 记住密码后登录

7.2.1 案例 登录界面布局文件如下。程序清单:SaveLoginInfo\res\layout\activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TableLayout android:layout_height="wrap_content" > <TableRow> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:text="@string/name" android:textSize="18sp" /> <EditText android:id="@+id/name" android:layout_width="240dp" android:layout_height="wrap_content" /> </TableRow> →垂直线性布局 →表格布局 →用户名文本显示框 →文本文字大小 →输入用户的文本编辑框 →文本编辑框宽度为240dp

7.2.1 案例 程序清单:SaveLoginInfo\res\layout\activity_main.xml →密码文本显示框 <TableRow> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/psd" android:textSize="18sp" /> <EditText android:id="@+id/psd" android:inputType="textPassword" /> </TableRow> </TableLayout> <LinearLayout android:layout_width="match_parent" android:orientation="horizontal" > <CheckBox android:id="@+id/rememberPsd" android:text="@string/remember_psd" /> →密码文本显示框 →文本文字大小 →输入密码的文本编辑框 →设置为密码框 →水平线性布局 →是否记住密码复选框

7.2.1 案例 程序清单:SaveLoginInfo\res\layout\activity_main.xml <CheckBox android:id="@+id/autoLogin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/auto_login" /> <Button android:id="@+id/login" android:layout_width="match_parent" android:text="@string/login" /> </LinearLayout> →是否自动登录复选框

7.2.1 案例 下面为登录按钮添加事件处理,由于并不是每一次都会进入登录界面,因此需要在界面中进行判断,如果保存的登录信息中,自动登录为true,那么就直接显示欢迎界面,否则才会显示登录界面。在本程序中,是通过改变界面布局来改变显示内容的,整个应用仍然只有一个Activity。只是该Activity在不同布局间切换,而不是不同Activity间相互跳转。 程序清单:SaveLoginInfo\src\iet\jxufe\cn\android\MainActivity.java public class MainActivity extends Activity { private Button login;// 登陆按钮 private CheckBox rememberPsdBox, autoLoginBox;// 记住密码、自动登陆复选框 private EditText name, psd;// 用户名和密码 private TextView userInfo; SharedPreferences loginPreferences, accessPreferences;// 保存登陆信息和访问次数 SharedPreferences.Editor loginEditor, accessEditor;// 对应的编辑器 String userName; String userPsd; boolean isSavePsd,isAutoLogin; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

7.2.1 案例 程序清单:SaveLoginInfo\src\iet\jxufe\cn\android\MainActivity.java loginPreferences = getSharedPreferences("login", Context.MODE_PRIVATE); accessPreferences = getSharedPreferences("access", Context.MODE_WORLD_READABLE); int count=accessPreferences.getInt("count",1); Toast.makeText(MainActivity.this,"欢迎您,这是第"+count+"次访问!", Toast.LENGTH_LONG).show(); loginEditor = loginPreferences.edit(); accessEditor = accessPreferences.edit(); accessEditor.putInt("count",++count); accessEditor.commit(); userName = loginPreferences.getString("name", null); userPsd = loginPreferences.getString("psd", null); isSavePsd=loginPreferences.getBoolean("isSavePsd",false); isAutoLogin=loginPreferences.getBoolean("isAutoLogin", false); System.out.println("userName=" + userName + ",userPsd=" + userPsd); →其他应用程序可读 →获取访问次数,默认为1 →每次登录时显示访问次数信息 →获取写入登录信息的Editor对象 →获取写入访问信息的Editor对象 →写入访问次数信息,每次自动加1 →提交写入的数据 →获取保存的用户信息 →获取保存的密码信息 →是否保存密码 →是否自动登录

7.2.1 案例 程序清单:SaveLoginInfo\src\iet\jxufe\cn\android\MainActivity.java if (isAutoLogin) { this.setContentView(R.layout.activity_welcome); userInfo = (TextView) findViewById(R.id.userInfo); userInfo.setText("欢迎您:" + userName + ",登陆成功!"); } else{ loadActivity(); } →如果自动登录为true →显示欢迎界面 →如果自动登录为false 加载新的布局文件作为界面的方法,代码如下:

7.2.1 案例 程序清单:SaveLoginInfo\src\iet\jxufe\cn\android\MainActivity.java public void loadActivity() { this.setContentView(R.layout.activity_main); login = (Button) findViewById(R.id.login); rememberPsdBox = (CheckBox) findViewById(R.id.rememberPsd); autoLoginBox = (CheckBox) findViewById(R.id.autoLogin); name = (EditText) findViewById(R.id.name); psd = (EditText) findViewById(R.id.psd); if (isSavePsd) { psd.setText(userPsd); name.setText(userName); rememberPsdBox.setChecked(true); } login.setOnClickListener(new OnClickListener() { public void onClick(View v) { loginEditor.putString("name", name.getText().toString()); loginEditor.putString("psd", psd.getText().toString()); loginEditor.putBoolean("isSavePsd", rememberPsdBox.isChecked()); loginEditor.putBoolean("isAutoLogin", autoLoginBox.isChecked()); loginEditor.commit(); MainActivity.this.setContentView(R.layout.activity_welcome); userInfo = (TextView) findViewById(R.id.userInfo); userInfo.setText("欢迎您:" + name.getText().toString() + ",登陆成功!"); } }); } →设置界面为登录界面 →如果获取的保存密码为true →设置密码框的值为保存的值 →显示用户名为保存的用户名 →设置保存密码复选框为选中状态 →写入用户名 →写入密码 →提交写入的登录信息 →切换到欢迎页面

注意 <map> <string name="psd">123456</string> 在data/data文件夹下生成的目录结构如下: 文本 文本 login.xml文件的内容 <map> <string name="psd">123456</string> <boolean name="isSavePsd" value="true" /> <string name="name">zhangsan</string> <boolean name="isAutoLogin" value="false" /> </map> 文本

7.2.1 案例 到此,保存登录信息的功能就实现了,但是存在一个问题,勾选自动登录复选框后,每次都会直接跳转到欢迎页面,如果想用另一个账号登录怎么办?似乎不可能,为此,我们为应用程序添加了菜单选项,包含注销和退出两个菜单,单击菜单键,单击注销菜单键则会跳转到用户登录界面,要求输入相应的登录信息,单击退出菜单项,退出程序。菜单的资源文件如下: 程序清单:SaveLoginInfo/res/menu <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_settings" android:title="@string/menu_settings"/> <item android:id="@+id/exit" android:title="@string/exit"/> </menu> →注销菜单项 →退出菜单项

7.2.1 案例 程序清单:SaveLoginInfo\src\iet\jxufe\cn\android\MainActivity.java public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_settings: loginEditor.putBoolean("isAutoLogin", false); loginEditor.commit(); onCreate(null); break; case R.id.exit: this.finish(); default: break; } return true; } } →创建上下文菜单方法 →绑定菜单资源文件 →为菜单项添加事件处理 →注销菜单项事件处理 →提交写入的登录信息 →重新调用onCreate方法, 显示登录界面 →退出菜单项事件处理 →结束当前Activity

7.2.1 案例 提示: 提示: SharedPreferences内部使用xml文件保存数据,getSharedPreferences (name,mode)方法的第一个参数用于指定该文件的名称,名称不用带后缀, 后缀会由Android自动加上。并且该XML文件以map为根元素,map元素的每 个子元素代表一个key-value对,子元素名称为value对应的类型名, SharedPreferences只能保存几种简单类型的数据。

7.2.2 读写其它应用的SharedPreferences 主要步骤如下: 1、需要创建其他程序对应的Context。(Android系统是根据应用程序的包名来作为该程序的标识的) 2、调用其他应用程序的Context的getSharedPreferences (String name, int mode)即可获取相应的SharedPreferences对象; 3、如果需要向其他应用的SharedPreferences数据写入数据,调用SharedPreferences的edit()方法获取相应的Editor即可。 注意:前提是创建该SharedPreferences的应用程序指定了 相应的访问权限。

7.2.2 案例 程序清单ReadOtherSharedPreferences\src\iet\jxufe\MainActivity.java protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SharedPreferences accessPreferences,loginPreferences; Context appContext = null; try { appContext = createPackageContext ("iet.jxufe.cn.android", Context.CONTEXT_IGNORE_SECURITY); }catch(Exception e){ e.printStackTrace(); } accessPreferences = appContext.getSharedPreferences("access", Context.MODE_WORLD_READABLE); int count = accessPreferences.getInt("count", 0); loginPreferences = appContext.getSharedPreferences("login", String name = loginPreferences.getString("name", null); Toast.makeText(this,"你好,"+",SaveLoginInfo应用程序已经被使用了"+count+"次!", Toast.LENGTH_LONG).show(); } } →创建上下文

7.2.2 案例 运行该程序,结果如下图,用户名信息无法获得,使用默认的null,而访问次数值和access.xml文件中一致。并且随着SaveLoginInfo应用程序的运行而变化。查看控制台打印信息发现有一条错误信息,试图打开没有许可的login.xml文件。 提示: ReadOtherSharedPreferences与SaveLoginInfo应用程序的包名不能一样,否则它们属于同一个应用程序,可以共享数据信息,即可以访问login.xml文件的内容,也就达不到本实例的结果。

7.3 SQLite数据库 在Android平台上,嵌入了一个轻量级的关系型数据库-SQLite。SQLite并没有包含大型客户/服务器数据库(如Oracle、SQL Server)的所有特性,但它包含了操作本地数据的所有功能,简单易用、反应快。 SQLite内部只支持 NULL、INTEGER、REAL(浮点数)、TEXT(字符串文本)和BLOB(二进制对象)这五种数据类型,但实际上SQLite也接受varchar(n)、char(n)、decimal(p,s)等数据类型,只不过在运算或保存时会转成上面对应的数据类型。

7.3.1 SQLite数据库简介 SQLite最大的特点是可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以把字符串类型的值存入INTEGER类型字段中,或者在布尔型字段中存放数值类型等。 但有一种情况例外:定义为INTEGER PRIMARY KEY的字段 只能存储64位整数, 当向这种字段保存除整数以外的数据时,将会产生错误。 由于SQLite允许存入数据时忽略底层数据列实际的数据类型,因此SQLite在解析建表语句时,会忽略建表语句中跟在字段名后面的数据类型信息。create table person_tb (id integer primary key autoincrement, name varchar(20)),因此在编写建表语句时可以省略数据列后面的类型声明。

7.3.1 SQLite数据库简介 常见SQL标准语句 查询语句:select * from 表名 where 条件子句 group by 分组字句 having ... order by 排序子句 如:select * from person 分页SQL与mysql类似,下面SQL语句获取5条记录,跳过前面3条记录 select * from Account limit 5 offset 3 插入语句:insert into 表名(字段列表) values(值列表)。 如:insert into person(name, age) values(‘张三’,26) 更新语句:update 表名 set 字段名=值 where 条件子句。 如:update person set name=‘李四‘ where id=10 删除语句:delete from 表名 where 条件子句。 如:delete from person where id=10

7.3.2 SQLite数据库相关类 SQLiteOpenHelper是Android提供的管理数据的工具类,主要用于数据库的创建、打开和版本更新。一般用法是创建SQLiteOpenHelper类的子类,并扩展它的onCreate()和onUpgrade()方法(这两个方法是抽象的,必须扩展),选择性的扩展它的onOpen()方法。 SQLiteDatabase getReadableDatabase():以读写的方式打开SQLiteDatabase对象,内部调用getWritableDatabase()方法; SQLiteDatabase getWritableDatabase():以写的方式打开SQLiteDatabase对象,一旦打开成功,将会缓存该数据库对象; abstract void onCreate(SQLiteDatabase db):当数据库第一次被创建的时候调用该方法; abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):当数据库需要更新的时候调用该方法;

7.3.2 SQLite数据库相关类 调用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法获取SQLiteDatabase实例,如果数据库不存在,Android系统会自动生成一个数据库,然后调用onCreate()方法,在onCreate()方法里生成数据库表结构及添加应用需要的初始化数据。 onUpgrade()方法在数据库的版本发生变化时会被调用,一般在软件升级时才需改变版本号,数据库的版本是由开发人员控制的,假设数据库现在的版本是1,由于业务的变更,修改了数据库表结构,这时候就需要升级软件,升级软件时希望更新用户手机里的数据库表结构,为了实现这一目的,可以把数据库版本设置为2,并在onUpgrade()方法里实现表结构的更新。onUpgrade()方法可以根据原版本号和目标版本号进行判断,然后作出相应的表结构及数据更新。

7.3.2 SQLite数据库相关类 SQLiteDatabase是Android提供的代表数据库的类(底层就是一个数据库文件),该类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作。对SQLiteDatabase的学习,应该重点掌握execSQL()和rawQuery()方法。execSQL()方法可以执行insert、delete、update和create table之类有更改行为的SQL语句;而rawQuery()方法用于执行select语句。 execSQL(String sql,Object[] bindArgs):执行带占位符的SQL语句,如果sql语句中没有占位符,则第二个参数可传null; execSQL(String sql):执行SQL语句; rawQuery(String sql,String[] selectionArgs):执行带占位符的SQL查询。

SQLite数据库存储数据 SQLiteDatabase还专门提供了对应于添加、删除、更新、查询的操作方法: insert()、delete()、update()和query() 。 例如:Cursor query(String table, String[] columns, String selection,String[] selectionArgs, String groupBy,String having,String orderBy, String limit)方法各参数的含义 table:表名,如果是多表联合查询,可以用逗号将两个表名分开; columns:要查询的列名,可以是多列。 selection:查询条件子句,相当于select语句where关键字后面的部分,在条件子句允许使用占位符“?”。 selectionArgs:对应于selection语句中占位符的值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常。 groupBy:相当于select语句group by关键字后面的部分 having:相当于select语句having关键字后面的部分 orderBy:相当于select语句order by关键字后面的部分,如:personid desc, age asc; limit:指定偏移量和获取的记录数,相当于select语句limit关键字后面的部分。

7.3.2 SQLite数据库相关类 Cursor接口主要用于存放查询记录的接口,Cursor是结果集游标,用于对结果集进行随机访问,如果熟悉JDBC,可发现Cursor与JDBC中的ResultSet作用很相似,提供了如下方法来移动查询结果的记录指针。 move(int offset):将记录指针向上或向下移动指定的行数。offset为正数就向下移动,为负数就向上移动; moveToNext()方法可以将游标从当前记录移动到下一记录,如果已经移过了结果集的最后一条记录,返回结果为false,否则为true; moveToPrevious()方法用于将游标从当前记录移动到上一记录,如果已经移过了结果集的第一条记录,返回值为false,否则为true ; moveToFirst()方法用于将游标移动到结果集的第一条记录,如果结果集为空,返回值为false,否则为true ; moveToLast()方法用于将游标移动到结果集的最后一条记录,如果结果集为空,返回值为false,否则为true。

7.3.2 SQLite数据库相关类 使用SQLiteDatabase进行数据库操作的步骤如下: 调用SQLiteDatabase的方法来执行SQL语句; 操作SQL语句的执行结果; 关闭SQLiteDatabase,回收资源。

7.3.3 备忘录实例 该程序实现备忘录功能,用于记录生活中一些重要事情,并提供查询功能,可按条件进行模糊查询。可输入主题、相关内容以及选择时间 ,单击添加按钮时,会将相关数据写入数据库,单击查询按钮时,会根据主题、内容以及时间进行精确和模糊查询。

7.3.2备忘录实例 程序清单:Memento\res\layout\activity_main.xml →垂直线性布局 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TableLayout android:layout_height="wrap_content" > <TableRow> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="@string/subject" android:textSize="20sp" /> <EditText android:id="@+id/subject" android:layout_height="wrap_content" /> </TableRow> →垂直线性布局 →表格布局,3行2列 →左边距10dp →显示主题标签 →文本大小为20sp →输入主题的文本编辑框

7.3.2备忘录实例 程序清单:Memento\res\layout\activity_main.xml <TableRow> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="@string/body" android:textSize="20sp" /> <EditText android:id="@+id/body" android:layout_width="match_parent" android:minLines="4" /> </TableRow> <Button android:id="@+id/chooseDate" android:text="@string/chooseDate" />

7.3.2备忘录实例 程序清单:Memento\res\layout\activity_main.xml <EditText android:id="@+id/date" android:layout_width="200dp" android:layout_height="wrap_content" android:editable="false" /> </TableRow> </TableLayout> <LinearLayout android:layout_width="match_parent" android:gravity="center_horizontal" > <Button android:id="@+id/add" android:layout_width="wrap_content" android:text="@string/add" /> android:id="@+id/query" android:text="@string/query" /> </LinearLayout> 程序清单:Memento\res\layout\activity_main.xml

7.3.2备忘录实例 程序清单:Memento\res\layout\activity_main.xml →需要动态设置属性,所以添加ID <LinearLayout android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView style="@style/TextView" android:layout_width="40dp" android:text="@string/num" android:textColor="#000000" /> android:layout_width="50dp" android:text="@string/subject" android:layout_width="110dp" android:text="@string/body" →需要动态设置属性,所以添加ID →水平线性布局 →引用定制好的样式 →设置TextView的宽度 →将颜色设置为黑色

7.3.2备忘录实例 程序清单:Memento\res\layout\activity_main.xml <TextView style="@style/TextView" android:layout_width="100dp" android:text="@string/date" android:textColor="#000000" /> </LinearLayout> <ListView android:id="@+id/result" android:layout_width="wrap_content" android:layout_height="wrap_content" /> 程序清单:Memento\res\layout\activity_main.xml 此布局使用了样式,程序清单:Memento\res\values\styles.xml <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="AppTheme" parent="android:Theme.Light" /> <style name="TextView"> <item name="android:layout_height">wrap_content</item> <item name="android:gravity">center_horizontal</item> <item name="android:textSize">18sp</item> <item name="android:textColor">#0000ff</item> </style> </resources>

7.3.2备忘录实例 下面为添加和查询两个按钮添加事件处理,所有的数据都已具备,如何将数据写入数据库了,首先写一个自己的数据库工具类,该类继承于SQLiteOpenHelper,并重写它的onCreate()和onUpdate()方法,数据库创建时,会调用onCreate()方法,因此,我们将建表语句放在里面,详细代码如下: 程序清单:Memento\src\iet\jxufe\cn\android\MyDatabaseHelper.java public class MyDatabaseHelper extends SQLiteOpenHelper { final String CREATE_TABLE_SQL= "create table memento_tb(_id integer primary " + "key autoincrement,subject,body,date)"; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_SQL); public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { System.out.println("---------"+oldVersion+"------->"+newVersion); } } →创建memento表的SQL语句 →构造方法 →执行建表语句,创建 memento表

7.3.2备忘录实例 然后通过该工具类,获取数据库,并进行添加和查询操作,关键代码如下: 程序清单:Memento\src\iet\jxufe\cn\android\MainActivity.java private class MyOnClickListerner implements OnClickListener { public void onClick(View v) { mydbHelper = new MyDatabaseHelper(MainActivity.this, "memento.db", null, 1); SQLiteDatabase db = mydbHelper.getReadableDatabase(); String subStr = subject.getText().toString(); String bodyStr = body.getText().toString(); String dateStr = date.getText().toString(); switch (v.getId()) { case R.id.add: title.setVisibility(View.INVISIBLE); addMemento(db, subStr, bodyStr, dateStr); Toast.makeText(MainActivity.this, "添加备忘录成功!", 1000).show(); result.setAdapter(null); break; →创建数据库辅助类 →获取SQLite数据库 →获取主题编辑框的内容 →获取内容编辑框的内容 →获取时间编辑框的内容 →单击的是添加按钮 →设置表头不可见 →调用添加记录方法 →下拉列表内容为空

7.3.2备忘录实例 程序清单:Memento\src\iet\jxufe\cn\android\MainActivity.java →单击的是查询按钮 case R.id.query: title.setVisibility(View.VISIBLE); Cursor cursor = queryMemento(db, subStr, bodyStr, dateStr); SimpleCursorAdapter resultAdapter = new SimpleCursorAdapter( MainActivity.this, R.layout.result, cursor, new String[] { "_id", "subject", "body", "date" }, new int[] { R.id.memento_num, R.id.memento_subject, R.id.memento_body, R.id.memento_date }); result.setAdapter(resultAdapter); break; default: } →设置表头可见 →调用查询方法 →将查询结果显示在下拉列表 中,注意一 一对应 →单击的是查询按钮

7.3.2备忘录实例 向数据库中插入和查询记录的方法如下。 程序清单:Memento\src\iet\jxufe\cn\android\MainActivity.java public void addMemento(SQLiteDatabase db, String subject, String body, String date) { db.execSQL("insert into memento_tb values(null,?,?,?)", new String[] { subject, body, date }); this.subject.setText(""); this.body.setText(""); this.date.setText(""); } public Cursor queryMemento(SQLiteDatabase db, String subject, String body, Cursor cursor = db.rawQuery( "select * from memento_tb where subject like ? and body like ? and date like ?", new String[] { "%" + subject + "%", "%" + body + "%", "%" + date + "%" }); return cursor; } →执行插入操作 →添加数据后,将所有的文本编辑框的内容设为空 →执行查询操作,提供模糊查询功能

7.3.2备忘录实例 事件监听器写好后,为按钮注册单击事件处理器,代码如下: 程序清单:Memento\src\iet\jxufe\cn\android\MainActivity.java MyOnClickListerner myOnClickListerner = new MyOnClickListerner(); add.setOnClickListener(myOnClickListerner); query.setOnClickListener(myOnClickListerner); 操作完成后,在结束Activity前关闭数据库,代码如下: 程序清单:Memento\src\iet\jxufe\cn\android\MainActivity.java protected void onDestroy() { if(mydbHelper!=null){ mydbHelper.close(); }

7.3.2备忘录实例 当程序第一次调用getReadableDatabase()方法后,SQLiteOpenHelpery会缓存已创建的SQLiteDatabase实例,多次调用getReadableDatabase()方法得到的都是同一个SQLitedatabase实例,即正常情况下,SQLiteDatebase实例会维持数据库的打开状态,因此在结束前应关闭数据库,否则会占用内存。 在上面程序中,我们使用了SimpleCursorAdapter封装Cursor,从而在下拉列表中显示结果记录信息,这里需注意,SimpleCursorAdapter封装Cursor时要求底层数据表的主键名为-id,因为SimpleCursorAdapter只能识别列名为-id的主键,否则会出现java.lang.IllegalArgumentException:column'-id' does not exist错误。 程序运行后,打开DDMS视图,查看File Explorer 面板,发现在我们应用程序的包下,生成了一个databases文件夹,下面有一个memento.db文件,如图,该文件就是我们在程序中创建的数据库文件。

7.3.2备忘录实例 数据库文件位于\data\data\你的程序的包名\databases\中,可通过DDMS工具将该文件夹下的数据库导出来,然后下载具体的图形化界面进行查看。

7.3.2备忘录实例 Android为我们提供了一个相应的命令行工具查看SQLite数据库。进入到数据库所在目录(导出的文件所放文件夹): 注意:通过命令行查看数据库内容时,中文在命令行上会显示乱码 文本

7.3.2备忘录实例 1、数据库的创建过程是怎么样的?当不存在数据库时,直接查找记录会不会出错? 2、数据库的后缀名有要求吗? (Android系统在调用SQLiteOpenHelper的getReadableDatabase()方法时会判断系统中是否已存在数据库,如果不存在,系统会创建数据库文件,因此查找记录时不会出错,只不过查询结果为空。但若我们在创建数据库时,没有指定表结构,添加或查询时会出错) 2、数据库的后缀名有要求吗? (后缀名可任意)