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

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

整个界面布局我设计成了


这样。它主要用了一个线性布局和相对布局的组合,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()。具体代码如下:

@Override
	protected 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)这样的用法。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值