一、項目
做一個帶動畫的登陸界面,如下圖所示:

當點擊輸入密碼時,貓頭鷹的翅膀向上翻轉(zhuǎn),遮住眼睛。

二、知識補充——屬性動畫
Android之前的補間動畫機制其實還算是比較健全的,在android.view.animation包下面有好多的類可以供我們操作,來完成一系列的動畫效果,比如說對View進行移動、縮放、旋轉(zhuǎn)和淡入淡出,并且我們還可以借助AnimationSet來將這些動畫效果組合起來使用,除此之外還可以通過配置Interpolator來控制動畫的播放速度等。補間動畫是只能夠作用在View上的。也就是說,我們可以對一個Button、TextView、甚至是LinearLayout、或者其它任何繼承自View的組件進行動畫操作,但是如果我們想要對一個非View的對象進行動畫操作,補間動畫就幫不上忙了。舉一個簡單的例子,比如說我們有一個自定義的View,在這個View當中有一個Point對象用于管理坐標,然后在onDraw()方法當中就是根據(jù)這個Point對象的坐標值來進行繪制的。也就是說,如果我們可以對Point對象進行動畫操作,那么整個自定義View的動畫效果就有了。顯然,補間動畫是不具備這個功能的,這是它的第一個缺陷。還有補間動畫只能夠?qū)崿F(xiàn)移動,縮放,旋轉(zhuǎn)和淡入淡出這四種動畫操作,不能對View的背景色進行改變。最后補間動畫只改變了view的效果而已,而沒有改變他的屬性。如果要改變view的屬性,就需要使用屬性動畫了

我們這個項目的動畫就是屬性動畫,貓頭鷹的翅膀旋轉(zhuǎn)上去之后還需要旋轉(zhuǎn)下來,轉(zhuǎn)之前的坐標和轉(zhuǎn)之后的坐標應該是不一樣的,但如果使用補間動畫旋轉(zhuǎn)之前后旋轉(zhuǎn)之后的坐標是一致的,這樣不利于翅膀的復原。
三、具體實現(xiàn)
1,在XML文件添加背景圖片,輸入框,文本框,圖像素材,按鈕等,還有調(diào)控它們的位置,還可以在背景上添加虛化層,需要導入第三方庫
在build.gradle中添加
implementation 'io.alterac.blurkit:blurkit:1.1.0'
并把 minSdkVersion 改成 21
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/bg"
android:scaleType="fitXY"/>
<!--添加虛化層-->
<io.alterac.blurkit.BlurLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:blk_fps="0"
app:blk_blurRadius="25">
</io.alterac.blurkit.BlurLayout>
<RelativeLayout
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
android:layout_alignTop="@id/bg"
android:layout_marginTop="-100dp"
>
<ImageView
android:id="@+id/iv_head"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="@drawable/owl" />
<!--添加手掌-->
<ImageView
android:id="@+id/leftHand"
android:layout_width="50dp"
android:layout_height="60dp"
android:src="@drawable/icon_hand"
android:layout_alignParentLeft="true"
android:layout_alignBottom="@id/iv_head"
android:layout_marginBottom="-20dp"
android:layout_marginLeft="10dp"/>
<!--添加手掌-->
<ImageView
android:id="@+id/rightHand"
android:layout_width="50dp"
android:layout_height="60dp"
android:src="@drawable/icon_hand"
android:layout_alignParentRight="true"
android:layout_alignBottom="@id/iv_head"
android:layout_marginBottom="-20dp"
android:layout_marginRight="10dp"/>
<!--添加翅膀-->
<ImageView
android:id="@+id/iv_leftArm"
android:layout_width="65dp"
android:layout_height="40dp"
android:src="@drawable/arm_left"
android:layout_below="@id/iv_head"
android:layout_alignParentLeft="true"
android:layout_marginLeft="20dp"/>
<ImageView
android:id="@+id/iv_rightArm"
android:layout_width="65dp"
android:layout_height="40dp"
android:src="@drawable/arm_right"
android:layout_below="@id/iv_head"
android:layout_alignParentRight="true"
android:layout_marginRight="20dp"/>
</RelativeLayout>
<!--輸入框-->
<io.alterac.blurkit.BlurLayout
android:id="@+id/bg"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@drawable/inputbackgroundshape"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
app:blk_fps="0"/>
<!--添加標題和輸入框背景-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:orientation="vertical"
android:padding="10dp"
android:layout_marginTop="10dp"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登錄"
android:textSize="20dp"
android:textColor="#999999"
android:textAlignment="center"
/>
<EditText
android:id="@+id/et_user"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/editor"
android:layout_marginTop="10dp"
android:drawableLeft="@drawable/iconfont_user"
android:paddingLeft="7dp"
android:drawablePadding="7dp"
android:textSize="20dp"
android:hint="請輸入用戶名"
android:inputType="text"
android:maxLines="1"
/>
<EditText
android:id="@+id/et_password"
style="@style/EditorTextStyle"
android:background="@drawable/editor"
android:drawableLeft="@drawable/iconfont_password"
android:inputType="textPassword"
android:hint="請輸入密碼"/>
<Button
android:id="@+id/button_login"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="登錄"
android:textColor="#ffffff"
android:layout_marginTop="20dp"
android:background="@drawable/loginbutton"
android:enabled="false"/>
</LinearLayout>
</RelativeLayout>
未添加虛化層效果展示:

