English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
前書き
良いアプリには良いユーザーエクスペリエンスを持つログインインターフェースが必要です。今や、多くのアプリのログインインターフェースにはユーザー名、パスワードの一括削除、ユーザー名、パスワードが空のアラート、および認証コードの入力が必要な機能があります。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の実現プロセス:
考え方:二つの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; /** * Administratorによって作成されました 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)の4つの値で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)の4つの値でX座標が0から表現されています-->10,Y座標が0から-->10
4.this.setOnFocusChangeListener(new FocusChangeListenerImpl()); フォーカスの変更を設定するのは、より人間的な使い方を提供するためです。フォーカスがこの行にあり、文字がある場合にのみ一括削除アイコンを表示。フォーカスがこの行にない場合にはアイコンを隠します。
5.this.addTextChangedListener(new TextWatcherImpl()); テキストの変更を監視するリスナーを設定。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){}の中に実装したいメソッドを追加するだけで良いです。テキストが変更されたときに、右側のアイコンの表示を設定します。
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(); Bitmap bmNumB = Bitmap.createBitmap(getBitmapFromView(tvHideB,20,50),0,0,20,50,matrixB,true); ivNumB.setImageBitmap(bmNumB); Matrix matrixC = new Matrix(); //マトリックスをリセットします matrixC.reset(); Bitmap bmNumC = Bitmap.createBitmap(getBitmapFromView(tvHideC,20,50),0,0,20,50,matrixC,true); ivNumC.setImageBitmap(bmNumC); Matrix matrixD = new Matrix(); //マトリックスをリセットします matrixD.reset(); 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; //黒 colorArray[1] = 0xFFFF00FF; // 紫 colorArray[2] = 0xFFFF0000; // 赤 colorArray[3] = 0xFF00FF00; // 緑 colorArray[4] = 0xFF0000FF; // 青 colorArray[5] = 0xFF00FFFF; // 青 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); アイコンを作成します。
4.Canvas canvas =newCanvas(bitmap);
.v.draw(canvas); 画像を描画します
5.v.measure(widSpec, heiSpec);
//v.layout(0,0, width, height); 画像の大きさを再描画します。
以下は実行時の画像です:
入力がある場合、右側の一括削除アイコンが表示され、フォーカスを失った場合、一括削除アイコンが非表示になります。验证码を更新するには、验证码をクリックしてください:
その後の詳細な図示は省略します。
源码ダウンロード:http://xiazai.jb51.net/201610/源码/Androidlogin(jb51.net).rar
これで本文のすべてが終わり、皆様の学習に役立つことを願っています。また、呐喊教程を多くのサポートをお願いします。
声明:本文の内容はインターネットから取得しており、著作権者に帰属します。インターネットユーザーにより自発的に貢献し、自己でアップロードされた内容であり、本サイトは所有権を持ちません。また、人工的な編集は行われておらず、関連する法的責任も負いません。著作権に疑われる内容が見つかった場合は、メールを送信して:notice#w までお知らせください。3codebox.com(メールを送信する際には、#を@に変更してください)で通報し、関連する証拠を提供してください。一旦確認が取れたら、本サイトは即座に侵害を疑われるコンテンツを削除します。