【Android自定義View】 仿照騰訊漫畫自定義Toast的實現


效果展示:

話不多說,先上效果圖:



 看完了效果,我們來分析一下如何實現,並且實現這個我們能學到什么。

如何實現:

   仿照騰訊漫畫的效果,實現toast從頂部出現,又從頂部消失,代碼邏輯比較簡單。只需要用到Android的補間動畫。另外需要仿照原生的調用方法,一行代碼實現調用,降低開發者學習成本。

學習目的:

  主要是強調一個良好的封裝和適配,在寫這個控件的過程中充分理解Android補間動畫,以及自定義View的使用。另外需要仿照原生的調用方法,一行代碼實現調用。

代碼結構:

     代碼非常簡單,只需要兩個類,一個ToastLayout繼承自RelativeLayout,是一個自定義view,動畫的邏輯可以寫在里面。另外一個類則是ZToast,是暴露給開發者的類,里面有靜態的方法將提供給開發者使用。另外還需要一個布局文件,自定義view將會使用到layout文件。


代碼以及講解:

首先是布局文件,比較簡單,不做詳細說明:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_toast"
    android:background="#eb6056"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:layout_marginBottom="8dp"
            android:layout_alignParentBottom="true"
            android:id="@+id/iv_icon"
            android:layout_marginLeft="20dp"
            android:layout_centerVertical="true"
            android:src="@mipmap/icon_toast"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:layout_marginBottom="5dp"
            android:layout_alignParentBottom="true"
            android:id="@+id/tv_content"
            android:layout_marginLeft="10dp"
            android:textSize="14dp"
            android:layout_toRightOf="@id/iv_icon"
            android:text="內容"
            android:textColor="#ffffff"
            android:layout_centerVertical="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </RelativeLayout>
</RelativeLayout>

ToastLayout:

package com.zhhr.custom;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.zhhr.R;


/**
 * Created by 皓然 on 2017/8/3.
 */

public class ToastLayout extends RelativeLayout {
    private static final int  ANIMATION_TIME = 200;
    private TextView mContent;
    private View view;
    private boolean isShow;
    private RelativeLayout mWrapper;
    private ImageView mIcon;
    private int height;

    public boolean isShow() {
        return isShow;
    }

    public ToastLayout(Context context) {
        this(context, null);
    }

    public ToastLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ToastLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        view = LayoutInflater.from(getContext()).inflate(R.layout.layout_toast, null);
        addView(view);
        mContent =  view.findViewById(R.id.tv_content);
        mWrapper = view.findViewById(R.id.rl_toast);
        mIcon = view.findViewById(R.id.iv_icon);
        height = 60;
    }

    public void setTextColor(int color){
        mContent.setTextColor(color);
    }

    public void setBgColor(int color){
        mWrapper.setBackgroundColor(color);
    }

    public void setIconVisible(boolean isShow){
       if(isShow){
           mIcon.setVisibility(View.VISIBLE);
       }else {
           mIcon.setVisibility(View.GONE);
       }
    }

    public void setIcon(int resId){
        mIcon.setImageResource(resId);
    }

    public void setHeight(int height) {
        this.height = height;
    }



    public void setContent(String content){
        if(mContent!=null){
            mContent.setText(content);
        }
    }

    public void showToast(long time){
        AnimationSet animationSet = new AnimationSet(true);
        TranslateAnimation trans1 = new TranslateAnimation(0, 0 ,-dip2px(getContext(),height) ,0);
        TranslateAnimation trans2 = new TranslateAnimation(0,0 , 0 , -dip2px(getContext(),height));
        trans1.setDuration(ANIMATION_TIME);
        trans2.setStartOffset(ANIMATION_TIME+time);
        trans2.setDuration(ANIMATION_TIME);
        animationSet.addAnimation(trans1);
        animationSet.addAnimation(trans2);
        this.startAnimation(animationSet);
        animationSet.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                isShow = true;
                ToastLayout.this.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                isShow = false;
                ToastLayout.this.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }

    /**
     * 將dip或dp值轉換為px值,保證尺寸大小不變
     * @param context
     * @param dipValue
     * @return
     */
    public static int dip2px(Context context, float dipValue) {
        float density = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * density + 0.5f);
    }

}