其中輸入框的形狀在drawable文件里面配置
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="5dp"/>
<stroke android:width="1dp"
android:color="#44000000"/>
</shape>
使用style抽離輸入框共有屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--app中的標題,字體,字號,顏色
共同擁有的東西-->
<style name="EditorTextStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">50dp</item>
<item name="android:layout_marginTop">10dp</item>
<item name="android:paddingLeft">7dp</item>
<item name="android:drawablePadding">7dp</item>
<item name="android:textSize">20dp</item>
<item name="android:maxLines">1</item>
</style>
在drawable文件里面配置按鈕的形狀
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--正常狀態(tài)是一個圓角藍色矩形-->
<item >
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
<solid android:color="#39A4F9"/>
</shape>
</item>
<!--無效狀態(tài)下-->
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
<solid android:color="#66494343"/>
</shape>
</item>
</selector>
</resources>
2,這樣就基本完成了素材的添加,接下來就需要為輸入框添加內(nèi)容和焦點改變事件,當有控件獲得焦點就會自動彈出鍵盤。
//監(jiān)聽內(nèi)容改變,控制按鈕的點擊狀態(tài)
user.addTextChangedListener(this);
password.addTextChangedListener(this);
//監(jiān)聽EditText的焦點變化,判斷是否需要捂住眼睛
password.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus == true){
//捂住眼睛
//測試代碼
// Toast toast= Toast.makeText(getApplicationContext(),"捂住眼睛",Toast.LENGTH_SHORT);
// toast.show();
closeEye();
}else{
//打開眼睛
open();
}
}
});
}
3,實現(xiàn)輸入框內(nèi)容改變事件回調(diào),管理登錄按鈕狀態(tài)
@Override
public void afterTextChanged(Editable s) {
//判斷這兩個輸入框是否有內(nèi)容
if(user.getText().toString().length() > 0&&
password.getText().toString().length() > 0){
//登錄按鈕可以點擊
button.setEnabled(true);
}else{
//登錄按鈕不可以點擊
button.setEnabled(false);
}
}
4,監(jiān)聽touch事件,觸摸屏幕,隱藏鍵盤,相應的輸入框失去焦點
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
//隱藏鍵盤
//1,獲取系統(tǒng)輸入的管理器
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
//2,隱藏軟鍵盤
inputMethodManager.hideSoftInputFromWindow(user.getWindowToken(),0);
//3,取消焦點
View focusView = getCurrentFocus();
if(focusView != null) {
focusView.clearFocus(); //取消焦點
}
//focusView.requestFocus();//請求焦點
//簡單方式
//getCurrentFocus().clearFocus();
}
return true;
}
5,創(chuàng)建一個anim文件,在里面配置手掌向上和向下的動畫

向上移動的文件和這個一致,只是移動的方向相反
6,給貓頭鷹的兩個翅膀添加旋轉(zhuǎn)動畫
//捂眼睛
public void closeEye(){
// 左邊 旋轉(zhuǎn)翅膀
//創(chuàng)建一個旋轉(zhuǎn)動畫
RotateAnimation rotateAnimation1 = new RotateAnimation(0,170,leftArm.getWidth(),0f);
rotateAnimation1.setDuration(600);
//旋轉(zhuǎn)后保持狀態(tài)
rotateAnimation1.setFillAfter(true);
leftArm.startAnimation(rotateAnimation1);
//創(chuàng)建一個旋轉(zhuǎn)動畫
RotateAnimation rotateAnimation2 = new RotateAnimation(0,-170,0f,0f);
rotateAnimation2.setDuration(600);
//旋轉(zhuǎn)后保持狀態(tài)
rotateAnimation2.setFillAfter(true);
rightArm.startAnimation(rotateAnimation2);
TranslateAnimation translateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this,R.anim.hand_down);
leftHand.startAnimation(translateAnimation);
rightHand.startAnimation(translateAnimation);
}
public void open(){
// 左邊 旋轉(zhuǎn)翅膀
//創(chuàng)建一個旋轉(zhuǎn)動畫
RotateAnimation rotateAnimation1 = new RotateAnimation(170,0,leftArm.getWidth(),0f);
rotateAnimation1.setDuration(600);
//旋轉(zhuǎn)后保持狀態(tài)
rotateAnimation1.setFillAfter(true);
leftArm.startAnimation(rotateAnimation1);
//創(chuàng)建一個旋轉(zhuǎn)動畫
RotateAnimation rotateAnimation2 = new RotateAnimation(-170,0,0f,0f);
rotateAnimation2.setDuration(600);
//旋轉(zhuǎn)后保持狀態(tài)
rotateAnimation2.setFillAfter(true);
rightArm.startAnimation(rotateAnimation2);
TranslateAnimation translateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this,R.anim.hand_up);
leftHand.startAnimation(translateAnimation);
rightHand.startAnimation(translateAnimation);
}
效果展示:

