上一节对欢迎模块进行了综述(可参见 10. 视频模块 进行了解),接下来将从视频模块开始详细介绍:
- [“我”模块(一)之创建数据库]
- [“我”模块(一)之“我”]
- [“我”模块(一)之注册]
- [“我”模块(一)之登录]
- [“我”模块(一)之个人资料]
- [“我”模块(一)之个人资料修改]
知识点
- 掌握SQLite数据库的使用,能够使用数据库存储用户信息。
- 掌握“我”界面开发,能够展示用户基本信息以及该界面的功能。
- 掌握“登录”“注册”界面的开发,实现用户登录注册功能。
- 掌握“个人资料”以及“修改”界面的开发,实现用户信息的展示与修改功能。
注册
任务综述:
“注册”界面主要用于输入注册消息(用户名与密码),点击“注册”按钮即可进行注册。由于头条项目把用户信息全部存放在本地,因此在注册成功后,需要将用户名和密码保存在SharedPreferences中便于后续用户登录。为了保证账户的安全,在保存密码时会采用MD5加密算法,这种算法是不可逆的,且具有一定的安全性。
7. “注册”界面
任务分析:
“注册”界面主要用于输入用户的注册信息,实现用户注册功能,界面效果如图所示。

任务实施:
(1)创建“注册”界面。创建RegisterActivity以及activity_register。在该布局文件中,通过<include>标签将main_title_bar.xml(标题栏)引入。
(2)导入界面图片(4个)。
(3)放置界面控件。
一个ImageViewRoundOval控件,用于显示用户默认头像;
两个EditText控件,用于输入用户名和密码;
一个ImageView控件,用于密码的显示和隐藏;
一个Button控件,作为注册按钮。
activity_register.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/register_bg_color"
android:orientation="vertical">
<include layout="@layout/main_title_bar" />
<com.itheima.topline.view.ImageViewRoundOval
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="25dp"
android:layout_marginTop="35dp"
android:scaleType="fitXY"
android:src="@drawable/default_head" />
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
app:cardCornerRadius="5dp"
app:cardElevation="3dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/et_user_name"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:background="@drawable/register_edittext_top_radius"
android:drawableLeft="@drawable/user_name_icon"
android:drawablePadding="10dp"
android:gravity="center_vertical"
android:hint="请输入用户名"
android:paddingLeft="8dp"
android:singleLine="true"
android:textColor="#000000"
android:textColorHint="@color/register_hint_text_color"
android:textCursorDrawable="@null"
android:textSize="14sp" />
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@color/divider_line_color" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="@+id/et_psw"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:background="@drawable/register_edittext_bottom_radius"
android:drawableLeft="@drawable/psw_icon"
android:drawablePadding="10dp"
android:hint="请输入密码"
android:inputType="textPassword"
android:paddingLeft="8dp"
android:singleLine="true"
android:textColor="#000000"
android:textColorHint="@color/register_hint_text_color"
android:textCursorDrawable="@null"
android:textSize="14sp" />
<ImageView
android:id="@+id/iv_show_psw"
android:layout_width="15dp"
android:layout_height="48dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:src="@drawable/hide_psw_icon" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<Button
android:id="@+id/btn_register"
android:layout_width="fill_parent"
android:layout_height="35dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
android:layout_marginTop="15dp"
android:background="@drawable/register_btn_selector"
android:text="注 册"
android:textColor="@android:color/white"
android:textSize="18sp" />
</LinearLayout>
(4)自定义ImageViewRoundOval控件。“注册”界面中有一个圆形默认头像,该效果是通过自定义ImageViewRoundOval控件实现的。在view包中创建一个ImageViewRoundOval类并继承ImageView类。
ImageViewRoundOval.java
/**
* 实现圆形、圆角、椭圆等自定义图片View
*/
public class ImageViewRoundOval extends ImageView {
private Paint mPaint;
private int mWidth;
private int mRadius; //圆半径
private RectF mRect; //矩形凹行大小
private int mRoundRadius; //圆角大小
private BitmapShader mBitmapShader; //图形渲染
private Matrix mMatrix;
private int mType; //记录是圆形还是圆角矩形
public static final int TYPE_CIRCLE = 0; //圆形
public static final int TYPE_ROUND = 1; //圆角矩形
public static final int TYPE_OVAL = 2; //椭圆形
public static final int DEFAUT_ROUND_RADIUS = 10; //默认圆角大小
public ImageViewRoundOval(Context context) {
this(context, null);
}
public ImageViewRoundOval(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ImageViewRoundOval(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mMatrix = new Matrix();
mRoundRadius = DEFAUT_ROUND_RADIUS;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//如果是绘制圆形,则强制宽高大小一致
if (mType == TYPE_CIRCLE) {
mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
mRadius = mWidth / 2;
setMeasuredDimension(mWidth, mWidth);
}
}
@Override
protected void onDraw(Canvas canvas) {
if (null == getDrawable()) {
return;
}
setBitmapShader();
if (mType == TYPE_CIRCLE) {
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
} else if (mType == TYPE_ROUND) {
mPaint.setColor(Color.RED);
canvas.drawRoundRect(mRect, mRoundRadius, mRoundRadius, mPaint);
}else if(mType == TYPE_OVAL){
canvas.drawOval(mRect, mPaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRect = new RectF(0, 0, getWidth(), getHeight());
}
/**
* 设置BitmapShader
*/
private void setBitmapShader() {
Drawable drawable = getDrawable();
if (null == drawable) {
return;
}
Bitmap bitmap = drawableToBitmap(drawable);
//将bitmap作为着色器来创建一个BitmapShader
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
float scale = 1.0f;
if (mType == TYPE_CIRCLE) {
//拿到bitmap宽或高的小值
int bSize = Math.min(bitmap.getWidth(), bitmap.getHeight());
scale = mWidth * 1.0f / bSize;
} else if (mType == TYPE_ROUND || mType == TYPE_OVAL) {
//如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,
//一定要大于view的宽高,所以这里取大值;
scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(), getHeight() * 1.0f /
bitmap.getHeight());
}
//shader的变换矩阵,这里主要用于放大或者缩小
mMatrix.setScale(scale, scale);
//设置变换矩阵
mBitmapShader.setLocalMatrix(mMatrix);
mPaint.setShader(mBitmapShader);
}
/**
* drawable转bitmap
* @param drawable
*/
private Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
return bitmapDrawable.getBitmap();
}
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
}
public int getType() {
return mType;
}
/**
* 设置图片类型:圆形、圆角矩形、椭圆形
* @param mType
*/
public void setType(int mType) {
if(this.mType != mType){
this.mType = mType;
invalidate();
}
}
}
(5)修改colors.xml文件。在“注册”界面用到了“注册”界面的背景颜色、注册文本颜色以及“注册”按钮的背景颜色。
<color name="register_bg_color">#f6f6f6</color>
<color name="register_hint_text_color">#a3a3a3</color>
<color name="register_btn_color">#d6d7d7</color>
(6)设置圆角形状。由于“注册”界面输入用户名与密码的控件需要设置圆角形状,因此需要在register_edittext_top_radius.xml文件与register_edittext_bottom_radius.xml文件,分别用于设置控件的顶部与底部的圆角形状。
register_edittext_top_radius.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
</shape>
</item>
</layer-list>
register_edittext_bottom_radius.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners
android:bottomLeftRadius="4dp"
android:bottomRightRadius="4dp" />
</shape>
</item>
</layer-list>
(7)创建背景选择器。在res/drawable文件夹中创建“注册”按钮的背景选择器register_btn_selector.xml。当按钮按下时显示灰色背景,当按钮弹起时显示红色背景。
register_btn_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="@color/register_btn_color" />
</shape>
</item>
<item android:state_pressed="false">
<shape android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="@color/rdTextColorPress" />
</shape>
</item>
</selector>
8. MD5加密算法
任务分析:
MD5的全称是Message-Digest Algorithm5(信息-摘要算法),MD5算法简单来说就是把任意长度的字符串变换成固定长度(通常是128位)的十六进制字符串。在存储密码过程中,直接存储明文密码是很危险的,因此在存储密码过程中,直接存储明文密码
任务实施:
(1)创建MD5Utils类(在utils包中创建)。
(2)进行MD5加密。在MD5Utils类中,创建一个md5()方法对密码进行加密。首先通过MessageDigest的getInstanc()方法获取数据加密对象digest,然后通过该对象的digest()方法对密码进行加密。
MD5Utils.java
public class MD5Utils {
/**
* md5加密的算法
*/
public static String md5(String text) {
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("md5");
byte[] result = digest.digest(text.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : result) {
int number = b & 0xff;
String hex = Integer.toHexString(number);
if (hex.length() == 1) {
sb.append("0" + hex);
} else {
sb.append(hex);
}
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
}
}
9. “注册”界面逻辑代码
任务分析:
在“注册”界面点击“注册”按钮后,需要获取用户名与密码,将用户名和密码(经过MD5加密)保存到SharedPrefereces中。同时,注册成功后需要将用户名传递到“登录”界面(LoginActivity目前还未创建)中。
任务实施:
(1)获取界面控件。在RegisterActivity中创建界面控件的初始化方法init(),用于获取注册界面所要用到的控件以及实现控件的点击事件。
(2)判断SharedPreferences中是否存在需要注册的用户名。在RegisterActivity中创建一个isExistUserName()方法,用于判断SharedPreferences中是否存在需要注册的用户名。
(3)保存注册信息。在RegisterActivity中创建一个saveRegisterInfo()方法,将注册成功的用户名和密码(经过MD5加密)保存到SharedPreferences中。
RegisterActivity.java
public class RegisterActivity extends AppCompatActivity implements View.OnClickListener
{
private TextView tv_main_title, tv_back;
private RelativeLayout rl_title_bar;
private SwipeBackLayout layout;
private EditText et_psw, et_user_name;
private ImageView iv_show_psw;
private Button btn_register;
private String userName, psw;
private boolean isShowPsw = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
layout = (SwipeBackLayout) LayoutInflater.from(this).inflate(
R.layout.base, null);
layout.attachToActivity(this);
setContentView(R.layout.activity_register);
init();
}
private void init() {
tv_main_title = (TextView) findViewById(R.id.tv_main_title);
tv_main_title.setText("注册");
rl_title_bar = (RelativeLayout) findViewById(R.id.title_bar);
rl_title_bar.setBackgroundColor(getResources().getColor(R.color.
rdTextColorPress));
tv_back = (TextView) findViewById(R.id.tv_back);
tv_back.setVisibility(View.VISIBLE);
btn_register = (Button) findViewById(R.id.btn_register);
et_user_name = (EditText) findViewById(R.id.et_user_name);
et_psw = (EditText) findViewById(R.id.et_psw);
iv_show_psw = (ImageView) findViewById(R.id.iv_show_psw);
tv_back.setOnClickListener(this);
iv_show_psw.setOnClickListener(this);
btn_register.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.tv_back:
RegisterActivity.this.finish();
break;
case R.id.iv_show_psw:
psw = et_psw.getText().toString();
if (isShowPsw) {
iv_show_psw.setImageResource(R.drawable.hide_psw_icon);
et_psw.setTransformationMethod(PasswordTransformationMethod.
getInstance());//隐藏密码
isShowPsw = false;
if (psw != null) {
et_psw.setSelection(psw.length());
}
} else {
iv_show_psw.setImageResource(R.drawable.show_psw_icon);
et_psw.setTransformationMethod(HideReturnsTransformationMethod.
getInstance());//显示密码
isShowPsw = true;
if (psw != null) {
et_psw.setSelection(psw.length());
}
}
break;
case R.id.btn_register:
//获取输入在相应控件中的字符串
userName = et_user_name.getText().toString().trim();
psw = et_psw.getText().toString().trim();
if (TextUtils.isEmpty(userName)) {
Toast.makeText(RegisterActivity.this, "请输入用户名",
Toast.LENGTH_SHORT).show();
return;
} else if (TextUtils.isEmpty(psw)) {
Toast.makeText(RegisterActivity.this, "请输入密码",
Toast.LENGTH_SHORT).show();
return;
} else if (isExistUserName(userName)) {
Toast.makeText(RegisterActivity.this, "此账户名已经存在",
Toast.LENGTH_SHORT).show();
return;
} else {
Toast.makeText(RegisterActivity.this, "注册成功",
Toast.LENGTH_SHORT).show();
//把用户名和密码保存到SharedPreferences中
saveRegisterInfo(userName, psw);
//注册成功后把用户名传递到LoginActivity.java中
Intent data = new Intent();
data.putExtra("userName", userName);
setResult(RESULT_OK, data);
RegisterActivity.this.finish();
}
break;
}
}
/**
* 从SharedPreferences中读取输入的用户名,判断SharedPreferences中是否有此用户名
*/
private boolean isExistUserName(String userName) {
boolean has_userName = false;
SharedPreferences sp = getSharedPreferences("loginInfo", MODE_PRIVATE);
String spPsw = sp.getString(userName, "");
if (!TextUtils.isEmpty(spPsw)) {
has_userName = true;
}
return has_userName;
}
/**
* 保存用户名和密码到SharedPreferences中
*/
private void saveRegisterInfo(String userName, String psw) {
String md5Psw = MD5Utils.md5(psw); //把密码用MD5加密
//loginInfo表示文件名
SharedPreferences sp = getSharedPreferences("loginInfo", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();//获取编辑器
//以用户名为key,密码为value保存到SharedPreferences中
editor.putString(userName, md5Psw);
editor.commit();//提交修改
}
}
(4)修改清单文件。由于注册界面向右滑动会关闭该界面,因此需要给界面添加透明主题的样式,在清单文件的RegisterActivity对应的activity标签中添加如下代码:
android:theme="@style/AppTheme.TransparentActivity"
网友评论