主要在showToast方法里寫了兩個動畫,這里用到了Android的補間動畫,詳細的說明可以參考博客Android開發——View動畫、幀動畫和屬性動畫詳解,這里使用的是位移動畫TranslationAnimation,分別設置了進入動畫trans1和退出動畫trans2,再通過動畫的持續時間來控制toast的彈出和退出。

ZToast:

package com.zhhr.custom;

import android.app.Activity;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

import com.zhhr.R;


/**
 * Created by zhhr on 2018/3/27.
 */

public class ZToast {

    private Activity mActivity;
    private RelativeLayout mToastLayout;
    private ToastLayout mToast;
    private ViewGroup mView;
    private String text;
    private long times;
    private static ZToast mToastInstance;
    /**
     * 固定參數
     */
    public static final long LENGTH_LONG = 3000;
    public static final long LENGTH_SHORT = 1000;
    /**
     * 靜態可設置參數
     */
    public static int TextColor;
    public static int BgColor;
    public static boolean isShowIcon = true;
    public static int height = 60;
    public static int resId;

    /**
     * 設置小圖標
     * @param resId
     */
    public static void setResId(int resId) {
        ZToast.resId = resId;
    }

    /**
     * 設置高度
     * @param height
     */
    public static void setHeight(int height) {
        ZToast.height = height;
    }

    /**
     * 圖標是否顯示
     * @param isShowIcon
     */
    public static void setIsShowIcon(boolean isShowIcon) {
        ZToast.isShowIcon = isShowIcon;
    }

    /**
     * 背景色
     * @param bgColor
     */
    public static void setBgColor(int bgColor) {
        BgColor = bgColor;
    }

    /**
     * 文字顏色
     * @param textColor
     */
    public static void setTextColor(int textColor) {
        TextColor = textColor;
    }

    /**
     * 初始化
     * @param BgColor 背景顏色
     * @param TextColor 文字顏色
     * @param isIcon 圖標是否顯示
     * @param resId 圖標
     * @param height 高度
     */
    public static void init(int BgColor, int TextColor, boolean isIcon,int resId,int height){
        ZToast.BgColor = BgColor;
        ZToast.TextColor = TextColor;
        ZToast.isShowIcon = isIcon;
        ZToast.height = height;
        ZToast.resId = resId;
    }


    /**
     * 構造函數,上下文為activity
     * @param mActivity
     * @param text
     * @param times
     */
    public ZToast(Activity mActivity, String text, long times){
        this.mActivity = mActivity;
        this.text = text;
        this.times = times;
    }

    /**
     * 構造函數,上下文為View
     * @param mView
     * @param text
     * @param times
     */
    public ZToast(ViewGroup mView, String text, long times){
        this.mView = mView;
        this.text = text;
        this.times = times;
    }

    /**
     * 調用方法,上下文為activity
     * @param mActivity
     * @param text
     * @param times
     * @return
     */
    public static ZToast makeText(Activity mActivity, String text, long times){
        mToastInstance = new ZToast(mActivity,text,times);
        return mToastInstance;
    }

    /**
     * 調用方法,上下文為view
     * @param mView
     * @param text
     * @param times
     * @return
     */
    public static ZToast makeText(ViewGroup mView, String text, long times){
        mToastInstance = new ZToast(mView,text,times);
        return mToastInstance;
    }


