ANDROID基础学习笔记_3_登录和保存数据

来源:互联网 发布:常熟淘宝摄影培训 编辑:程序博客网 时间:2024/06/05 23:56

对于一款软件产品,首先呈现给用户的应该是登录界面,所以今天我来简单的做一个登录界面。

整个界面布局我设计成了


这样。它主要用了一个线性布局和相对布局的组合,LinearLayout属性android:orientation设置成vertical。里面放置两个文本控件,两个输入框控件,再嵌套一个相对布局标签RelativeLayout来布置记住密码的CheckBox和登录Button。或许用表格布局也比较容易。

在做密码输入框时用到了一个inputType属性,将它的值设置成textPassword,另外我还看到了很多其他属性值比如numberPassword,phone,datetime等等。

而对于记住密码复选框,用到了android:checked属性,属性值为true。以此来实现了打开界面默认选中记住密码。以下是.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"    tools:context=".MainActivity" >    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="请输入用户名" />    <EditText        android:id="@+id/UserName"        android:layout_width="fill_parent"        android:layout_height="wrap_content" />    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="请输入密码" />    <!-- 通过android:inputType来实现密码输入框 -->    <EditText        android:id="@+id/PassWord"        android:inputType="textPassword"        android:layout_width="fill_parent"        android:layout_height="wrap_content" />    <RelativeLayout        android:layout_width="fill_parent"        android:layout_height="wrap_content" >        <!-- 通过CheckBox标签的android:checked=true来实现记住密码 -->        <CheckBox            android:id="@+id/rememberPwd"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:checked="true"            android:text="记住密码" />        <Button            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignParentRight="true"            android:text="登录"            android:onClick="login" />    </RelativeLayout></LinearLayout>

在后台代码里,我们需要实现按钮的点击事件login,事件里面首先要判断用户名和密码输入框是否为空。然后判断用户名是否存在,不存在返回存在继续判断密码是否正确,密码正确在判断是否选中了保存密码,如果选中就执行保存方法,反之不执行。最后执行登录成功后的操作。这些过程无非是进行了若干个if判断,只要把逻辑关系捋顺了,应该没什么难度。

由于没有学习到在安卓上如何连接数据库或服务器,所以我的代码只是模拟的实现了验证用户名和密码的功能。但是在保存用户名和密码这个功能上,我又学习了如何在指定路径下生成文件

1. 首先,需要创建一个方法saveUserInfo用来保存用户名和密码,传递了三个参数,除了用户名和密码外还有一个context(上下文)。对于这个单词,我在学习JAVA的servlet的时候就接触过,也知道这么一个意为上下文的单词,但是从字面意思上我始终不能理解上下文到底有什么用。这一次有幸算是稍微了解了一点点吧,上下文就是一个可以获取周围环境内容的这么一个类。具体更深层次的讲,其实我也不是很清楚了。。。

2. 然后接着就是创建文件,在context里有一个方法getFilesDir()可以获取到当前应用程序包下的files文件夹(如果没有,就会被创建),我们就指定将文件建立在这个路径下。(另外还有一个方法可以获取cache文件夹,这里面是用来存放缓存文件的,如果清理应用缓存,cache文件夹下的文件会被清理)

3. 最后就是创建输出流,将用户名和密码写到文件里,关闭输出流。

相应的源代码也是比较简单:

public static boolean saveUserInfo(Context context, String unm, String pwd){//context.getFilesDir();//返回一个路径/data/data/包名/files///context.getCacheDir();//返回一个路径/data/data/包名/catch/File file = new File(context.getFilesDir(),"info.txt");try {FileOutputStream fos = new FileOutputStream(file);fos.write((unm+"####"+pwd).getBytes());fos.close();return true;} catch (Exception e) {e.printStackTrace();return false;}}
同时,我们可以把这个方法和另外一个(模拟)获取所有用户名密码的方法写到一个类里方便以后调用。

通过文件输入流获取文件内容的代码是:

public static Map<String, String> getUserInfo(Context context){File file = new File(context.getFilesDir(),"info.txt");try {FileInputStream fis = new FileInputStream(file);BufferedReader br = new BufferedReader(new InputStreamReader(fis));String str = br.readLine();String[] infos = str.split("####");Map<String, String> map = new HashMap<String, String>();map.put("userName", infos[0]);map.put("passWord", infos[1]);return map;} catch (Exception e) {e.printStackTrace();return null;}}
关于文件的读取FileInputStream类我上网查的它是针对文件以二进制流读取,而BufferedReader不是只针对文件的,读取形式也不是二进制。

保存数据到sd卡

和保存到手机一样,只不过是更换了一个路径。用到了另外一个类:Environment类,其中有一个静态方法可以获取外部存储空间路径:getExternalStorageDirectory(),返回File。在创建文件的时候将这个file做参数传递,命名文件后即可在sd卡上创建一个文件。

但是如果手机没有sd卡,在保存的时候想必肯定会报错。所以在创建文件之前,我们需要判断sd卡是否存在。同样还是用到了Environment这个类,静态方法getExternalStorageState()来获取sd卡的状态,其返回的参数多样且都在Environment类里设置成了字符串常量,我从网上拷来了它们的解释:

MEDIA_BAD_REMOVAL 在没有挂载前存储媒体已经被移除。
MEDIA_CHECKING 正在检查存储媒体。
MEDIA_MOUNTED 存储媒体已经挂载,并且挂载点可读/写。
MEDIA_MOUNTED_READ_ONLY 存储媒体已经挂载,挂载点只读。
MEDIA_NOFS 存储媒体是空白或是不支持的文件系统。
MEDIA_REMOVED 存储媒体被移除。
MEDIA_SHARED 存储媒体正在通过USB共享。
MEDIA_UNMOUNTABLE 存储媒体无法挂载。
MEDIA_UNMOUNTED 存储媒体没有挂载。

很显然我们用到的是MEDIA_MOUNTED这个,所以整个保存到sd卡的代码如下:

//获取外部存储目录File file = new File(Environment.getExternalStorageDirectory(),"info.txt");try {//获取外部存取器状态if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){FileOutputStream fos = new FileOutputStream(file);fos.write((unm+"####"+pwd).getBytes());fos.close();return true;}else{Toast.makeText(context, "sd卡不可用", Toast.LENGTH_SHORT);return false;}} catch (Exception e) {e.printStackTrace();return false;}
关于Environment类在网上找到的详解:传送门
获取手机内部/外部存储空间和可用空间

又用到了一个新类StatFs,我在网上找了半天发现没有合适的,好像所有的博客或资料都在介绍怎样用它来获取存储空间大小,由此可见,也许它的作用仅限于此了。

还是先通过Environment.getExternalStorageDirectory()获取外部存储路径。

然后创建一个StatFs类的实例,用获取的文件的路径字符串做参数:StatFs stat = new StatFs(path.getPath())。

接下来是用getBlockSize()获取单个block的大小。我始终找不到对于这个东西的解释,或许就是单纯的一个存储块吧。

相应的就可以获取总存储块数getBlockCount()以及可用存储块数getAvailableBlocks(),将这些块数和单个存储块大小相乘,即可获得最终的总容量和可用容量。

最后我们还需要做进一步的转化,因为相乘之后的结果是长整型的字节,转换成我们常见的k/m/g/t等。

而获取内部存储空间方法大致相同,只是在一开始获取路径的方法不同:Environment.getDataDirectory()。具体代码如下:

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView tv = (TextView) findViewById(R.id.tv);//获取外部存储路径File path = Environment.getExternalStorageDirectory();//StatFs获取的都是以block为单位的StatFs stat = new StatFs(path.getPath());//得到单个block的大小long bolckSize = stat.getBlockSize();//获取所有数据块数long totalBlocks = stat.getBlockCount();//获取可用数据块数long availableBlocks = stat.getAvailableBlocks();long totalSize = bolckSize*totalBlocks;long availSize = availableBlocks*bolckSize;String totalStr = Formatter.formatFileSize(this, totalSize);String availStr = Formatter.formatFileSize(this, availSize);tv.setText("totalSize:"+totalSize+"\navailSize:"+availSize+"\n总容量:"+totalStr+"\n可用容量"+availStr+"\n"+getRomSpaceInfo());}public String getRomSpaceInfo(){File path = Environment.getDataDirectory();StatFs stat = new StatFs(path.getPath());long bolckSize = stat.getBlockSize();long totalBlocks = stat.getBlockCount();long availableBlocks = stat.getAvailableBlocks();long totalSize = bolckSize*totalBlocks;long availSize = availableBlocks*bolckSize;String totalStr = Formatter.formatFileSize(this, totalSize);String availStr = Formatter.formatFileSize(this, availSize);return "手机存储总空间:"+totalStr+"\n手机存储可用空间:"+availStr;}
看似复杂,其实重要的代码不超过十行。

最近发现自己学东西有一种小题大做的感觉,一篇博文写下来,感觉有营养的代码真的是少之又少。经验不足的菜鸟啊,还得多看看别人的博文是怎么写的。

找到一篇比较好的介绍数据存储的博文,前往围观

紧接着,我又学了一种保存信息的方式:用SharedPreferences类来保存和获取信息

保存信息

通过上下文这个类里的getSharedPreferences()方法,我们可以创建出来一个.xml文件,它被创建在了当前环境下新建的sharePre文件夹下,返回一个SharedPreferences类型的实例sp。

然后调用sp的edit()方法得到编辑器,返回的是一个Editor类型实例editor。

然后调用editor的putString()方法,就可以像使用map一样,包用户名,密码等数据以.xml文件保存起来。

同样的,还可以调用putBoolean()putInt()putLong()等方法,存储相应类型的数据。

在最后注意这样一行代码:editor.commit();它的意义在于确保数据的同时提交,假如保存过程中报错,那么这条代码可以保证数据回滚,恢复之前的操作,类似于数据库的事务。具体代码如下:

SharedPreferences sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);//得到sp的编辑器Editor editor = sp.edit();editor.putString("userName", unm);editor.putString("passWord", pwd);//类似数据库的事务,保证数据同时提交editor.commit();
获取信息
非常简单的一条代码:sp.getString("userName", "");

之所以要传递两个参数,是为了保证如果在文件中没有找到键相对应的值,那么会返回一个默认值,类似于数据库里isnull(mycount,0)这样的用法。

好吧,我承认,这是我好几天学的东西组合到了一起,真的是有点乱啊。。。

0 0
原创粉丝点击