哈哈,看到這個標(biāo)題是不是JH一緊疑俭,你可能會說我就沒遇到過粮呢,但是現(xiàn)在沒遇到不代表就遇不到,畢竟設(shè)計也是變幻莫測,只有你想不到的啄寡,沒有你不能實現(xiàn)的豪硅,說的這么吊,到底是啥效果这难?沒錯就是一個小小的登錄頁面舟误,大家都有拉勾app吧,看拉勾的登錄頁做的很是平滑動畫姻乓,而且?guī)赢嬓Ч兑纾跃陀辛祟愃评吹卿浶Ч鐖D:
github地址
雖然是個簡單的頁面蹋岩,但是涵蓋的東西不算少啊赖草,很納悶為何谷歌一直不提供簡單,方便剪个,準(zhǔn)確的鍵盤監(jiān)聽事件秧骑?惆悵啊,所以我們只能自己從側(cè)面監(jiān)聽鍵盤事件了扣囊,我們可以監(jiān)聽最外層布局的變化來判斷鍵盤是不是彈起了乎折。閑話不多說,上車吧侵歇。
布局文件骂澄,大家都能看懂吧。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:clipToPadding="true"
android:background="@color/color_ffffff"
android:orientation="vertical">
<ImageView
android:id="@+id/logo"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerHorizontal="true"
android:layout_gravity="center"
android:background="@null"
android:layout_marginTop="80dp"
android:scaleType="centerCrop"
android:src="@drawable/skin_about_brand" />
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:fillViewport="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:scrollbarThumbVertical="@android:color/transparent"
android:scrollbars="vertical"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginTop="200dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="13dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="15dp"
android:src="@drawable/ic_mobile_flag" />
<EditText
android:id="@+id/et_mobile"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="@string/hint_login_username"
android:inputType="textVisiblePassword"
android:maxLength="11"
android:singleLine="true"
android:text=""
android:textColor="@color/color_999999"
android:textColorHint="@color/color_999999"
android:textSize="14dp" />
<ImageView
android:id="@+id/iv_clean_phone"
android:layout_width="40dp"
android:layout_height="fill_parent"
android:scaleType="centerInside"
android:src="@drawable/ic_clear"
android:visibility="gone" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/color_eeeeee" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="13dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="15dp"
android:src="@drawable/ic_password_flag" />
<EditText
android:id="@+id/et_password"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="@string/hint_login_password"
android:inputType="textPassword"
android:maxLength="30"
android:singleLine="true"
android:text=""
android:textColor="@color/color_999999"
android:textColorHint="@color/color_999999"
android:textSize="14dp" />
<ImageView
android:id="@+id/clean_password"
android:layout_width="40dp"
android:layout_height="fill_parent"
android:scaleType="centerInside"
android:src="@drawable/ic_clear"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_show_pwd"
android:layout_width="40dp"
android:layout_height="fill_parent"
android:scaleType="centerInside"
android:src="@drawable/pass_gone" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/color_eeeeee" />
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="21dp"
android:background="@drawable/bg_btn_login_selected"
android:text="@string/login"
android:textColor="@color/color_ffffff"
android:textSize="18dp" />
<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<TextView
android:id="@+id/regist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginBottom="10dp"
android:text="注冊新用戶"
android:layout_weight="1"
android:textColor="@color/color_b0b8b2"
android:textSize="14dp" />
<TextView
android:id="@+id/forget_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginBottom="10dp"
android:layout_marginLeft="21dp"
android:text="@string/login_forget_pwd"
android:textColor="@color/color_b0b8b2"
android:textSize="14dp" />
</LinearLayout>
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/service"
android:layout_width="match_parent"
android:orientation="horizontal"
android:gravity="center"
android:padding="10dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="聯(lián)系客服"
android:textColor="@color/color_b0b8b2"
android:textSize="14dp" />
<View
android:layout_width="1dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@color/color_eeeeee"
android:layout_height="match_parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="關(guān)于我們"
android:textColor="@color/color_b0b8b2"
android:textSize="14dp" />
</LinearLayout>
</RelativeLayout>
我們要想監(jiān)聽鍵盤事件惕虑,首先我們想得到的是鍵盤彈起的時候我們可以去搞點事情坟冲,鍵盤搜起的時候我們再去搞點事情,知道這些還不夠溃蔫,我們還要知道鍵盤彈起了多少健提,以及需要平移多少的距離。我們都知道我們的一個頁面彈起鍵盤的時候這個頁面的根布局會回調(diào)他的監(jiān)聽方法:addOnLayoutChangeListener( );當(dāng)鍵盤彈起的時候伟叛,我們的布局是變化了私痹,因此會執(zhí)行這個回調(diào)方法,但是前提是必須設(shè)置我們的Activity的windowSoftInputMode屬性為adjustResize痪伦。
我們想讓布局整體平移的距離也就是彈起時候處于最底部的view距離頂部的高度減去我們鍵盤的高度≈读瘢現(xiàn)在認(rèn)為只要控件將Activity向上推的高度超過了1/3屏幕高,就認(rèn)為軟鍵盤彈起
scrollView.addOnLayoutChangeListener(new ViewGroup.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
/* old是改變前的左上右下坐標(biāo)點值网沾,沒有old的是改變后的左上右下坐標(biāo)點值
現(xiàn)在認(rèn)為只要控件將Activity向上推的高度超過了1/3屏幕高,就認(rèn)為軟鍵盤彈起*/
if (oldBottom != 0 && bottom != 0 && (oldBottom - bottom > keyHeight)) {
Log.e("wenzhihao", "up------>"+(oldBottom - bottom));
int dist = btn_login.getBottom() - bottom;
if (dist>0){
ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(content, "translationY", 0.0f, -dist);
mAnimatorTranslateY.setDuration(300);
mAnimatorTranslateY.setInterpolator(new LinearInterpolator());
mAnimatorTranslateY.start();
zoomIn(logo, dist);
}
service.setVisibility(View.INVISIBLE);
} else if (oldBottom != 0 && bottom != 0 && (bottom - oldBottom > keyHeight)) {
Log.e("wenzhihao", "down------>"+(bottom - oldBottom));
if ((btn_login.getBottom() - oldBottom)>0){
ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(content, "translationY", content.getTranslationY(), 0);
mAnimatorTranslateY.setDuration(300);
mAnimatorTranslateY.setInterpolator(new LinearInterpolator());
mAnimatorTranslateY.start();
//鍵盤收回后蕊爵,logo恢復(fù)原來大小辉哥,位置同樣回到初始位置
zoomOut(logo);
}
service.setVisibility(View.VISIBLE);
}
}
});
//btn_login是登錄按鈕
這樣我們發(fā)現(xiàn)是可以實現(xiàn)效果了,但是我想全屏顯示,懵比了醋旦,發(fā)現(xiàn)全屏的時候不回調(diào)這個方法了恒水,怎么辦?又是查資料一看原來這個也是一個bug饲齐,但是有解決方案钉凌,AndroidBug5497Workaround。也是谷歌提供的捂人?直接拷貝過來御雕,會發(fā)現(xiàn)其實他的作用就是讓Activity最外層的根布局,當(dāng)有布局變化時去響應(yīng)這個變化mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener();
package com.wzh.study.login;
import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
public class AndroidBug5497Workaround {
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static void assistActivity (Activity activity) {
new AndroidBug5497Workaround(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private AndroidBug5497Workaround(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
使用方式,如果我們設(shè)置了全屏滥搭,就去加載它酸纲,不設(shè)置不管:
if(isFullScreen(this)){
AndroidBug5497Workaround.assistActivity(this);
}
...
public boolean isFullScreen(Activity activity) {
return (activity.getWindow().getAttributes().flags &
WindowManager.LayoutParams.FLAG_FULLSCREEN)==WindowManager.LayoutParams.FLAG_FULLSCREEN;
}
接下來就看具體動畫事件了,鍵盤彈起來的時候整體向上平移,LOGO縮小瑟匆,鍵盤收起的時候整體下移闽坡,并且LOGO恢復(fù)原來大小。這里用到的都是屬性動畫愁溜,只有屬性動畫我們才可以實現(xiàn)真正平移效果疾嗅。
我看網(wǎng)上很多人使用addOnLayoutChangeListener()去監(jiān)聽鍵盤事件,但是這個方法回調(diào)的太頻繁冕象,比如本例特效代承,輸入框后面有文字時候顯示清除的圖標(biāo),如果用這個方法那么也會執(zhí)行一次交惯,可能會影響你的動畫次泽,當(dāng)然你也可以去記錄第一次的高度讓他不會走邏輯,但是我覺得也不是很靠譜席爽,雖然我這個方法也不是很棒 ?乛?乛?~意荤。
最后貼上源碼:
package com.wzh.study.login;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.animation.LinearOutSlowInInterpolator;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.wzh.study.R;
/**
* Created by WZH on 2017/3/25.
*/
public class OtherLoginAct extends FragmentActivity implements View.OnClickListener {
private ImageView logo;
private ScrollView scrollView;
private EditText et_mobile;
private EditText et_password;
private ImageView iv_clean_phone;
private ImageView clean_password;
private ImageView iv_show_pwd;
private Button btn_login;
private TextView forget_password;
private int screenHeight = 0;//屏幕高度
private int keyHeight = 0; //軟件盤彈起后所占高度
private float scale = 0.6f; //logo縮放比例
private View service,content;
private int height = 0 ;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other_login);
if(isFullScreen(this)){
AndroidBug5497Workaround.assistActivity(this);
}
initView();
initListener();
}
public boolean isFullScreen(Activity activity) {
return (activity.getWindow().getAttributes().flags &
WindowManager.LayoutParams.FLAG_FULLSCREEN)==WindowManager.LayoutParams.FLAG_FULLSCREEN;
}
private void initView() {
logo = (ImageView) findViewById(R.id.logo);
scrollView = (ScrollView) findViewById(R.id.scrollView);
et_mobile = (EditText) findViewById(R.id.et_mobile);
et_password = (EditText) findViewById(R.id.et_password);
iv_clean_phone = (ImageView) findViewById(R.id.iv_clean_phone);
clean_password = (ImageView) findViewById(R.id.clean_password);
iv_show_pwd = (ImageView) findViewById(R.id.iv_show_pwd);
btn_login = (Button) findViewById(R.id.btn_login);
forget_password = (TextView) findViewById(R.id.forget_password);
service = findViewById(R.id.service);
content = findViewById(R.id.content);
screenHeight = this.getResources().getDisplayMetrics().heightPixels; //獲取屏幕高度
keyHeight = screenHeight / 3;//彈起高度為屏幕高度的1/3
}
private void initListener() {
iv_clean_phone.setOnClickListener(this);
clean_password.setOnClickListener(this);
iv_show_pwd.setOnClickListener(this);
et_mobile.addTextChangedListener(new TextWatcher() {
@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 (!TextUtils.isEmpty(s) && iv_clean_phone.getVisibility() == View.GONE) {
iv_clean_phone.setVisibility(View.VISIBLE);
} else if (TextUtils.isEmpty(s)) {
iv_clean_phone.setVisibility(View.GONE);
}
}
});
et_password.addTextChangedListener(new TextWatcher() {
@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 (!TextUtils.isEmpty(s) && clean_password.getVisibility() == View.GONE) {
clean_password.setVisibility(View.VISIBLE);
} else if (TextUtils.isEmpty(s)) {
clean_password.setVisibility(View.GONE);
}
if (s.toString().isEmpty())
return;
if (!s.toString().matches("[A-Za-z0-9]+")) {
String temp = s.toString();
Toast.makeText(OtherLoginAct.this, R.string.please_input_limit_pwd, Toast.LENGTH_SHORT).show();
s.delete(temp.length() - 1, temp.length());
et_password.setSelection(s.length());
}
}
});
/**
* 禁止鍵盤彈起的時候可以滾動
*/
scrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
scrollView.addOnLayoutChangeListener(new ViewGroup.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
/* old是改變前的左上右下坐標(biāo)點值,沒有old的是改變后的左上右下坐標(biāo)點值
現(xiàn)在認(rèn)為只要控件將Activity向上推的高度超過了1/3屏幕高只锻,就認(rèn)為軟鍵盤彈起*/
if (oldBottom != 0 && bottom != 0 && (oldBottom - bottom > keyHeight)) {
Log.e("wenzhihao", "up------>"+(oldBottom - bottom));
int dist = content.getBottom() - bottom;
if (dist>0){
ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(content, "translationY", 0.0f, -dist);
mAnimatorTranslateY.setDuration(300);
mAnimatorTranslateY.setInterpolator(new LinearInterpolator());
mAnimatorTranslateY.start();
zoomIn(logo, dist);
}
service.setVisibility(View.INVISIBLE);
} else if (oldBottom != 0 && bottom != 0 && (bottom - oldBottom > keyHeight)) {
Log.e("wenzhihao", "down------>"+(bottom - oldBottom));
if ((content.getBottom() - oldBottom)>0){
ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(content, "translationY", content.getTranslationY(), 0);
mAnimatorTranslateY.setDuration(300);
mAnimatorTranslateY.setInterpolator(new LinearInterpolator());
mAnimatorTranslateY.start();
//鍵盤收回后玖像,logo恢復(fù)原來大小,位置同樣回到初始位置
zoomOut(logo);
}
service.setVisibility(View.VISIBLE);
}
}
});
}
/**
* 縮小
* @param view
*/
public void zoomIn(final View view, float dist) {
view.setPivotY(view.getHeight());
view.setPivotX(view.getWidth() / 2);
AnimatorSet mAnimatorSet = new AnimatorSet();
ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, scale);
ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f, scale);
ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(view, "translationY", 0.0f, -dist);
mAnimatorSet.play(mAnimatorTranslateY).with(mAnimatorScaleX);
mAnimatorSet.play(mAnimatorScaleX).with(mAnimatorScaleY);
mAnimatorSet.setDuration(300);
mAnimatorSet.start();
}
/**
* f放大
* @param view
*/
public void zoomOut(final View view) {
view.setPivotY(view.getHeight());
view.setPivotX(view.getWidth() / 2);
AnimatorSet mAnimatorSet = new AnimatorSet();
ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(view, "scaleX", scale, 1.0f);
ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(view, "scaleY", scale, 1.0f);
ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(view, "translationY", view.getTranslationY(), 0);
mAnimatorSet.play(mAnimatorTranslateY).with(mAnimatorScaleX);
mAnimatorSet.play(mAnimatorScaleX).with(mAnimatorScaleY);
mAnimatorSet.setDuration(300);
mAnimatorSet.start();
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.iv_clean_phone:
et_mobile.setText("");
break;
case R.id.clean_password:
et_password.setText("");
break;
case R.id.iv_show_pwd:
if (et_password.getInputType() != InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
et_password.setInputType(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
iv_show_pwd.setImageResource(R.drawable.pass_visuable);
} else {
et_password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
iv_show_pwd.setImageResource(R.drawable.pass_gone);
}
String pwd = et_password.getText().toString();
if (!TextUtils.isEmpty(pwd))
et_password.setSelection(pwd.length());
break;
}
}
}
如果有什么問題歡迎指出齐饮,我將給出例子地址捐寥,包含另一種實現(xiàn)方式就是用scrollview滑動到最底部的方式來實現(xiàn)平移效果~
github地址