前言
一個好的應用需要一個有良好的用戶體驗的登錄界面,現如今,許多應用的的登錄界面都有着用戶名,密碼一鍵刪除,用戶名,密碼爲空提示,以及需要輸入驗證碼的功能。看着csdn上的大牛們的文章,心裏想着也寫一個登錄界面學習學習,許多東西都是參考別的文章,綜合起來的。廢話少說,接下來看看是如何實現的。
ps:由於懶得摳圖。所以程序的圖標很難看。
程序運行時的圖示:
首先是佈局文件沒有什麼難度。
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<ImageView
android:id="@+id/tv_login"
android:src="@drawable/ic_launcher"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:gravity="center"
/>
<com.example.administrator.texttest.DeletableEditText
android:id="@+id/tv_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30dp"
android:layout_below="@id/tv_login"
android:drawableLeft="@drawable/ic_launcher"
android:drawableRight="@drawable/ic_launcher"
android:hint="請輸入賬戶"
android:ems="10"/>
<com.example.administrator.texttest.DeletableEditText
android:id="@+id/tv_psd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30dp"
android:layout_below="@id/tv_user"
android:drawableLeft="@drawable/ic_launcher"
android:drawableRight="@drawable/ic_launcher"
android:hint="請輸入密碼"
android:inputType="textPassword"
android:ems="10"/>
<LinearLayout
android:id="@+id/lyYanzhengma"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_psd">
<LinearLayout
android:id="@+id/lyVerify"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvHideA"
android:layout_width="70dp"
android:layout_height="70dp"
android:visibility="gone"
android:gravity="center"
android:textSize="30dp"
/>
<TextView
android:id="@+id/tvHideB"
android:layout_width="70dp"
android:layout_height="70dp"
android:visibility="gone"
android:gravity="center"
android:textSize="30dp"
/>
<TextView
android:id="@+id/tvHideC"
android:layout_width="70dp"
android:layout_height="70dp"
android:visibility="gone"
android:gravity="center"
android:textSize="30dp"
/>
<TextView
android:id="@+id/tvHideD"
android:layout_width="70dp"
android:layout_height="70dp"
android:visibility="gone"
android:gravity="center"
android:textSize="30dp"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/IV_num"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_height="70dp"
android:layout_width="50dp"
android:id="@+id/ivNumA"/>
<ImageView
android:layout_height="70dp"
android:layout_width="50dp"
android:id="@+id/ivNumB"/>
<ImageView
android:layout_height="70dp"
android:layout_width="50dp"
android:id="@+id/ivNumC"/>
<ImageView
android:layout_height="70dp"
android:layout_width="50dp"
android:id="@+id/ivNumD"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<EditText
android:layout_height="wrap_content"
android:layout_width="120dp"
android:textSize="30dp"
android:id="@+id/etCheck"
android:maxLength="4"
android:singleLine="true"
android:hint="驗證碼"/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="結果"
android:id="@+id/tvCheck"
android:textSize="30dp"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/bt_login"
android:text="登 錄"
android:textSize="30dp"
android:layout_below="@id/lyYanzhengma"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
然後是加載一些佈局文件,和一些控件的初始化
//登錄按鈕
private Button btLogin;
//賬戶
private DeletableEditText userEditText;
//密碼
private DeletableEditText psdEditText;
//驗證碼的數字文本
private TextView tvHideA,tvHideB,tvHideC,tvHideD;
//驗證碼的圖片文本
private ImageView ivNumA,ivNumB,ivNumC,ivNumD;
//驗證碼輸入文本
private EditText etCheck;
//驗證碼的檢測顯示文本
private TextView tvCheck;
//存儲每個驗證碼的數字
private String numStrTmp = "";
//存儲整個驗證碼的數字
private String numStr = "";
//存儲驗證碼的數組
private int[] numArray = new int[4];
//存儲顏色的數組
private int[] colorArray = new int[6];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupViews();
}
private void setupViews() {
btLogin = (Button) findViewById(R.id.bt_login);
btLogin.setOnClickListener(new OnClickListenerImpl());
userEditText = (DeletableEditText) findViewById(R.id.tv_user);
psdEditText = (DeletableEditText) findViewById(R.id.tv_psd);
tvHideA = (TextView) findViewById(R.id.tvHideA);
tvHideB = (TextView) findViewById(R.id.tvHideB);
tvHideC = (TextView) findViewById(R.id.tvHideC);
tvHideD = (TextView) findViewById(R.id.tvHideD);
ivNumA = (ImageView) findViewById(R.id.ivNumA);
ivNumB = (ImageView) findViewById(R.id.ivNumB);
ivNumC = (ImageView) findViewById(R.id.ivNumC);
ivNumD = (ImageView) findViewById(R.id.ivNumD);
ivNumA.setOnClickListener(new OnClickListenerImpl());
ivNumB.setOnClickListener(new OnClickListenerImpl());
ivNumC.setOnClickListener(new OnClickListenerImpl());
ivNumD.setOnClickListener(new OnClickListenerImpl());
tvCheck = (TextView) findViewById(R.id.tvCheck);
etCheck = (EditText) findViewById(R.id.etCheck);
setNum();
自定義EditText的實現過程:
思路:設置兩個EidtText,在這個EditText中各設置圖標。左邊圖標爲賬戶和密碼的圖標提示,右邊圖標爲一鍵刪除 。因爲EditText中的圖標沒有onClick事件,爲了實現點擊一鍵刪除效果所以要使用OnTouchEvent回調方法,監聽點擊事件來判斷實現一鍵刪除。當賬戶和密碼沒有字符時,右邊的一鍵刪除圖標設置隱藏,當有字符時,設置圖標顯示。當點擊到右邊圖標範圍時,刪除所在行的字符。從而實現一鍵刪除。 此外,當賬戶和密碼爲空而要登錄時。這兩行抖動提示。
廢話不多說,直接上代碼註釋很清楚。
package com.example.administrator.texttest;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.CycleInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.EditText;
/**
* Created by Administrator on 2015-10-10.
*/
public class DeletableEditText extends EditText {
private Drawable mRightDrawable;
private boolean isHasFocus;
public DeletableEditText(Context context) {
this(context, null);
}
public DeletableEditText(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.editTextStyle);
}
public DeletableEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setupViews();
}
private void setupViews() {
//取的view的上下左右邊距
Drawable[] drawables = this.getCompoundDrawables();
// 取得right位置的Drawable
// 即我們在佈局文件中設置的android:drawableRight
mRightDrawable = drawables[2];
// 設置焦點變化的監聽
this.setOnFocusChangeListener(new FocusChangeListenerImpl());
// 設置EditText文字變化的監聽
this.addTextChangedListener(new TextWatcherImpl());
// 初始化時讓右邊clean圖標不可見
setClearDrawableVisible(false);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
//當點擊鬆開時判斷點擊的位置。這裏只進行了X軸方向的判斷。
case MotionEvent.ACTION_UP:
//判斷是否點擊到了右邊的圖標區域
boolean isClean = (event.getX() > (getWidth() - getTotalPaddingRight()))
&& (event.getX() < (getWidth() - getPaddingRight()));
if (isClean) {
//清除字符
setText("");
}
break;
default:
break;
}
return super.onTouchEvent(event);
}
private class FocusChangeListenerImpl implements OnFocusChangeListener {
@Override
public void onFocusChange(View v, boolean hasFocus) {
isHasFocus = hasFocus;
if (isHasFocus) {
boolean isVisible = getText().toString().length() >= 1;
setClearDrawableVisible(isVisible);
} else {
setClearDrawableVisible(false);
}
}
}
// 當輸入結束後判斷是否顯示右邊clean的圖標
private class TextWatcherImpl implements TextWatcher {
@Override
public void afterTextChanged(Editable s) {
//當有字符時爲true
boolean isVisible = getText().toString().length() >= 1;
//顯示右邊的圖標
setClearDrawableVisible(isVisible);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
}
// 隱藏或顯示右邊clean的圖標
protected void setClearDrawableVisible(boolean isVisible) {
Drawable rightDrawable;
if (isVisible) {
rightDrawable = mRightDrawable;
} else {
rightDrawable = null;
}
// 使用代碼設置該控件right處的圖標
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], rightDrawable,
getCompoundDrawables()[3]);
}
// 顯示動畫
public void setShakeAnimation() {
this.startAnimation(shakeAnimation(5));
}
// CycleTimes動畫重複的次數
public Animation shakeAnimation(int CycleTimes) {
//設置偏移動畫 其中new TranslateAnimation(0,10,0,10)四個值表示爲 X座標從0-->10,Y座標從0-->10
Animation translateAnimation = new TranslateAnimation(0, 10, 0, 10);
//設置動畫次數
translateAnimation.setInterpolator(new CycleInterpolator(CycleTimes));
//設置動畫間隔
translateAnimation.setDuration(1000);
return translateAnimation;
}
}
其中需要注意的知識:
1.Drawable[] drawables = this.getCompoundDrawables(); 得到此View的 drawable. getCompoundDrawables()方法得到的有4個Drawable對象,分別對應此View的左,上,右,下的邊距
2.boolean isClean = (event.getX() > (getWidth() - getTotalPaddingRight()))&& (event.getX() < (getWidth() - getPaddingRight())); 判斷點擊的區域是否爲右邊圖標範圍。其中event.getX()爲點擊的位置的X座標大小。詳細如下圖所示:
3.Animation translateAnimation = new TranslateAnimation(0, 10, 0, 10); 設置偏移動畫 其中new TranslateAnimation(0,10,0,10)四個值表示爲 X座標從0-->10,Y座標從0-->10
4.this.setOnFocusChangeListener(new FocusChangeListenerImpl());設置焦點變化的目的是爲了更人性化。當焦點在此行並且有字符時才顯示一鍵刪除圖標。不在此行時圖標隱藏。
5.
this.addTextChangedListener(new TextWatcherImpl()); 設置text變化監聽。new TextWatcher{}中有3個方法。分別是:
1.public void beforeTextChanged(CharSequence s, int start, int count,int after) {}
2.public void onTextChanged(CharSequence s, int start, int before,int count) {}
3.public void afterTextChanged(Editable s) {}
我們只需要在afterTextChanged(){Editable s}{}中添加要實現的方法即可。當監聽到text變化時,設置右邊圖標顯示。
TextWatcher { @Override public void afterTextChanged(Editable s) { //當有字符時爲true boolean isVisible = getText().toString().length() >= 1; //顯示右邊的圖標 setClearDrawableVisible(isVisible); }
驗證碼的實現過程。
思路:設置4個ImageView。 首先隨機生成4個10以內的數字存儲在數組裏。並且記錄整個驗證碼。在利用Bitmap.createBitmap方法講這4個數字轉化爲圖片並且設置隨機顏色。每個數字圖片轉化的時候在隨機設置旋轉角度使這4個數字圖標傾斜一定的角度。 驗證碼就生成了。
驗證驗證碼的過程只是模擬下: 將輸入的驗證碼跟記錄的驗證碼作比較。相同 提示正確,不同提示錯誤。並且重置驗證碼。
點擊驗證碼圖片區域也會重置驗證碼。
代碼的註釋很詳細。上代碼~~~~:
package com.example.administrator.texttest;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
//登錄按鈕
private Button btLogin;
//賬戶
private DeletableEditText userEditText;
//密碼
private DeletableEditText psdEditText;
//驗證碼的數字文本
private TextView tvHideA,tvHideB,tvHideC,tvHideD;
//驗證碼的圖片文本
private ImageView ivNumA,ivNumB,ivNumC,ivNumD;
//驗證碼輸入文本
private EditText etCheck;
//驗證碼的檢測顯示文本
private TextView tvCheck;
//存儲每個驗證碼的數字
private String numStrTmp = "";
//存儲整個驗證碼的數字
private String numStr = "";
//存儲驗證碼的數組
private int[] numArray = new int[4];
//存儲顏色的數組
private int[] colorArray = new int[6];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupViews();
}
private void setupViews() {
btLogin = (Button) findViewById(R.id.bt_login);
btLogin.setOnClickListener(new OnClickListenerImpl());
userEditText = (DeletableEditText) findViewById(R.id.tv_user);
psdEditText = (DeletableEditText) findViewById(R.id.tv_psd);
tvHideA = (TextView) findViewById(R.id.tvHideA);
tvHideB = (TextView) findViewById(R.id.tvHideB);
tvHideC = (TextView) findViewById(R.id.tvHideC);
tvHideD = (TextView) findViewById(R.id.tvHideD);
ivNumA = (ImageView) findViewById(R.id.ivNumA);
ivNumB = (ImageView) findViewById(R.id.ivNumB);
ivNumC = (ImageView) findViewById(R.id.ivNumC);
ivNumD = (ImageView) findViewById(R.id.ivNumD);
ivNumA.setOnClickListener(new OnClickListenerImpl());
ivNumB.setOnClickListener(new OnClickListenerImpl());
ivNumC.setOnClickListener(new OnClickListenerImpl());
ivNumD.setOnClickListener(new OnClickListenerImpl());
tvCheck = (TextView) findViewById(R.id.tvCheck);
etCheck = (EditText) findViewById(R.id.etCheck);
setNum();
}
private void setNum() {
initNum();
tvHideA.setText("" + numArray[0]);
tvHideA.setTextColor(randomColor());
tvHideB.setText("" + numArray[1]);
tvHideB.setTextColor(randomColor());
tvHideC.setText("" + numArray[2]);
tvHideC.setTextColor(randomColor());
tvHideD.setText("" + numArray[3]);
tvHideD.setTextColor(randomColor());
Matrix matrixA = new Matrix();
//重設矩陣
matrixA.reset();
matrixA.setRotate(randomAngle());
Bitmap bmNumA = Bitmap.createBitmap(getBitmapFromView(tvHideA,20,50),0,0,20,50,matrixA,true);
ivNumA.setImageBitmap(bmNumA);
Matrix matrixB = new Matrix();
//重設矩陣
matrixB.reset();
matrixB.setRotate(randomAngle());
Bitmap bmNumB = Bitmap.createBitmap(getBitmapFromView(tvHideB,20,50),0,0,20,50,matrixB,true);
ivNumB.setImageBitmap(bmNumB);
Matrix matrixC = new Matrix();
//重設矩陣
matrixC.reset();
matrixC.setRotate(randomAngle());
Bitmap bmNumC = Bitmap.createBitmap(getBitmapFromView(tvHideC,20,50),0,0,20,50,matrixC,true);
ivNumC.setImageBitmap(bmNumC);
Matrix matrixD = new Matrix();
//重設矩陣
matrixD.reset();
matrixD.setRotate(randomAngle());
Bitmap bmNumD = Bitmap.createBitmap(getBitmapFromView(tvHideD,20,50),0,0,20,50,matrixD,true);
ivNumD.setImageBitmap(bmNumD);
}
private Bitmap getBitmapFromView(View v,int width,int height ) {
int widSpec = View.MeasureSpec.makeMeasureSpec(width,View.MeasureSpec.EXACTLY);
int heiSpec = View.MeasureSpec.makeMeasureSpec(height,View.MeasureSpec.EXACTLY);
//重新繪製圖片大小
v.measure(widSpec, heiSpec);
//
v.layout(0, 0, width, height);
Bitmap bitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
//畫出圖片
Canvas canvas = new Canvas(bitmap);
v.draw(canvas);
return bitmap;
}
//設置隨機傾斜的角度
private int randomAngle() {
return 20*(new Random().nextInt(5)-new Random().nextInt(3));
}
//隨機生成顏色
private int randomColor() {
colorArray[0]=0xFF000000; //BLACK
colorArray[1] = 0xFFFF00FF; // MAGENTA
colorArray[2] = 0xFFFF0000; // RED
colorArray[3] = 0xFF00FF00; // GREEN
colorArray[4] = 0xFF0000FF; // BLUE
colorArray[5] = 0xFF00FFFF; // CYAN
int randomColoId = new Random().nextInt(5);
return colorArray[randomColoId];
}
//初始化驗證碼
private void initNum() {
numStr="";
numStrTmp="";
for (int i = 0; i < numArray.length; i++) {
//隨機生成10以內數字
int numIntTmp = new Random().nextInt(10);
//保存各個驗證碼
numStrTmp = String.valueOf(numIntTmp);
//保存整個驗證碼
numStr = numStr+numStrTmp;
numArray[i] = numIntTmp;
}
}
private class OnClickListenerImpl implements View.OnClickListener {
@Override
public void onClick(View v) {
//當點擊的爲登錄按鈕時
if(v==btLogin){
//判斷賬戶字符是否爲空,
if (TextUtils.isEmpty(userEditText.getText().toString())){
//爲空時抖動提示
userEditText.setShakeAnimation();
Toast.makeText(MainActivity.this,"賬戶或密碼不能爲空",Toast.LENGTH_SHORT).show();
}
//判斷密碼字符是否爲空
if (TextUtils.isEmpty(psdEditText.getText().toString())){
//爲空時抖動提示
psdEditText.setShakeAnimation();
Toast.makeText(MainActivity.this,"賬戶或密碼不能爲空",Toast.LENGTH_SHORT).show();
}
//驗證輸入的驗證碼是否正確
if(etCheck.getText().toString()!=null&&etCheck.getText().toString().trim().length()>0){
tvCheck.setVisibility(View.VISIBLE);
if (numStr.equals(etCheck.getText().toString())){
tvCheck.setTextColor(Color.GREEN);
tvCheck.setText("驗證碼正確!");
}else{
tvCheck.setTextColor(Color.RED);
tvCheck.setText("驗證碼錯誤!");
etCheck.setText("");
setNum();
}
}
//如果OnClick不是登錄按鈕時只剩下驗證碼圖片有監聽事件。等同於點擊驗證碼圖片。改變驗證碼。
}else {
setNum();
tvCheck.setVisibility(View.GONE);
}
}
}
}
需要注意的知識:
1.Bitmap.createBitmap(getBitmapFromView(tvHideA,20,50),0,0,20,50,matrixA,true);
Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)
Bitmap
source:要從中截圖的原始位圖
int x: 起始x座標
int y:起始y座標
int width: 要截的圖的寬度
int height:要截的圖的高度
boolean filter 當進行的不只是平移變換時,filter參數爲true可以進行濾波處理,有助於改善新圖像質量;flase時,計算機不做過濾處理。
2.intwidSpec = View.MeasureSpec.makeMeasureSpec(width,View.MeasureSpec.EXACTLY);
int heiSpec = View.MeasureSpec.makeMeasureSpec(height,View.MeasureSpec.EXACTLY);
設置View的寬和高。View.MeasureSpec.EXACTLY 指的是設置爲實際View的大小。即前面的width(height)爲多大就爲多大。
3.Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888); create一個圖標。
4.Canvas canvas =newCanvas(bitmap);
v.draw(canvas); 畫出圖片
5.v.measure(widSpec, heiSpec);
//v.layout(0,0, width, height); 重新繪製圖片的大小。
後面是運行時的圖片:
當有輸入時右邊的一鍵刪除圖標顯示 當失去焦點時一鍵刪除圖標隱藏: 點擊驗證碼更新驗證碼:
:
後面的就不詳細圖示了。
資源代碼下載地址