    /**
     * 展示
     */
    public void show(){
        if(mActivity!=null){
            mToastLayout = (RelativeLayout) mActivity.findViewById(R.id.rl_toast);
            if(mToastLayout==null){//判斷是否已經添加進母VIEW里,沒有則添加進去
                mToast = new ToastLayout(mActivity);
                initToast(mToast);
                mActivity.addContentView(mToast,new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ToastLayout.dip2px(mActivity,height)));
            }else{//如果有,則直接取出
                mToast = (ToastLayout) mToastLayout.getParent();
            }
            mToast.setContent(text);
            mToast.showToast(times);
            return;
        }else if(mView!=null){
            mToastLayout = (RelativeLayout) mView.findViewById(R.id.rl_toast);
            if(mToastLayout==null){
                mToast = new ToastLayout(mView.getContext());
                initToast(mToast);
                mView.addView(mToast,new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ToastLayout.dip2px(mView.getContext(),height)));
            }else{
                mToast = (ToastLayout) mToastLayout.getParent();
            }
            mToast.setContent(text);
            mToast.showToast(times);
        }
    }

    /**
     * 設置各個參數
     * @param mToast
     */
    private void initToast(ToastLayout mToast) {
        if(TextColor!=0){
            mToast.setTextColor(TextColor);
        }
        if(BgColor!=0){
            mToast.setBgColor(BgColor);
        }
        if(resId!=0){
            mToast.setIcon(resId);
        }

        mToast.setIconVisible(isShowIcon);
        mToast.setHeight(height);
    }

    private boolean isShowToast(){
        if(mToast == null){
            return false;
        }
        return  mToast.isShow();
    }

    /**
     * 是否在展示
     * @return
     */
    public static boolean isShow(){
        if(mToastInstance == null){
            return false;
        }else{
            boolean isShow = mToastInstance.isShowToast();
            mToastInstance = null;
            return isShow;
        }
    }
}

     這個類主要是做了一些基本的封裝,我們要基本仿照原生toast的寫法,降低開發者的學習成本,所以采用ZToast.makeText(MainActivity.this,"文字",ZToast.LENGTH_SHORT).show(); 這樣的方法來進行實現。首先判斷上下文是activity還是自定義view,這里提供了兩套構造方法。如果是activity,則通過addContentView()的方法把我們剛剛寫完的自定義view添加進activity里;如果是view,則通過addview()方法把自定義view添加進去,再調用時候自定義view的showToast()方法來進行動畫的播放。

     還可以通過init方法和各種set方法來手動設置自定義view的屬性。

實現:

顯示toast

在activity中使用

ZToast.makeText(MainActivity.this,"文字",ZToast.LENGTH_SHORT).show();

在fragment中使用

ZToast.makeText(getActivity(), "文字",1000).show();

在自定義View中使用

ZToast.makeText((ViewGroup) getParent(),"文字"",1000).show();

設置toast的各個參數

在調用makeText方法之前調用init方法來設置參數


ZToast.init(Color.parseColor("#000000"),Color.parseColor("#ff00ff"),true,R.mipmap.item_pasue,90);//參數為 背景色 文字顏色 是否有圖標 圖標資源 高度

//也可以單獨設置參數,如高度
ZToast.setHeight(200);
//最后調用toast方法
ZToast.makeText(MainActivity.this,"點擊事件",ZToast.LENGTH_SHORT).show();

判斷是否正在顯示toast

ZToast.isShow()
當isShow()為true時,則說明正在顯示中,可以用來做雙擊退出

點擊兩下back按鍵退出可以這樣寫:

代碼如下:
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if((keyCode == KeyEvent.KEYCODE_BACK)){
        if(ZToast.isShow()){
            return super.onKeyDown(keyCode, event);
        }else{
            ZToast.makeText(MainActivity.this,"再按一次返回鍵退出",1000).show();
            return false;
        }
    }else{
        return super.onKeyDown(keyCode, event);
    }
}

PS:推薦使用無actionbar的主題和設置statusbar顏色

強烈建議搭配無actionbar的主題來使用

在style.xml中:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

最好再設置一下statusbar的顏色

在activity中:

Window window = getWindow();
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    window.setStatusBarColor(Color.TRANSPARENT);

總結:

    自定義view的使用已經仿照原生控件的封裝方式,極大的簡化了我們代碼,為項目中今后所有需要用到toast的地方都提供了完整的一套方案。掌握的並不是說代碼的技術含量,而是一種封裝的思路。

  完整的DEMO代碼已經提交到GitHub和Jcenter。GITHUB地址:ZToastDemo

   安裝方法:

    compile 'com.zhhr:ztoast:1.0.0'


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com