一、项目
做一个带动画的登陆界面,如下图所示:
当点击输入密码时,猫头鹰的翅膀向上翻转,遮住眼睛。
二、知识补充——属性动画
Android之前的补间动画机制其实还算是比较健全的,在android.view.animation包下面有好多的类可以供我们操作,来完成一系列的动画效果,比如说对View进行移动、缩放、旋转和淡入淡出,并且我们还可以借助AnimationSet来将这些动画效果组合起来使用,除此之外还可以通过配置Interpolator来控制动画的播放速度等。补间动画是只能够作用在View上的。也就是说,我们可以对一个Button、TextView、甚至是LinearLayout、或者其它任何继承自View的组件进行动画操作,但是如果我们想要对一个非View的对象进行动画操作,补间动画就帮不上忙了。举一个简单的例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的第一个缺陷。还有补间动画只能够实现移动,缩放,旋转和淡入淡出这四种动画操作,不能对View的背景色进行改变。最后补间动画只改变了view的效果而已,而没有改变他的属性。如果要改变view的属性,就需要使用属性动画了
我们这个项目的动画就是属性动画,猫头鹰的翅膀旋转上去之后还需要旋转下来,转之前的坐标和转之后的坐标应该是不一样的,但如果使用补间动画旋转之前后旋转之后的坐标是一致的,这样不利于翅膀的复原。
三、具体实现
1,在XML文件添加背景图片,输入框,文本框,图像素材,按钮等,还有调控它们的位置,还可以在背景上添加虚化层,需要导入第三方库
在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">
<!--正常状态是一个圆角蓝色矩形-->
<item >
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
<solid android:color="#39A4F9"/>
</shape>
</item>
<!--无效状态下-->
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
<solid android:color="#66494343"/>
</shape>
</item>
</selector>
</resources>
2,这样就基本完成了素材的添加,接下来就需要为输入框添加内容和焦点改变事件,当有控件获得焦点就会自动弹出键盘。
//监听内容改变,控制按钮的点击状态
user.addTextChangedListener(this);
password.addTextChangedListener(this);
//监听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,实现输入框内容改变事件回调,管理登录按钮状态
@Override
public void afterTextChanged(Editable s) {
//判断这两个输入框是否有内容
if(user.getText().toString().length() > 0&&
password.getText().toString().length() > 0){
//登录按钮可以点击
button.setEnabled(true);
}else{
//登录按钮不可以点击
button.setEnabled(false);
}
}
4,监听touch事件,触摸屏幕,隐藏键盘,相应的输入框失去焦点
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
//隐藏键盘
//1,获取系统输入的管理器
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,创建一个anim文件,在里面配置手掌向上和向下的动画
向上移动的文件和这个一致,只是移动的方向相反
6,给猫头鹰的两个翅膀添加旋转动画
//捂眼睛
public void closeEye(){
// 左边 旋转翅膀
//创建一个旋转动画
RotateAnimation rotateAnimation1 = new RotateAnimation(0,170,leftArm.getWidth(),0f);
rotateAnimation1.setDuration(600);
//旋转后保持状态
rotateAnimation1.setFillAfter(true);
leftArm.startAnimation(rotateAnimation1);
//创建一个旋转动画
RotateAnimation rotateAnimation2 = new RotateAnimation(0,-170,0f,0f);
rotateAnimation2.setDuration(600);
//旋转后保持状态
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(){
// 左边 旋转翅膀
//创建一个旋转动画
RotateAnimation rotateAnimation1 = new RotateAnimation(170,0,leftArm.getWidth(),0f);
rotateAnimation1.setDuration(600);
//旋转后保持状态
rotateAnimation1.setFillAfter(true);
leftArm.startAnimation(rotateAnimation1);
//创建一个旋转动画
RotateAnimation rotateAnimation2 = new RotateAnimation(-170,0,0f,0f);
rotateAnimation2.setDuration(600);
//旋转后保持状态
rotateAnimation2.setFillAfter(true);
rightArm.startAnimation(rotateAnimation2);
TranslateAnimation translateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this,R.anim.hand_up);
leftHand.startAnimation(translateAnimation);
rightHand.startAnimation(translateAnimation);
}
效果展示:
录制屏幕时当点击输入密码时录制的内容是黑屏,目前还没解决……
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);
//调用
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);
//监听内容改变,控制按钮的点击状态
user.addTextChangedListener(this);
password.addTextChangedListener(this);
//监听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,获取系统输入的管理器
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) {
//判断这两个输入框是否有内容
if(user.getText().toString().length() > 0&&
password.getText().toString().length() > 0){
//按钮可以点击
button.setEnabled(true);
}else{
button.setEnabled(false);
}
}
//捂眼睛
public void closeEye(){
// 左边 旋转翅膀
//创建一个旋转动画
RotateAnimation rotateAnimation1 = new RotateAnimation(0,170,leftArm.getWidth(),0f);
rotateAnimation1.setDuration(600);
//旋转后保持状态
rotateAnimation1.setFillAfter(true);
leftArm.startAnimation(rotateAnimation1);
//创建一个旋转动画
RotateAnimation rotateAnimation2 = new RotateAnimation(0,-170,0f,0f);
rotateAnimation2.setDuration(600);
//旋转后保持状态
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(){
// 左边 旋转翅膀
//创建一个旋转动画
RotateAnimation rotateAnimation1 = new RotateAnimation(170,0,leftArm.getWidth(),0f);
rotateAnimation1.setDuration(600);
//旋转后保持状态
rotateAnimation1.setFillAfter(true);
leftArm.startAnimation(rotateAnimation1);
//创建一个旋转动画
RotateAnimation rotateAnimation2 = new RotateAnimation(-170,0,0f,0f);
rotateAnimation2.setDuration(600);
//旋转后保持状态
rotateAnimation2.setFillAfter(true);
rightArm.startAnimation(rotateAnimation2);
TranslateAnimation translateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this,R.anim.hand_up);
leftHand.startAnimation(translateAnimation);
rightHand.startAnimation(translateAnimation);
}
}
四、写在最后
学习的主要内容主要是属性动画的使用,所以密码设置这一块并没有做,因为之前写过用用户偏好保存密码所以这里就没有具体写。还有手机屏幕录制时点击输入密码后录制的一段是黑屏,百度之后发现这是应用的自身策略。密码界面调起安全输入法的时候,系统自带的录屏功能、第三方的录屏软件无法录制屏幕,截取结果是黑屏。这是由于安全输入法在此做了访问控制,避免你的密码被泄露,让你的手机更加安全。