任务实施 (1) 创建项目 图 3-1 欢迎界面 首先创建一个工程, 将其命名为 BoXueGu, 指定包名为 com.boxuegu (2) 导入界面图片将欢迎界面所需要的背景图片 launch_bg.png 导入到 drawable 文件夹中, 项目的 icon 图标 app_icon.png

Similar documents
多媒體應用 13 新增專案並完成版面配置 <ExMusic01> <activity_main.xml> ImageView ID imgplay ImageView ID imgstop ImageView ID imgfront TextView ID txtsong TextView ID t

res/layout 目录下的 main.xml 源码 : <?xml version="1.0" encoding="utf 8"?> <TabHost android:layout_height="fill_parent" xml

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new

预览图 : (2) 在 SelectCity.java 中增加控件, 用于绑定 select_city 文件的 ListView, TextView,EditTest 等控件 代码和注释如下 :

單步除錯 (1/10) 打開 Android Studio, 點選 Start a new Android Studio project 建立專案 Application name 輸入 BMI 點下 Next 2 P a g e

RecyclerView and CardVew

Android Fragment

0511-Android程式之GPS應用_專題週記4

Dynamic Layout in Android

Android Service

用手機直接傳值不透過網頁連接, 來當作搖控器控制家電 ( 電視遙控器 ) 按下按鍵發送同時會回傳值來確定是否有送出 問題 :1. 應該是使用了太多 thread 導致在傳值上有問題 2. 一次按很多次按鈕沒辦法即時反應

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

Android + NFC

Microsoft Word - 第3章 Activity.doc

一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页

Microsoft Word - 第3章.doc

ShareText

Microsoft Word - 01.DOC

建立Android新專案

Microsoft Word - 第3章.doc

幻灯片 1

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入

Lecture01_Android介绍

建模与图形思考

JavaIO.PDF

Android 开发教程

Database_001

小应用 Magic8

Microsoft PowerPoint - 05.Android 介面元件-RelativeLayout、Button、TextVeiw、EditText

Microsoft Word - 02.目錄.doc

《大话设计模式》第一章

<android.support.v7.widget.recyclerview android:layout_width="0dp" android:layout_height="0dp" android:layout_marginbottom

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit

untitled

Android Android Android SDK iv

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

03 开发入门.key

Chapter 10

PowerPoint 簡報

// HDevelopTemplateWPF projects located under %HALCONEXAMPLES%\c# using System; using HalconDotNet; public partial class HDevelopExport public HTuple

RxJava

<4D F736F F F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074>

Java

Microsoft Word - Android 7.x.doc

Microsoft Word - Android App开发从入门到精通.doc

TVS厂商接入流程API文档

untitled

题目

软件工程文档编制

Microsoft Word - 第5章.doc

在Spring中使用Kafka:Producer篇

使用MapReduce读取XML文件

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

EJB-Programming-4-cn.doc

Android + WebService

Microsoft Word - AEL CH05.doc

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

教育部補助資訊軟體人才培育先導計畫 100 年度課程發展專案計畫 實驗課程名稱 : IPC(Inter-Process Communication) 開發教師 : 張晉源老師 開發學生 : 林政揚 學校系所 : 樹德科技大學資訊工程學系

Guava学习之Resources

Microsoft PowerPoint - 07.Android 介面元件-TableLayout、Toast、AlertDialog

Microsoft Word - 第1章 Android基本概念.docx

输入 project name 选择完成

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 ->

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6


任務二 : 產生 20 個有炸彈的磚塊, 放在隨機的位置編輯 Block 類別的程式碼 import greenfoot.; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) Write a description of class

實作SQLiteOpenHelper類別

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7.

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

雲端 Cloud Computing 技術指南 運算 應用 平台與架構 10/04/15 11:55:46 INFO 10/04/15 11:55:53 INFO 10/04/15 11:55:56 INFO 10/04/15 11:56:05 INFO 10/04/15 11:56:07 INFO

Microsoft Word - CX1000-HMI_程序开发_PLC通讯

Microsoft Word - ch04三校.doc

Chapter 9: Objects and Classes

9, : Java 19., [4 ]. 3 Apla2Java Apla PAR,Apla2Java Apla Java.,Apla,,, 1. 1 Apla Apla A[J ] Get elem (set A) A J A B Intersection(set A,set B) A B A B

第 2 節 介面佈局檔 第 1 項 說明 第 2 項 原始碼 第 3 節 主程式開發 第 1 項 主程式 - 基本設定 第 2 項 主程式 - 產生亂數 第 3 項 主程式 - 數字靠邊 數字加總 第 4 節 加入手

Transcription:

第 3 章注册与登录模块 学习目标 掌握欢迎界面的开发, 能够独立制作欢迎界面 ; 掌握注册和登录模块的开发, 能够实现用户登录功能 博学谷项目的注册与登录模块主要用于创建用户账号, 管理用户信息 当用户注册成功后会跳转到登录界面, 用户登录后可以修改密码以及设置密保, 且只有设置过密保的账户才可以找回密码 本章将针对注册与登录模块进行详细讲解 3.1 欢迎界面 任务综述 在实际开发中, 开启应用程序时首先会呈现一个欢迎界面, 用于展示产品 Logo 或展示广告等, 接下来将创建博学谷项目的欢迎界面 博学谷项目的欢迎界面是由 RelativeLayout 布局和一个 TextView 控件组成, 其中 RelativeLayout 的背景图片用于展示产品 Logo,TextView 控件用于展示程序版本号 知识点 布局文件的创建与设计 ; RelativeLayout 布局 TextView 控件 ; Timer 与 TimerTask 技能点 实现 Android 项目的创建 ; 通过 Timer 实现界面延迟跳转 ; 通过 PackageManager 获取程序版本号 任务 3-1 欢迎界面 任务分析 博学谷项目的欢迎界面效果如图 3-1 所示

