Android 登陸頁面仿拉鉤動效,你總會需要它!

nchok 7年前發布 | 11K 次閱讀 安卓開發 Android開發 移動開發

哈哈,看到這個標題是不是JH一緊,你可能會說我就沒遇到過,但是現在沒遇到不代表就遇不到,畢竟設計也是變幻莫測,只有你想不到的,沒有你不能實現的,說的這么吊,到底是啥效果?沒錯就是一個小小的登錄頁面,大家都有拉勾app吧,看拉勾的登錄頁做的很是平滑動畫,而且帶動畫效果,所以就有了類似拉勾登錄效果,如圖:

登錄動畫

github地址

雖然是個簡單的頁面,但是涵蓋的東西不算少啊,很納悶為何谷歌一直不提供簡單,方便,準確的鍵盤監聽事件?惆悵啊,所以我們只能自己從側面監聽鍵盤事件了,我們可以監聽最外層布局的變化來判斷鍵盤是不是彈起了。閑話不多說,上車吧。

布局文件,大家都能看懂吧。

<?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="聯系客服"
            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="關于我們"
            android:textColor="@color/color_b0b8b2"
            android:textSize="14dp" />
    </LinearLayout>

</RelativeLayout>

我們要想監聽鍵盤事件,首先我們想得到的是鍵盤彈起的時候我們可以去搞點事情,鍵盤搜起的時候我們再去搞點事情,知道這些還不夠,我們還要知道鍵盤彈起了多少,以及需要平移多少的距離。我們都知道我們的一個頁面彈起鍵盤的時候這個頁面的根布局會回調他的監聽方法:addOnLayoutChangeListener( );當鍵盤彈起的時候,我們的布局是變化了,因此會執行這個回調方法,但是前提是必須設置我們的Activity的windowSoftInputMode屬性為adjustResize。

我們想讓布局整體平移的距離也就是彈起時候處于最底部的view距離頂部的高度減去我們鍵盤的高度。現在認為只要控件將Activity向上推的高度超過了1/3屏幕高,就認為軟鍵盤彈起

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是改變前的左上右下坐標點值,沒有old的是改變后的左上右下坐標點值
      現在認為只要控件將Activity向上推的高度超過了1/3屏幕高,就認為軟鍵盤彈起*/
        if (oldBottom != 0 && bottom != 0 && (oldBottom - bottom > keyHeight)) {
            Log.e("wenzhihao", "up------>"+(oldBottom - bottom));
            if ((btn_login.getTop()-(oldBottom - bottom))>0){
                int dist = btn_login.getTop()-(oldBottom - bottom);
                ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(content, "translationY", 0.0f, -dist);
                mAnimatorTranslateY.setDuration(300);
                mAnimatorTranslateY.setInterpolator(new LinearInterpolator());
                mAnimatorTranslateY.start();
                service.setVisibility(View.INVISIBLE);
            }
            zoomIn(logo, (oldBottom - bottom) - keyHeight);
        } else if (oldBottom != 0 && bottom != 0 && (bottom - oldBottom > keyHeight)) {
            Log.e("wenzhihao", "down------>"+(bottom - oldBottom));
            ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(content, "translationY", content.getTranslationY(), 0);
            mAnimatorTranslateY.setDuration(300);
            mAnimatorTranslateY.setInterpolator(new LinearInterpolator());
            mAnimatorTranslateY.start();
            service.setVisibility(View.VISIBLE);
            //鍵盤收回后,logo恢復原來大小,位置同樣回到初始位置
            zoomOut(logo, (bottom - oldBottom) - keyHeight);
        }
    }
});
//btn_login是登錄按鈕

這樣我們發現是可以實現效果了,但是我想全屏顯示,懵比了,發現全屏的時候不回調這個方法了,怎么辦?又是查資料一看原來這個也是一個bug,但是有解決方案,AndroidBug5497Workaround。也是谷歌提供的?直接拷貝過來,會發現其實他的作用就是讓Activity最外層的根布局,當有布局變化時去響應這個變化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);
    }

}

使用方式,如果我們設置了全屏,就去加載它,不設置不管:

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恢復原來大小。這里用到的都是屬性動畫,只有屬性動畫我們才可以實現真正平移效果。

我看網上很多人使用addOnLayoutChangeListener()去監聽鍵盤事件,但是這個方法回調的太頻繁,比如本例特效,輸入框后面有文字時候顯示清除的圖標,如果用這個方法那么也會執行一次,可能會影響你的動畫,當然你也可以去記錄第一次的高度讓他不會走邏輯,但是我覺得也不是很靠譜,雖然我這個方法也不是很棒 ?乛?乛?~。

最后貼上源碼:

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是改變前的左上右下坐標點值,沒有old的是改變后的左上右下坐標點值
              現在認為只要控件將Activity向上推的高度超過了1/3屏幕高,就認為軟鍵盤彈起*/
                if (oldBottom != 0 && bottom != 0 && (oldBottom - bottom > keyHeight)) {
                    Log.e("wenzhihao", "up------>"+(oldBottom - bottom));
                    if ((btn_login.getTop()-(oldBottom - bottom))>0){
                        int dist = btn_login.getTop()-(oldBottom - bottom);
                        ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(content, "translationY", 0.0f, -dist);
                        mAnimatorTranslateY.setDuration(300);
                        mAnimatorTranslateY.setInterpolator(new LinearInterpolator());
                        mAnimatorTranslateY.start();
                        service.setVisibility(View.INVISIBLE);
                    }
                    zoomIn(logo, (oldBottom - bottom) - keyHeight);
                } else if (oldBottom != 0 && bottom != 0 && (bottom - oldBottom > keyHeight)) {
                    Log.e("wenzhihao", "down------>"+(bottom - oldBottom));
                    ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(content, "translationY", content.getTranslationY(), 0);
                    mAnimatorTranslateY.setDuration(300);
                    mAnimatorTranslateY.setInterpolator(new LinearInterpolator());
                    mAnimatorTranslateY.start();
                    service.setVisibility(View.VISIBLE);
                    //鍵盤收回后,logo恢復原來大小,位置同樣回到初始位置
                    zoomOut(logo, (bottom - oldBottom) - keyHeight);
                }
            }
        });
    }
    /**
     * 縮小
     * @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, float dist) {
        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;
        }
    }
}

如果有什么問題歡迎指出,我將給出例子地址,包含另一種實現方式就是用scrollview滑動到最底部的方式來實現平移效果~

 

項目主頁:http://www.baiduhome.net/lib/view/home/1490748738209

 本文由用戶 nchok 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!