大众化的登录界面的一种完美简单的实现方法(验证码+自带一键删除+用户名密码为空时抖动提示)

 

前言

     一个好的应用需要一个有良好的用户体验的登录界面,现如今,许多应用的的登录界面都有着用户名,密码一键删除,用户名,密码为空提示,以及需要输入验证码的功能。看着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);  重新绘制图片的大小。

 

 

 

 

 

 

 

后面是运行时的图片:

 当有输入时右边的一键删除图标显示                当失去焦点时一键删除图标隐藏:         点击验证码更新验证码:

 

 

 

 

后面的就不详细图示了。

 

 

 

 

 


 


 

 

 

资源代码下载地址

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值