任务实施 (1) 创建项目 图 3-1 欢迎界面 首先创建一个工程, 将其命名为 BoXueGu, 指定包名为 com.boxuegu (2) 导入界面图片将欢迎界面所需要的背景图片 launch_bg.png 导入到 drawable 文件夹中, 项目的 icon 图标 app_icon.png 导入到 mipmap 文件夹中的 mipmap-hdpi 中 (mipmap 文件夹通常用于存放应用程序的启动图标, 它会根据不同手机分辨率对图标进行优化, 其他图片资源要放到 drawable 文件夹中 将图片拷贝到 mipmap 文件夹时会弹出一个对话框, 显示 mipmap-hdpi mipmap-mdpi mipmap-xhdpi mipmap-xxhdpi mipmap-xxxhdpi 五个文件夹, 按照分辨率不同选择合适的文件夹存放图片即可 ) (3) 创建欢迎界面在程序中选中 com.boxuegu 包, 在该包下创建一个 activity 包, 然后在 activity 包中创建一个 Empty Activity 类, 名为 SplashActivity 并将布局文件名指定为 activity_splash, 具体代码如 文件 3-1 所示 文件 3-1 activity_splash.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:background="@drawable/launch_bg" > 6 <TextView 7 android:id="@+id/tv_version" 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:layout_centerinparent="true" 11 android:textcolor="@android:color/white" 12 android:textsize="14sp"/> 13 </RelativeLayout> 从上述代码中可以看出, 通过 RelativeLayout 布局的 android:background 属性, 将布局背景设置成欢迎图片 在布局中放置一个 TextView 用于显示版本号信息, 通过 android:layout_centerinparent="true" 属性将 TextView 置于父控件的中心位置

(4) 修改清单文件每个应用程序都会有属于自己的 icon 图标, 同样博学谷项目也会使用自己的 icon 图标, 因此需要在 AndroidManifest.xml 的 <application> 标签中修改 icon 属性, 引入博学谷图标, 具体代码如下 : android:icon="@mipmap/app_icon" 项目创建后所有界面需要使用蓝色标题栏, 因此需要在 <application> 标签中修改 theme 属性, 去掉标题栏, 具体代码如下 : android:theme="@style/theme.appcompat.noactionbar" 博学谷项目启动时, 首先进入的是欢迎界面 SplashActivity 而不是系统默认的 MainActivity, 因此需要将欢迎界面指定为程序默认启动界面 在配置文件中将 MainActivity 的 <intent-filter> 标签以及标签中的所有内容剪切到 SplashActivity 所在的 <activity> 标签中, 具体代码如下 : <activity android:name=".activity.splashactivity" > <intent-filter> <action android:name="android.intent.action.main" /> <category android:name="android.intent.category.launcher" /> </intent-filter> </activity> 任务 3-2 欢迎界面逻辑代码 任务分析 欢迎界面主要展示产品 Logo 和版本信息, 通常会在该界面停留一段之后自动跳转到其他界面, 因此需要在逻辑代码中设置欢迎界面暂停几秒 (3 秒 ) 后再跳转, 并获取程序的版本号 任务实施 (1) 获取版本号在 SplashActivity 中创建 init() 方法, 在该方法中获取 TextView 控件并通过 PackageManager( 包管理器 ) 获取程序版本号 ( 版本号是 build.gradle 文件中的 versionname 的值 ) 显示在 TextView 控件上 (2) 让界面延迟跳转在 init() 方法中使用 Timer 以及 TimerTask 类设置欢迎界面延迟 3 秒再跳转到主界面 (MainActivity 所对应的界面, 此界面目前为空白页面 ), 具体代码如 文件 3-2 所示 文件 3-2 SplashActivity.java 1 package com.boxuegu.activity; 2 import android.content.intent; 3 import android.content.pm.activityinfo; 4 import android.content.pm.packageinfo; 5 import android.content.pm.packagemanager; 6 import android.os.bundle; 7 import android.support.v7.app.appcompatactivity; 8 import android.widget.textview; 9 import com.boxuegu.mainactivity; 10 import com.boxuegu.r; 11 import java.util.timer; 12 import java.util.timertask;