錄制屏幕時當點擊輸入密碼時錄制的內(nèi)容是黑屏,目前還沒解決……
MainActivity完整代碼
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.RotateAnimation;
import android.view.animation.TranslateAnimation;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity implements TextWatcher {
private EditText user;
private EditText password;
private Button button;
private ImageView leftArm;
private ImageView rightArm;
private ImageView leftHand;
private ImageView rightHand;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//調(diào)用
initView();
}
//初始化
public void initView(){
user = findViewById(R.id.et_user);
password = findViewById(R.id.et_password);
button = findViewById(R.id.button_login);
leftArm = findViewById(R.id.iv_leftArm);
rightArm = findViewById(R.id.iv_rightArm);
leftHand = findViewById(R.id.leftHand);
rightHand = findViewById(R.id.rightHand);
//監(jiān)聽內(nèi)容改變,控制按鈕的點擊狀態(tài)
user.addTextChangedListener(this);
password.addTextChangedListener(this);
//監(jiān)聽EditText的焦點變化,判斷是否需要捂住眼睛
password.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus == true){
//捂住眼睛
//測試代碼
// Toast toast= Toast.makeText(getApplicationContext(),"捂住眼睛",Toast.LENGTH_SHORT);
// toast.show();
closeEye();
}else{
open();
}
}
});
}
/**
* 當有控件獲得焦點focus,自動彈出鍵盤
* 1,點擊軟鍵盤的enter鍵 自動收回鍵盤
* 2,代碼控制 InputMentionManager
* showSoftInput:顯示鍵盤
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
//隱藏鍵盤
//1,獲取系統(tǒng)輸入的管理器
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
//2,隱藏軟鍵盤
inputMethodManager.hideSoftInputFromWindow(user.getWindowToken(),0);
//3,取消焦點
View focusView = getCurrentFocus();
if(focusView != null) {
focusView.clearFocus(); //取消焦點
}
//focusView.requestFocus();//請求焦點
//簡單方式
//getCurrentFocus().clearFocus();
}
return true;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//判斷這兩個輸入框是否有內(nèi)容
if(user.getText().toString().length() > 0&&
password.getText().toString().length() > 0){
//按鈕可以點擊
button.setEnabled(true);
}else{
button.setEnabled(false);
}
}
//捂眼睛
public void closeEye(){
// 左邊 旋轉(zhuǎn)翅膀
//創(chuàng)建一個旋轉(zhuǎn)動畫
RotateAnimation rotateAnimation1 = new RotateAnimation(0,170,leftArm.getWidth(),0f);
rotateAnimation1.setDuration(600);
//旋轉(zhuǎn)后保持狀態(tài)
rotateAnimation1.setFillAfter(true);
leftArm.startAnimation(rotateAnimation1);
//創(chuàng)建一個旋轉(zhuǎn)動畫
RotateAnimation rotateAnimation2 = new RotateAnimation(0,-170,0f,0f);
rotateAnimation2.setDuration(600);
//旋轉(zhuǎn)后保持狀態(tài)
rotateAnimation2.setFillAfter(true);
rightArm.startAnimation(rotateAnimation2);
TranslateAnimation translateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this,R.anim.hand_down);
leftHand.startAnimation(translateAnimation);
rightHand.startAnimation(translateAnimation);
}
public void open(){
// 左邊 旋轉(zhuǎn)翅膀
//創(chuàng)建一個旋轉(zhuǎn)動畫
RotateAnimation rotateAnimation1 = new RotateAnimation(170,0,leftArm.getWidth(),0f);
rotateAnimation1.setDuration(600);
//旋轉(zhuǎn)后保持狀態(tài)
rotateAnimation1.setFillAfter(true);
leftArm.startAnimation(rotateAnimation1);
//創(chuàng)建一個旋轉(zhuǎn)動畫
RotateAnimation rotateAnimation2 = new RotateAnimation(-170,0,0f,0f);
rotateAnimation2.setDuration(600);
//旋轉(zhuǎn)后保持狀態(tài)
rotateAnimation2.setFillAfter(true);
rightArm.startAnimation(rotateAnimation2);
TranslateAnimation translateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this,R.anim.hand_up);
leftHand.startAnimation(translateAnimation);
rightHand.startAnimation(translateAnimation);
}
}
四、寫在最后
學習的主要內(nèi)容主要是屬性動畫的使用,所以密碼設置這一塊并沒有做,因為之前寫過用用戶偏好保存密碼所以這里就沒有具體寫。還有手機屏幕錄制時點擊輸入密碼后錄制的一段是黑屏,百度之后發(fā)現(xiàn)這是應用的自身策略。密碼界面調(diào)起安全輸入法的時候,系統(tǒng)自帶的錄屏功能、第三方的錄屏軟件無法錄制屏幕,截取結(jié)果是黑屏。這是由于安全輸入法在此做了訪問控制,避免你的密碼被泄露,讓你的手機更加安全。