13 public class SplashActivity extends AppCompatActivity { 14 private TextView tv_version; 15 @Override 16 protected void oncreate(bundle savedinstancestate) { 17 super.oncreate(savedinstancestate); 18 setcontentview(r.layout.activity_splash); 19 // 设置此界面为竖屏 20 setrequestedorientation(activityinfo.screen_orientation_portrait); 21 init(); 22 } 23 private void init(){ 24 tv_version=(textview) findviewbyid(r.id.tv_version); 25 try { 26 // 获取程序包信息 27 PackageInfo info= getpackagemanager().getpackageinfo(getpackagename(), 0); 28 tv_version.settext("v"+info.versionname); 29 }catch (PackageManager.NameNotFoundException e){ 30 e.printstacktrace(); 31 tv_version.settext("v"); 32 } 33 // 利用 Timer 让此界面延迟 3 秒后再跳转,timer 中有一个线程, 这个线程不断执行 task 34 Timer timer=new Timer(); 35 //timertask 实现 runnable 接口,TimerTask 类表示一个在指定时间内执行的 task 36 TimerTask task=new TimerTask() { 37 @Override 38 public void run() { 39 Intent intent=new Intent(SplashActivity.this,MainActivity.class); 40 startactivity(intent); 41 SplashActivity.this.finish(); 42 } 43 }; 44 timer.schedule(task, 3000); // 设置这个 task 在延迟三秒之后自动执行 45 } 46 } 第 27-28 行代码首先通过 PackageManager 的 getpackageinfo() 方法获取 PackageInfo 对象, 然后通 过该对象的 versionname 属性获取到程序的版本号, 最后通过 settext() 方法将获取到的版本号设 置到 TextView 控件上 第 34-44 行代码的作用是让程序在欢迎界面停留 3 秒后再跳转 在此段代码中主要用到两个类, 分别为 Timer 类和 TimerTask 类, 其中 Timer 类是 JDK(JavaSE Development Kit 是 Java 开发工 具包 ) 中提供的一个定时器工具, 使用时会在主线程之外开启一个单独的线程执行指定任务, 任 务可以执行一次或多次 TimerTask 类是一个实现了 Runnable 接口的抽象类, 同时代表一个可以 被 Timer 执行的任务, 因此跳转到主界面的任务代码写在 TimerTask 的 run() 方法中 Timer 的 schedule() 方法是任务调度方法, 在 3 秒之后调度 TimerTask 执行跳转操作, 实现延迟跳转功能

3.2 注册 任务综述 注册界面主要用于用户输入注册信息, 在注册界面中用户需要输入用户名 密码 再次输入密码 ( 确保密码输入无误 ), 当点击注册按钮时进行注册 由于博学谷项目使用的是本地数据, 因此注册成功后, 需要将用户名和密码保存在 SharedPreferences 中便于后续用户登录 为了保证账户的安全, 在保存密码时会采用 MD5 加密算法, 这种算法是不可逆的, 且具有一定的安全性 知识点 标题栏的创建 ; ImageView 控件 EditText 控件 Button 控件 ; SharedPreferences 的使用 ; setresult(result_ok, data) 方法的使用 ; MD5 加密算法 技能点 掌握注册界面的设计和逻辑构思 ; 掌握标题栏的创建以及常用控件的使用 ; 通过 SharedPreferences 实现数据的存取功能 ; 通过 setresult(result_ok, data) 方法实现界面间数据的回传 ; 通过 MD5 加密算法实现密码加密功能 ; 实现博学谷的注册功能 任务 3-3 标题栏 任务分析 在博学谷项目中, 大部分界面都有一个后退键和一个标题栏 为了便于代码重复利用, 可以将后退键 和标题栏抽取出来单独放在一个布局文件 (main_title_bar.xml) 中, 界面效果如图 3-2 所示 任务实施 (1) 创建标题栏界面 图 3-2 标题栏界面 在 res/layout 文件夹中, 创建一个布局文件 main_title_bar.xml 在该布局文件中, 放置 2 个 TextView 控件, 分别用于显示后退键 ( 后退键的样式采用背景选择器的方式 ) 和当前界面标题 ( 界面标题暂未设置, 需要在代码中动态设置 ), 并设置标题栏背景透明, 具体代码如 文件 3-3 所示 文件 3-3 main_title_bar.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/title_bar" 4 android:layout_width="match_parent" 5 android:layout_height="50dp" 6 android:background="@android:color/transparent" >

7 <TextView 8 android:id="@+id/tv_back" 9 android:layout_width="50dp" 10 android:layout_height="50dp" 11 android:layout_alignparentleft="true" 12 android:layout_centervertical="true" 13 android:background="@drawable/go_back_selector" /> 14 <TextView 15 android:id="@+id/tv_main_title" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:textcolor="@android:color/white" 19 android:textsize="20sp" 20 android:layout_centerinparent="true" /> 21 </RelativeLayout> (2) 创建背景选择器标题栏界面中的返回键在按下与弹起时, 返回键会有明显的区别, 这种效果可以通过背景选择器进行实现 首先将图片 iv_back_selected.png iv_back.png 导入到 drawable 文件夹中, 然后选中 drawable 文件夹, 右键选择 New Drawable resource file 选项, 创建一个背景选择器 go_back_selector.xml, 根据按钮按下和弹起的状态来切换它的背景图片, 给用户一个动态效果 当按钮按下时显示灰色图片 (iv_back_selected.png), 当按钮弹起时显示白色图片 (iv_back.png), 具体代码如 文件 3-4 所示 文件 3-4 go_back_selector.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <selector xmlns:android="http://schemas.android.com/apk/res/android"> 3 <item android:drawable="@drawable/iv_back_selected" android:state_pressed="true"/> 4 <item android:drawable="@drawable/iv_back"/> 5 </selector> 任务 3-4 注册界面 任务分析 注册界面用于输入用户的注册信息, 在注册界面中需要 3 个 EditText 用于输入用户名 密码 再次确 认密码, 当点击注册按钮后完成用户注册, 界面效果如图 3-3 所示

任务实施 (1) 创建注册界面 图 3-3 注册界面 在 com.boxuegu.activity 包中创建一个 Empty Activity 类, 名为 RegisterActivity 并将布局文件名指定为 activity_register 在该布局文件中, 通过 <include> 标签将 main_title_bar.xml( 标题栏 ) 引入 (2) 导入界面图片将注册界面所需图片 register_bg.png default_icon.png user_name_icon.png psw_icon.png register_user_name_bg.png register_psw_bg.png register_psw_again_bg.png 导入到 drawable 文件夹中 (3) 放置界面控件在布局文件中, 放置 1 个 ImageView 控件, 用于显示用户头像 ;3 个 EditText 控件, 用于输入用户名 密码 再次输入密码 ;1 个 Button 控件作为注册按钮, 具体代码如 文件 3-5 所示 文件 3-5 activity_register.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:background="@drawable/register_bg" 6 android:orientation="vertical" > 7 <include layout="@layout/main_title_bar" /> 8 <ImageView 9 android:layout_width="70dp" 10 android:layout_height="70dp" 11 android:layout_gravity="center_horizontal" 12 android:layout_margintop="25dp" 13 android:src="@drawable/default_icon" /> 14 <EditText 15 android:id="@+id/et_user_name" 16 android:layout_width="fill_parent" 17 android:layout_height="48dp" 18 android:layout_gravity="center_horizontal"

19 android:layout_marginleft="35dp" 20 android:layout_marginright="35dp" 21 android:layout_margintop="35dp" 22 android:background="@drawable/register_user_name_bg" 23 android:drawableleft="@drawable/user_name_icon" 24 android:drawablepadding="10dp" 25 android:gravity="center_vertical" 26 android:hint=" 请输入用户名 " 27 android:paddingleft="8dp" 28 android:singleline="true" 29 android:textcolor="#000000" 30 android:textcolorhint="#a3a3a3" 31 android:textsize="14sp" /> 32 <EditText 33 android:id="@+id/et_psw" 34 android:layout_width="fill_parent" 35 android:layout_height="48dp" 36 android:layout_gravity="center_horizontal" 37 android:layout_marginleft="35dp" 38 android:layout_marginright="35dp" 39 android:background="@drawable/register_psw_bg" 40 android:drawableleft="@drawable/psw_icon" 41 android:drawablepadding="10dp" 42 android:hint=" 请输入密码 " 43 android:inputtype="textpassword" 44 android:paddingleft="8dp" 45 android:singleline="true" 46 android:textcolor="#000000" 47 android:textcolorhint="#a3a3a3" 48 android:textsize="14sp" /> 49 <EditText 50 android:id="@+id/et_psw_again" 51 android:layout_width="fill_parent" 52 android:layout_height="48dp" 53 android:layout_gravity="center_horizontal" 54 android:layout_marginleft="35dp" 55 android:layout_marginright="35dp" 56 android:background="@drawable/register_psw_again_bg" 57 android:drawableleft="@drawable/psw_icon" 58 android:drawablepadding="10dp" 59 android:hint=" 请再次输入密码 " 60 android:inputtype="textpassword" 61 android:paddingleft="8dp" 62 android:singleline="true"

63 android:textcolor="#000000" 64 android:textcolorhint="#a3a3a3" 65 android:textsize="14sp" /> 66 <Button 67 android:id="@+id/btn_register" 68 android:layout_width="fill_parent" 69 android:layout_height="40dp" 70 android:layout_gravity="center_horizontal" 71 android:layout_marginleft="35dp" 72 android:layout_marginright="35dp" 73 android:layout_margintop="15dp" 74 android:background="@drawable/register_selector" 75 android:text=" 注册 " 76 android:textcolor="@android:color/white" 77 android:textsize="18sp" /> 78 </LinearLayout> (4) 创建背景选择器将 register_icon_normal.png register_icon_selected.png 图片导入到 drawable 文件夹中, 并在该文件中创建注册按钮的背景选择器 register_selector.xml 当按钮按下时显示灰色图片(register_icon_selected.png), 当按钮弹起时显示橙色图片 (register_icon_normal.png), 具体代码如 文件 3-6 所示 文件 3-6 register_selector.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <selector xmlns:android="http://schemas.android.com/apk/res/android"> 3 <item android:drawable="@drawable/register_icon_selected" 4 android:state_pressed="true"/> 5 <item android:drawable="@drawable/register_icon_normal"/> 6 </selector> 任务 3-5 MD5 加密算法 任务分析 MD5 的全称是 Message-Digest Algorithm 5( 信息 -- 摘要算法 ),MD5 算法简单来说就是把任意长度的字符串变换成固定长度 ( 通常是 128 位 ) 的 16 进制字符串 在存储密码过程中, 直接存储明文密码是很危险的, 因此在存储密码前需要使用 MD5 算法加密, 这样不仅提高了用户信息的安全性, 同时也增加了密码破解的难度 任务实施 (1) 创建 MD5Utils 类选中 com.boxuegu 包, 在该包下创建 utils 包, 在 utils 包中创建一个 Java 类, 名为 MD5Utils (2) 进行 MD5 加密在 MD5Utils 类中, 创建一个 md5() 方法对密码进行加密 首先通过 MessageDigest 的 getinstance() 方法获取数据加密对象 digest, 然后通过该对象的 digest() 方法对密码进行加密, 具体代码如 文件 3-7 所示 文件 3-7 MD5Utils.java

1 package com.boxuegu.utils; 2 import java.security.messagedigest; 3 import java.security.nosuchalgorithmexception; 4 public class MD5Utils { 5 /** 6 * md5 加密的算法 7 */ 8 public static String md5(string text) { 9 MessageDigest digest = null; 10 try { 11 digest = MessageDigest.getInstance("md5"); 12 byte[] result = digest.digest(text.getbytes()); 13 StringBuilder sb = new StringBuilder(); 14 for (byte b : result) { 15 int number = b & 0xff; 16 String hex = Integer.toHexString(number); 17 if (hex.length() == 1) { 18 sb.append("0" + hex); 19 } else { 20 sb.append(hex); 21 } 22 } 23 return sb.tostring(); 24 } catch (NoSuchAlgorithmException e) { 25 e.printstacktrace(); 26 return ""; 27 } 28 } 29 } 任务 3-6 注册界面逻辑代码 任务分析 在注册界面点击注册按钮后, 需要获取用户名, 用户密码和再次确认密码, 当两次密码相同时, 将用户名和密码 ( 经过 MD5 加密 ) 保存到 SharedPreferences 中 同时当注册成功之后需要将用户名传递到登录界面 (LoginActivity 目前还未创建 ) 中 任务实施 (1) 获取界面控件在 RegisterActivity 中创建界面控件的初始化方法 init(), 用于获取注册界面所要用到的控件以及实现控件的点击事件 (2) 保存注册信息到 SharedPreferences 中

接下来在 RegisterActivity 中创建一个 saveregisterinfo() 方法, 将注册成功的用户名和密码 ( 经过 MD5 加密 ) 保存到 SharedPreferences 中, 具体代码如 文件 3-8 所示 文件 3-8 RegisterActivity.java 1 package com.boxuegu.activity; 2 import android.content.intent; 3 import android.content.sharedpreferences; 4 import android.content.pm.activityinfo; 5 import android.graphics.color; 6 import android.os.bundle; 7 import android.support.v7.app.appcompatactivity; 8 import android.text.textutils; 9 import android.view.view; 10 import android.widget.button; 11 import android.widget.edittext; 12 import android.widget.relativelayout; 13 import android.widget.textview; 14 import android.widget.toast; 15 import com.boxuegu.r; 16 import com.boxuegu.utils.md5utils; 17 public class RegisterActivity extends AppCompatActivity { 18 private TextView tv_main_title; // 标题 19 private TextView tv_back; // 返回按钮 20 private Button btn_register; // 注册按钮 21 // 用户名 密码 再次输入的密码的控件 22 private EditText et_user_name,et_psw,et_psw_again; 23 // 用户名 密码 再次输入的密码的控件的获取值 24 private String username,psw,pswagain; 25 // 标题布局 26 private RelativeLayout rl_title_bar; 27 @Override 28 protected void oncreate(bundle savedinstancestate) { 29 super.oncreate(savedinstancestate); 30 // 设置页面布局 31 setcontentview(r.layout.activity_register); 32 // 设置此界面为竖屏 33 setrequestedorientation(activityinfo.screen_orientation_portrait); 34 init(); 35 } 36 private void init(){ 37 // 从 main_title_bar.xml 页面布局中获得对应的 UI 控件 38 tv_main_title=(textview) findviewbyid(r.id.tv_main_title); 39 tv_main_title.settext(" 注册 "); 40 tv_back=(textview) findviewbyid(r.id.tv_back); 41 rl_title_bar=(relativelayout) findviewbyid(r.id.title_bar);

42 rl_title_bar.setbackgroundcolor(color.transparent); 43 // 从 activity_register.xml 页面布局中获得对应的 UI 控件 44 btn_register=(button) findviewbyid(r.id.btn_register); 45 et_user_name=(edittext) findviewbyid(r.id.et_user_name); 46 et_psw=(edittext) findviewbyid(r.id.et_psw); 47 et_psw_again=(edittext) findviewbyid(r.id.et_psw_again); 48 tv_back.setonclicklistener(new View.OnClickListener() { 49 @Override 50 public void onclick(view v) { 51 RegisterActivity.this.finish(); 52 } 53 }); 54 btn_register.setonclicklistener(new View.OnClickListener() { 55 @Override 56 public void onclick(view v) { 57 // 获取输入在相应控件中的字符串 58 geteditstring(); 59 if(textutils.isempty(username)){ 60 Toast.makeText(RegisterActivity.this, " 请输入用户名 ", 61 Toast.LENGTH_SHORT).show(); 62 return; 63 }else if(textutils.isempty(psw)){ 64 Toast.makeText(RegisterActivity.this, " 请输入密码 ", 65 Toast.LENGTH_SHORT).show(); 66 return; 67 }else if(textutils.isempty(pswagain)){ 68 Toast.makeText(RegisterActivity.this, " 请再次输入密码 ", 69 Toast.LENGTH_SHORT).show(); 70 return; 71 }else if(!psw.equals(pswagain)){ 72 Toast.makeText(RegisterActivity.this, " 输入两次的密码不一样 ", 73 Toast.LENGTH_SHORT).show(); 74 return; 75 }else if(isexistusername(username)){ 76 Toast.makeText(RegisterActivity.this, " 此账户名已经存在 ", 77 Toast.LENGTH_SHORT).show(); 78 return; 79 }else{ 80 Toast.makeText(RegisterActivity.this, " 注册成功 ", 81 Toast.LENGTH_SHORT).show(); 82 // 把用户名和密码保存到 SharedPreferences 中 83 saveregisterinfo(username, psw); 84 // 注册成功后把用户名传递到 LoginActivity.java 中 85 Intent data =new Intent();

86 data.putextra("username", username); 87 setresult(result_ok, data); 88 RegisterActivity.this.finish(); 89 } 90 } 91 }); 92 } 93 /** 94 * 获取控件中的字符串 95 */ 96 private void geteditstring(){ 97 username=et_user_name.gettext().tostring().trim(); 98 psw=et_psw.gettext().tostring().trim(); 99 pswagain=et_psw_again.gettext().tostring().trim(); 100 } 101 /** 102 * 从 SharedPreferences 中读取输入的用户名, 判断 SharedPreferences 中是否有此用户名 103 */ 104 private boolean isexistusername(string username){ 105 boolean has_username=false; 106 SharedPreferences sp=getsharedpreferences("logininfo", MODE_PRIVATE); 107 String sppsw=sp.getstring(username, ""); 108 if(!textutils.isempty(sppsw)) { 109 has_username=true; 110 } 111 return has_username; 112 } 113 /** 114 * 保存用户名和密码到 SharedPreferences 中 115 */ 116 private void saveregisterinfo(string username,string psw){ 117 String md5psw=md5utils.md5(psw); // 把密码用 MD5 加密 118 //logininfo 表示文件名 119 SharedPreferences sp=getsharedpreferences("logininfo", MODE_PRIVATE); 120 SharedPreferences.Editor editor=sp.edit();// 获取编辑器 121 // 以用户名为 key, 密码为 value 保存到 SharedPreferences 中 122 editor.putstring(username, md5psw); 123 editor.commit();// 提交修改 124 } 125 } 第 54-91 行代码主要是处理点击注册按钮逻辑 当点击注册按钮时, 首先获取 3 个 EditText 控件 ( 用户名 密码 再次输入密码 ) 的输入值, 判断它们是否为空, 密码和再次输入的密码是否一 致, 用户名是否已经存在, 之后将用户名和密码 (MD5 加密之后的密码 ) 保存到 SharedPreferences 中

第 85-87 行代码是调用回传数据的方法 setresult(result_ok, data) 把注册成功的用户名传递到登录界面 第 104-112 行代码用于判断用户名是否已经存在, 通过输入的用户名查询 SharedPreferences 中是否已经存在该用户 第 116-124 行代码用于 MD5 加密, 通过调用 MD5Utils 的 md5() 方法对密码进行加密, 之后将用户名和密码保存到 SharedPreferences 中 3.3 登录 任务综述 登录界面主要是为用户提供一个输入登录信息的界面, 当点击登录按钮时需要在 SharedPreferences 中查询输入的用户名是否有对应的密码, 如果有则用此密码与当前输入的密码 ( 需 MD5 加密 ) 进行比对, 如果信息一致, 则登录成功, 并把登录成功的状态和用户名保存到 SharedPreferences 中, 便于后续判断登录状态和获取用户名 如果登录失败, 则有两种情况, 一种是输入的用户名和密码不一致 ; 另一种是此用户名不存在 知识点 标题栏的引用 ; EditText 控件 Button 控件 ; SharedPreferences 的使用 ; setresult(result_ok, data) 方法的使用 ; Intent 的使用 技能点 掌握登录界面的设计和逻辑构思 ; 通过 SharedPreferences 实现数据的存取功能 ; 通过 setresult(result_ok, data) 方法实现界面间数据的回传 ; 通过 Intent 实现 Activity 之间的跳转 ; 实现博学谷的登录功能 任务 3-7 登录界面 任务分析 登录界面主要是为用户提供一个登录的入口, 在登录界面中用户可以输入用户名和密码, 点击登录按钮 若用户还未注册, 可以点击 立即注册 进入注册界面 ; 若用户忘记密码, 则可以点击 找回密码 进入找回密码界面 ( 找回密码界面未创建 ), 界面效果如图 3-4 所示

任务实施 (1) 创建登录界面 图 3-4 登录界面 在 com.boxuegu.activity 包中创建一个 Activity 类, 名为 LoginActivity 并将布局文件名指定为 activity_login 在该布局文件中, 通过 <include> 标签将 main_title_bar.xml( 标题栏 ) 引入 (2) 导入界面图片将登录界面所需图片 login_bg.png login_user_name_bg.png login_psw_bg.png 导入到 drawable 文件夹中 (3) 放置界面控件在布局文件中, 放置 1 个 ImageView 控件, 用于显示用户头像 ;2 个 EditText 控件, 分别用于输入用户名和密码 ;1 个 Button 控件作为登录按钮 ( 和注册按钮用同一个背景选择器 );2 个 TextView 控件, 分别用于显示文字 立即注册 和 找回密码?, 具体代码如 文件 3-9 所示 文件 3-9 activity_login.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:background="@drawable/login_bg" 6 android:orientation="vertical" > 7 <include layout="@layout/main_title_bar" /> 8 <ImageView 9 android:id="@+id/iv_head" 10 android:layout_width="70dp" 11 android:layout_height="70dp" 12 android:layout_margintop="25dp" 13 android:layout_gravity="center_horizontal" 14 android:background="@drawable/default_icon" /> 15 <EditText 16 android:id="@+id/et_user_name" 17 android:layout_width="fill_parent"

18 android:layout_height="48dp" 19 android:layout_margintop="35dp" 20 android:layout_marginleft="35dp" 21 android:layout_marginright="35dp" 22 android:layout_gravity="center_horizontal" 23 android:background="@drawable/login_user_name_bg" 24 android:drawableleft="@drawable/user_name_icon" 25 android:drawablepadding="10dp" 26 android:paddingleft="8dp" 27 android:gravity="center_vertical" 28 android:hint=" 请输入用户名 " 29 android:singleline="true" 30 android:textcolor="#000000" 31 android:textcolorhint="#a3a3a3" 32 android:textsize="14sp" /> 33 <EditText 34 android:id="@+id/et_psw" 35 android:layout_width="fill_parent" 36 android:layout_height="48dp" 37 android:layout_gravity="center_horizontal" 38 android:layout_marginleft="35dp" 39 android:layout_marginright="35dp" 40 android:background="@drawable/login_psw_bg" 41 android:drawableleft="@drawable/psw_icon" 42 android:drawablepadding="10dp" 43 android:paddingleft="8dp" 44 android:hint=" 请输入密码 " 45 android:inputtype="textpassword" 46 android:singleline="true" 47 android:textcolor="#000000" 48 android:textcolorhint="#a3a3a3" 49 android:textsize="14sp" /> 50 <Button 51 android:id="@+id/btn_login" 52 android:layout_width="fill_parent" 53 android:layout_height="40dp" 54 android:layout_margintop="15dp" 55 android:layout_marginleft="35dp" 56 android:layout_marginright="35dp" 57 android:layout_gravity="center_horizontal" 58 android:background="@drawable/register_selector" 59 android:text=" 登录 " 60 android:textcolor="@android:color/white" 61 android:textsize="18sp" />

62 <LinearLayout 63 android:layout_width="fill_parent" 64 android:layout_height="fill_parent" 65 android:layout_margintop="8dp" 66 android:layout_marginleft="35dp" 67 android:layout_marginright="35dp" 68 android:gravity="center_horizontal" 69 android:orientation="horizontal" > 70 <TextView 71 android:id="@+id/tv_register" 72 android:layout_width="0dp" 73 android:layout_height="wrap_content" 74 android:layout_weight="1" 75 android:gravity="center_horizontal" 76 android:padding="8dp" 77 android:text=" 立即注册 " 78 android:textcolor="@android:color/white" 79 android:textsize="14sp" /> 80 <TextView 81 android:id="@+id/tv_find_psw" 82 android:layout_width="0dp" 83 android:layout_height="wrap_content" 84 android:layout_weight="1" 85 android:gravity="center_horizontal" 86 android:padding="8dp" 87 android:text=" 找回密码?" 88 android:textcolor="@android:color/white" 89 android:textsize="14sp" /> 90 </LinearLayout> 91 </LinearLayout> 任务 3-8 登录界面逻辑代码 任务分析 当点击登录按钮时, 需要先判断用户名和密码是否为空, 若为空则提示请输入用户名和密码 ; 若不为空则获取用户输入的用户名, 由于博学谷项目用的是本地数据, 因此根据用户名在 SharedPreferences 中查询是否有对应的密码, 如果有对应的密码并且与用户输入的密码 ( 需 MD5 加密 ) 比对一致, 则登录成功 任务实施 (1) 获取界面控件在 LoginActivity 中创建界面控件的初始化方法 init(), 用于获取登录界面所要用的控件并设置登录按钮 返回键 立即注册 找回密码的点击事件 (2) 获取回传数据

重写 onactivityresult() 方法, 通过 data.getstringextra() 方法来获取注册成功的一个用户名, 并将其显示在用户名控件上 (3) 保存登录状态到 SharedPreferences 中由于在后续创建 我 的界面时, 需要根据登录状态来设置界面的图标和用户名, 因此需要创建 saveloginstatus() 方法在登录成功时把登录状态和用户名保存到 SharedPreferences 中, 具体代码如 文件 3-10 所示 文件 3-10 LoginActivity.java 1 package com.boxuegu.activity; 2 import android.content.intent; 3 import android.content.sharedpreferences; 4 import android.content.pm.activityinfo; 5 import android.support.v7.app.appcompatactivity; 6 import android.os.bundle; 7 import android.text.textutils; 8 import android.view.view; 9 import android.widget.button; 10 import android.widget.edittext; 11 import android.widget.textview; 12 import android.widget.toast; 13 import com.boxuegu.r; 14 import com.boxuegu.utils.md5utils; 15 public class LoginActivity extends AppCompatActivity { 16 private TextView tv_main_title; 17 private TextView tv_back,tv_register,tv_find_psw; 18 private Button btn_login; 19 private String username,psw,sppsw; 20 private EditText et_user_name,et_psw; 21 @Override 22 protected void oncreate(bundle savedinstancestate) { 23 super.oncreate(savedinstancestate); 24 setcontentview(r.layout.activity_login); 25 // 设置此界面为竖屏 26 setrequestedorientation(activityinfo.screen_orientation_portrait); 27 init(); 28 } 29 /** 30 * 获取界面控件 31 */ 32 private void init(){ 33 tv_main_title=(textview) findviewbyid(r.id.tv_main_title); 34 tv_main_title.settext(" 登录 "); 35 tv_back=(textview) findviewbyid(r.id.tv_back); 36 tv_register=(textview) findviewbyid(r.id.tv_register); 37 tv_find_psw= (TextView) findviewbyid(r.id.tv_find_psw);

38 btn_login=(button) findviewbyid(r.id.btn_login); 39 et_user_name=(edittext) findviewbyid(r.id.et_user_name); 40 et_psw=(edittext) findviewbyid(r.id.et_psw); 41 // 返回键的点击事件 42 tv_back.setonclicklistener(new View.OnClickListener() { 43 @Override 44 public void onclick(view v) { 45 LoginActivity.this.finish(); 46 } 47 }); 48 // 立即注册控件的点击事件 49 tv_register.setonclicklistener(new View.OnClickListener() { 50 @Override 51 public void onclick(view v) { 52 Intent intent=new Intent(LoginActivity.this,RegisterActivity.class); 53 startactivityforresult(intent, 1); 54 } 55 }); 56 // 找回密码控件的点击事件 57 tv_find_psw.setonclicklistener(new View.OnClickListener() { 58 @Override 59 public void onclick(view v) { 60 // 跳转到找回密码界面 ( 此界面暂时未创建 ) 61 } 62 }); 63 // 登录按钮的点击事件 64 btn_login.setonclicklistener(new View.OnClickListener() { 65 @Override 66 public void onclick(view v) { 67 username=et_user_name.gettext().tostring().trim(); 68 psw=et_psw.gettext().tostring().trim(); 69 String md5psw=md5utils.md5(psw); 70 sppsw=readpsw(username); 71 if(textutils.isempty(username)){ 72 Toast.makeText(LoginActivity.this, " 请输入用户名 ", 73 Toast.LENGTH_SHORT).show(); 74 return; 75 }else if(textutils.isempty(psw)){ 76 Toast.makeText(LoginActivity.this, " 请输入密码 ", 77 Toast.LENGTH_SHORT).show(); 78 return; 79 }else if(md5psw.equals(sppsw)){ 80 Toast.makeText(LoginActivity.this, " 登录成功 ", 81 Toast.LENGTH_SHORT).show();

82 // 保存登录状态和登录的用户名 83 saveloginstatus(true,username); 84 // 把登录成功的状态传递到 MainActivity 中 85 Intent data=new Intent(); 86 data.putextra("islogin", true); 87 setresult(result_ok, data); 88 LoginActivity.this.finish(); 89 return; 90 }else if((!textutils.isempty(sppsw)&&!md5psw.equals(sppsw))){ 91 Toast.makeText(LoginActivity.this, " 输入的用户名和密码不一致 ", 92 Toast.LENGTH_SHORT).show(); 93 return; 94 }else{ 95 Toast.makeText(LoginActivity.this, " 此用户名不存在 ", 96 Toast.LENGTH_SHORT).show(); 97 } 98 } 99 }); 100 } 101 /** 102 * 从 SharedPreferences 中根据用户名读取密码 103 */ 104 private String readpsw(string username){ 105 SharedPreferences sp=getsharedpreferences("logininfo", MODE_PRIVATE); 106 return sp.getstring(username, ""); 107 } 108 /** 109 * 保存登录状态和登录用户名到 SharedPreferences 中 110 */ 111 private void saveloginstatus(boolean status,string username){ 112 //logininfo 表示文件名 113 SharedPreferences sp=getsharedpreferences("logininfo", MODE_PRIVATE); 114 SharedPreferences.Editor editor=sp.edit(); // 获取编辑器 115 editor.putboolean("islogin", status); // 存入 boolean 类型的登录状态 116 editor.putstring("loginusername", username);// 存入登录时的用户名 117 editor.commit(); // 提交修改 118 } 119 @Override 120 protected void onactivityresult(int requestcode, int resultcode, Intent data) { 121 super.onactivityresult(requestcode, resultcode, data); 122 if(data!=null){ 123 // 从注册界面传递过来的用户名 124 String username =data.getstringextra("username"); 125 if(!textutils.isempty(username)){

126 et_user_name.settext(username); 127 // 设置光标的位置 128 et_user_name.setselection(username.length()); 129 } 130 } 131 } 132 } 第 52-53 行代码主要是调用 startactivityforresult(intent, 1) 方法跳转到注册界面, 目的是从注册界面回传数据到登录界面 第一个参数 intent 是数据载体, 第二个参数 requestcode 是请求码, 一般是大于等于 0 的整数 第 64-99 行代码用于实现点击登录, 当点击登录按钮时, 获取用户输入的用户名和密码, 若用户名密码为空, 则提示用户输入用户名或密码 若输入的密码与 SharedPreferences 中保存的密码一致, 则保存用户的登录状态, 并将登录成功的状态发送到 MainActivity 第 111-118 行的作用是当用户登录成功后, 把登录状态和登录的用户名保存到 SharedPreferences 中 第 122-130 行代码主要是获取注册界面回传过来的用户名, 设置用户名到 et_user_name 控件上, 并调用 et_user_name 控件的 setselection() 方法来设置光标位置 3.4 本章小结 本章主要讲解了博学谷项目的欢迎界面 注册模块 登录模块功能 这三个功能模块是本项目最简单的部分, 因此放在开篇讲解, 读者先熟悉下项目的开发流程以及开发步骤, 方便后续学习 思考题 1. 请思考如何使用 MD5 加密算法对密码进行加密? 2. 请思考博学谷项目中如何实现用户登录的?