android从源码解析并实现各种Toast效果合集

前言

安卓中为了给用户的友好提示,一般的表现形式为Dialog、PopWindow、Toast,自从Snackbar的出现绝对是秒杀Toast了,Snackbar不仅能够简单实现toast的效果而且还能setAction,但是还是有很多的应用是使用了Toast的,并且安卓死丢丢也有toast的插件,可见toast还是有他存在的价值。

看效果

csdn传图片不能太大,马蛋来2张好了:

从源码解读安卓的Toast

Toast窗口其实和前面分析的Activity、Dialog、PopWindow都是不同的,因为它和输入法、墙纸类似,都是系统窗口。

我们先看下Toast的静态makeText方法吧,如下:

public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
        //new一个Toast对象
        Toast result = new Toast(context);
        //获取前面有篇文章分析的LayoutInflater
        LayoutInflater inflate = (LayoutInflater)   context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //加载解析Toast的布局,实质transient_notification.xml是一个LinearLayout中套了一个@android:id/message的TextView而已
        View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
        //取出布局中的TextView
        TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
        //把我们的文字设置到TextView上
        tv.setText(text);
        //设置一些属性
        result.mNextView = v;
        result.mDuration = duration;
        //返回新建的Toast
        return result;
    }

可以看见,这个方法构造了一个Toast,然后把要显示的文本放到这个View的TextView中,然后初始化相关属性后返回这个新的Toast对象。

当我们有了这个Toast对象之后,可以通过show方法来显示出来,如下看下show方法源码:

    public void show() {
        ......
        //通过AIDL(Binder)通信拿到NotificationManagerService的服务访问接口,当前Toast类相当于上面例子的客户端!!!相当重要!!!
        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
            //把TN对象和一些参数传递到远程NotificationManagerService中去
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }

继续来看Toast中的show方法的service.enqueueToast(pkg, tn, mDuration);语句,service实质是远程的NotificationManagerService,所以enqueueToast方法就是 NotificationManagerService类的,如下:

 private final IBinder mService = new INotificationManager.Stub() {
        @Override
        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
        {
            ......
            synchronized (mToastQueue) {
                int callingPid = Binder.getCallingPid();
                long callingId = Binder.clearCallingIdentity();
                try {
                    ToastRecord record;
                    //查看该Toast是否已经在队列当中
                    int index = indexOfToastLocked(pkg, callback);
                    // If it‘s already in the queue, we update it in place, we don‘t
                    // move it to the end of the queue.
                    //注释说了,已经存在则直接取出update
                    if (index >= 0) {
                        record = mToastQueue.get(index);
                        record.update(duration);
                    } else {
                        // Limit the number of toasts that any given package except the android
                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
                        ......
                        //将Toast封装成ToastRecord对象,放入mToastQueue中
                        record = new ToastRecord(callingPid, pkg, callback, duration);
                        //把他添加到ToastQueue队列中
                        mToastQueue.add(record);
                        index = mToastQueue.size() - 1;
                        //将当前Toast所在的进程设置为前台进程
keepProcessAliveLocked(callingPid);
                    }
                    //如果index为0,说明当前入队的Toast在队头,需要调用showNextToastLocked方法直接显示
                    if (index == 0) {
                        showNextToastLocked();
                    }
                } finally {
                    Binder.restoreCallingIdentity(callingId);
                }
            }
        }
   }

不多看了,我们知道Toast都是加入到一个enqueueToast,然后通过handle来处理,因为是系统的窗口,所以我们换个界面然后toast还没有完你也不要感到奇怪。

在开发应用程序时使用Toast注意事项:

  • 通过分析TN类的handler可以发现,如果想在非UI线程使用Toast需要自行声明Looper,否则运行会抛出Looper相关的异常;UI线程不需要,因为系统已经帮忙声明。
  • 在使用Toast时context参数尽量使用getApplicationContext(),可以有效的防止静态引用导致的内存泄漏。
  • 有时候我们会发现Toast弹出过多就会延迟显示,因为上面源码分析可以看见Toast.makeText是一个静态工厂方法,每次调用这 个方法都会产生一个新的Toast对象,当我们在这个新new的对象上调用show方法就会使这个对象加入到 NotificationManagerService管理的mToastQueue消息显示队列里排队等候显示;所以如果我们不每次都产生一个新的 Toast对象(使用单例来处理)就不需要排队,也就能及时更新了。

自定义Toast

package com.losileeya.toastmaster.view;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Toast;

import com.losileeya.toastmaster.R;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 超级自定义的Toast,单例模式获取本类
 */
public class SuperCustomToast {
    /**
     * 获取当前Android系统版本
     */
    static int currentapiVersion = Build.VERSION.SDK_INT;

    /**
     * 入场动画持续时间
     */
    private static final int TIME_START_ANIM = 500;
    /**
     * 离场动画持续时间
     */
    private static final int TIME_END_ANIM = 500;
    /**
     * 每条Toast显示持续时间
     */
    private static final int TIME_DURATION = 2500;
    /**
     * UI线程句柄
     */
    Handler mHandler;

    /**
     * 内容对象
     */
    Context mContext;

    /**
     * 顶层布局
     */
    LinearLayout mTopView, mTopView2;

    /**
     * 内容布局
     */
    public LinearLayout mView;

    /**
     * 布局属性
     */
    LayoutParams lp_WW, lp_MM;

    /**
     * 屏幕宽度
     */
    int screenWidth;
    /**
     * 屏幕高度
     */
    int screenHeight;

    /**
     * 默认背景的resid
     */
    Integer defaultBackgroundResid;

    /**
     * 默认背景的颜色
     */
    Drawable defaultBackgroundColor;

    /**
     * 默认文字颜色
     */
    int defaultTextColor;
    private View layout;
    /**
     * 反射过程中是否出现异常的标志
     */
    boolean hasReflectException = false;

    /**
     * 单例
     */
    private static SuperCustomToast instance;

    /**
     * 获得单例
     *
     * @param context
     * @return
     */
    public static SuperCustomToast getInstance(Context context) {
        if (instance == null) {
            instance = new SuperCustomToast(context);
        }
        return instance;
    }

    private SuperCustomToast(Context context) {
        if (context == null || context.getApplicationContext() == null) {
            throw new NullPointerException("context can‘t be null");
        }
        mContext = context.getApplicationContext();
        initView();
        initTN();

        // 防反射获取实例
        if (instance != null)
            throw new NullPointerException("error");
    }

    /**
     * 初始化视图控件
     */
    public void initView() {
        mHandler = new Handler(mContext.getMainLooper());

        lp_WW = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT);
        lp_MM = new LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT);
         //获取屏幕密度
        DisplayMetrics mDisplayMetrics = mContext.getResources()
                .getDisplayMetrics();
                //获取屏幕宽高
        screenWidth = mDisplayMetrics.widthPixels;
        screenHeight = mDisplayMetrics.heightPixels;

        mTopView = new LinearLayout(mContext);
        mTopView.setLayoutParams(lp_MM);
        mTopView.setOrientation(LinearLayout.VERTICAL);
        mTopView.setGravity(Gravity.CENTER);

        mTopView2 = new LinearLayout(mContext);
        LayoutParams params = new LayoutParams(screenWidth, screenHeight);
        mTopView2.setLayoutParams(params);
        mTopView2.setOrientation(LinearLayout.VERTICAL);
        mTopView2.setGravity(Gravity.BOTTOM);

        mView = new LinearLayout(mContext);
        mView.setLayoutParams(lp_MM);
        mView.setOrientation(LinearLayout.VERTICAL);
        mView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);

        View gapView = new View(mContext);
        gapView.setLayoutParams(new LayoutParams(screenWidth, screenHeight / 4));
        mView.addView(gapView);

        mTopView.addView(mTopView2);
        mTopView2.addView(mView);

        resetDefaultBackgroundAndTextColor();
    }

    /**
     * 显示一条Toast
     * layoutId 自定义布局
     * @param msg
     *            消息内容
     */
    public void show(String msg, int layoutId, int rootId, Activity activity) {
        LayoutInflater inflater = activity.getLayoutInflater();
        layout = inflater.inflate(layoutId,
                (ViewGroup) activity.findViewById(rootId));
        layout.setLayoutParams(lp_WW);
        TextView title = (TextView) layout.findViewById(R.id.title);
        title.setText(msg);
        title.setTextColor(defaultTextColor);
        show(layout, null, null, null);
    }

    /**
     * 显示一条Toast
     *
     * @param msg
     *            消息内容
     */
    public void show(String msg) {
        show(getTextView(msg), null, null, null);
    }

    /**
     * 显示一条Toast
     *
     * @param v
     *            消息内容
     */
    public void show(View v) {
        show(v, null, null, null);
    }

    /**
     * 显示一条Toast
     *
     * @param msg
     *            消息内容
     * @param duration
     *            持续时间,单位为毫秒
     */
    public void show(String msg, long duration) {
        show(getTextView(msg), duration, null, null);
    }

    /**
     * 一个toast已经显示,在下一个msg相同的情况下, 一定时间不显示新的toast. 如果同样的消息,在两次显示时间差
     * 大于duration的情况下,才出现新的toast
     *
     * @param msg
     *            消息内容
     * @param duration
     *            持续时间,单位为毫秒
     */
    Long totalTime;

    public void setTime(Long time) {
        this.totalTime = time;
    }

    private boolean firstCalled = true;
    String lastMsg;
    long startTime;

    public void showSameMsg(String msg, long duration) {
        if (firstCalled) {
            lastMsg = msg;
            show(getTextView(msg), duration, null, null);
            firstCalled = false;
            // 第一次调用的时间
            startTime = (new Date()).getTime();
        } else if (msg.equals(lastMsg)) {
            long endTime = (new Date()).getTime();
            long totalTime = endTime - startTime;
            Log.e("time", "开始时间:" + startTime + "  结束时间: " + endTime + "  总时间"
                    + totalTime);
            // 第二次相同
            if (totalTime > duration) {
                show(getTextView(msg), duration, null, null);
                startTime = endTime;
            }
        }
    }

    /**
     * 显示一条Toast
     *
     * @param v
     *            消息内容
     * @param duration
     *            持续时间,单位为毫秒
     */
    public void show(View v, long duration) {
        show(v, duration, null, null);
    }

    /**
     * 显示一条Toast
     *
     * @param msg
     *            消息内容
     * @param duration
     *            持续时间,单位为毫秒
     * @param startAnim
     *            入场动画
     * @param endAnim
     *            离场动画
     */
    public void show(String msg, Long duration, Animation startAnim,
            Animation endAnim) {
        show(getTextView(msg), duration, startAnim, endAnim);
    }

    /**
     * 显示一条图文并存Toast
     *
     * @param resid
     *            图片资源
     * @param msg
     *            消息内容
     * @param duration
     *            持续时间,单位为毫秒
     * @param startAnim
     *            入场动画
     * @param endAnim
     *            离场动画
     */
    public void show(int resid, String msg, Long duration, Animation startAnim,
            Animation endAnim) {
        show(getView(resid, msg), duration, null, null);
    }

    /**
     * 显示一条Toast
     *
     * @param v
     *            显示的内容
     * @param duration
     *            持续时间,单位为毫秒
     * @param startAnim
     *            入场动画
     * @param endAnim
     *            离场动画
     */
    public final void show(final View v, Long duration, Animation startAnim,
            final Animation endAnim) {
        // 反射过程异常时则使用源生Toast
        if (hasReflectException) {
            Toast t = new Toast(mContext);
            t.setView(v);
            t.setDuration(Toast.LENGTH_SHORT);
            t.show();
            // 重新获取反射对象
            initTN();
            return;
        }

        // 显示顶层容器控件
        if (mView.getChildCount() == 1)
            showToast();

        // 获得入场动画
        if (startAnim == null) {
            startAnim = getStartAnimation();
        }
        v.clearAnimation();
        v.startAnimation(startAnim);
        // 把传入的toast显示出来
        mView.addView(v, 0);

        // 延迟后隐藏传入toast
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                hide(v, endAnim);
            }
        }, duration == null ? TIME_DURATION : duration);

    }

    /**
     * 设置默认背景颜色
     *
     * @param color
     *            颜色值
     * @param alpha
     *            透明度
     */
    public void setDefaultBackgroundColor(int color, Integer alpha) {
        defaultBackgroundColor = new ColorDrawable(color);
        if (alpha != null)
            defaultBackgroundColor.setAlpha(alpha);
        defaultBackgroundResid = null;
    }

    /**
     * 设置默认背景资源
     *
     * @param resid
     *            图片资源文件
     */
    public void setDefaultBackgroundResource(int resid) {
        defaultBackgroundResid = resid;
    }

    /**
     * 设置默认文字颜色
     *
     * @param color
     */
    public void setDefaultTextColor(int color) {
        defaultTextColor = color;
    }

    /**
     * 重置背景和文字颜色
     */
    public void resetDefaultBackgroundAndTextColor() {
        defaultTextColor = Color.WHITE;
        defaultBackgroundColor = new ColorDrawable(Color.BLACK);
        defaultBackgroundColor.setAlpha(200);
        defaultBackgroundResid = null;
    }

    /**
     * 隐藏指定控件
     *
     * @param v
     *            需要隐藏的控件
     * @param endAnim
     *            结束动画
     */
    public final void hide(final View v, Animation endAnim) {
        if (v == null || mView.indexOfChild(v) < 0)
            return;
        // 获得出场动画
        if (endAnim == null)
            endAnim = getEndAnimation();
        v.clearAnimation();
        // 开始出场动画
        v.startAnimation(endAnim);

        // 动画结束后移除控件
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (v == null || mView.indexOfChild(v) < 0)
                    return;
                // 移除指定控件
                mView.removeView(v);
                // 隐藏顶层容器控件
                if (mView.getChildCount() == 1)
                    hideToast();
            }
        }, TIME_END_ANIM);
    }

    /**
     * 获得一个设置好属性的TextView
     *
     * @param msg
     * @return
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public TextView getTextView(String msg) {
        TextView tv = new TextView(mContext);
        tv.setLayoutParams(lp_WW);
        tv.setText(msg);
        tv.setTextColor(defaultTextColor);
        Drawable background = null;
        if (defaultBackgroundResid != null) {
            background = mContext.getResources().getDrawable(
                    defaultBackgroundResid);
        } else {
            background = defaultBackgroundColor;
        }
        if (currentapiVersion > 10)
            tv.setBackground(background);
        else
            tv.setBackgroundDrawable(background);
        tv.setPadding(5, 5, 5, 5);
        tv.setGravity(Gravity.CENTER);
        return tv;
    }

    /**
     * 获得一图片或图文并存的toast
     *
     * @param msg
     * @return
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public LinearLayout getView(int resId, String msg) {
        LinearLayout layout = new LinearLayout(mContext);
        layout.setLayoutParams(new LayoutParams(
                LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT));
        layout.setOrientation(LinearLayout.VERTICAL);
        layout.setGravity(Gravity.CENTER);
        layout.setPadding(10, 10, 10, 10);
        ImageView imageView = new ImageView(mContext);
        imageView.setLayoutParams(lp_WW);
        imageView.setImageResource(resId);
        layout.addView(imageView);
        if(!TextUtils.isEmpty(msg)){
            TextView tv = new TextView(mContext);
            tv.setLayoutParams(lp_WW);
            tv.setText(msg);
            tv.setTextColor(defaultTextColor);
            tv.setPadding(5, 5, 5, 5);
            tv.setGravity(Gravity.CENTER);
            layout.addView(tv);
        }

        Drawable background = null;
        if (defaultBackgroundResid != null) {
            background = mContext.getResources().getDrawable(
                    defaultBackgroundResid);
        } else {
            background = defaultBackgroundColor;
        }
        if (currentapiVersion > 10)
            layout.setBackground(background);
        else
            layout.setBackgroundDrawable(background);

        return layout;
    }
    /**
     * 获得入场动画
     *
     * @return
     */
    protected Animation getStartAnimation() {
        AlphaAnimation animAlpha = new AlphaAnimation(0, 1);
        animAlpha.setDuration(TIME_START_ANIM);
        animAlpha.setFillAfter(true);
        TranslateAnimation animTrans = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT, 1.5f,
                Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT,
                0f, Animation.RELATIVE_TO_PARENT, 0f);
        animTrans.setDuration(TIME_START_ANIM);
        animTrans.setFillAfter(true);
        animTrans.setInterpolator(new DecelerateInterpolator());

        AnimationSet sets = new AnimationSet(true);
        sets.addAnimation(animAlpha);
        sets.addAnimation(animTrans);

        return sets;
    }

    /**
     * 获得离场动画
     *
     * @return
     */
    protected Animation getEndAnimation() {
        AlphaAnimation animAlpha = new AlphaAnimation(1, 0);
        animAlpha.setDuration(TIME_END_ANIM);
        animAlpha.setFillAfter(true);

        TranslateAnimation animTrans = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT,
                -1.5f, Animation.RELATIVE_TO_PARENT, 0f,
                Animation.RELATIVE_TO_PARENT, 0f);
        animTrans.setDuration(TIME_END_ANIM);
        animTrans.setFillAfter(true);
        animTrans.setInterpolator(new AccelerateInterpolator());
        AnimationSet sets = new AnimationSet(true);
        sets.addAnimation(animAlpha);
        sets.addAnimation(animTrans);
        return sets;
    }

    /* 以下为反射相关内容 */
    Toast mToast;
    Field mTN;
    Object mObj;
    Method showMethod, hideMethod;

    /**
     * 通过反射获得mTN下的show和hide方法
     */
    private void initTN() {
        mToast = new Toast(mContext);
        mToast.setView(mTopView);
        Class<Toast> clazz = Toast.class;
        try {
            mTN = clazz.getDeclaredField("mTN");
            mTN.setAccessible(true);
            mObj = mTN.get(mToast);
            showMethod = mObj.getClass().getDeclaredMethod("show",
                    new Class<?>[0]);
            hideMethod = mObj.getClass().getDeclaredMethod("hide",
                    new Class<?>[0]);
            hasReflectException = false;
        } catch (NoSuchFieldException e) {
            hasReflectException = true;
            System.out.println(e.getMessage());
        } catch (IllegalAccessException e) {
            hasReflectException = true;
            System.out.println(e.getMessage());
        } catch (IllegalArgumentException e) {
            hasReflectException = true;
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            hasReflectException = true;
            System.out.println(e.getMessage());
        }
    }

    /**
     * 通过反射获得的show方法显示指定View
     */
    private void showToast() {
        try {
            // 高版本需要再次手动设置mNextView属性
            if (currentapiVersion > 10) {
                Field mNextView = mObj.getClass().getDeclaredField("mNextView");
                mNextView.setAccessible(true);
                mNextView.set(mObj, mTopView);
            }
            showMethod.invoke(mObj, new Object[0]);
            hasReflectException = false;
        } catch (Exception e) {
            hasReflectException = true;
            System.out.println(e.getMessage());
        }
    }

    /**
     * 通过反射获得的hide方法隐藏指定View
     */
    public void hideToast() {
        try {
            hideMethod.invoke(mObj, new Object[0]);
            hasReflectException = false;
        } catch (Exception e) {
            hasReflectException = true;
            System.out.println(e.getMessage());
        }
    }
    public void removeView() {

    }
}

从上面的代码可以看出:里面反射来创建Toast,通过反射来获得mTN下的show和hide方法来控制toast的show和hide,然后获得handle来处理ui的刷新,代码通过一些参数来设置toast,比如显示的文字,背景颜色,背景图片,显示的图片和时长。

然后就是activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        android:gradientRadius="80%p"
        android:stateListAnimator="@drawable/touch_raise"
        app:cardBackgroundColor="#303069"
        app:cardCornerRadius="4dp"
        app:cardElevation="5dp"
        app:cardMaxElevation="10dp"
        app:cardPreventCornerOverlap="true"
        app:cardUseCompatPadding="true"
        >

        <TextView
            android:id="@+id/showToast1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="5dp"
            android:text="默认Toast"
            android:textColor="#ffffff"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        android:gradientRadius="80%p"
        android:stateListAnimator="@drawable/touch_raise"
        app:cardBackgroundColor="#303069"
        app:cardCornerRadius="4dp"
        app:cardElevation="5dp"
        app:cardMaxElevation="10dp"
        app:cardPreventCornerOverlap="true"
        app:cardUseCompatPadding="true">

        <TextView
            android:id="@+id/showToast2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="5dp"
            android:text="自定义5秒"
            android:textColor="#ffffff"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        android:gradientRadius="80%p"
        android:stateListAnimator="@drawable/touch_raise"
        app:cardBackgroundColor="#303069"
        app:cardCornerRadius="4dp"
        app:cardElevation="5dp"
        app:cardMaxElevation="10dp"
        app:cardPreventCornerOverlap="true"
        app:cardUseCompatPadding="true">

        <TextView
            android:id="@+id/showToast3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="5dp"
            android:text="背景+文字"
            android:textColor="#ffffff"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        android:gradientRadius="80%p"
        android:stateListAnimator="@drawable/touch_raise"
        app:cardBackgroundColor="#303069"
        app:cardCornerRadius="4dp"
        app:cardElevation="5dp"
        app:cardMaxElevation="10dp"
        app:cardPreventCornerOverlap="true"
        app:cardUseCompatPadding="true">

        <TextView
            android:id="@+id/showToast4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="5dp"
            android:text="背景为图片"
            android:textColor="#ffffff"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        android:gradientRadius="80%p"
        android:stateListAnimator="@drawable/touch_raise"
        app:cardBackgroundColor="#303069"
        app:cardCornerRadius="4dp"
        app:cardElevation="5dp"
        app:cardMaxElevation="10dp"
        app:cardPreventCornerOverlap="true"
        app:cardUseCompatPadding="true">

        <TextView
            android:id="@+id/showToast5"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="5dp"
            android:text="自定义动画"
            android:textColor="#ffffff"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        android:gradientRadius="80%p"
        android:stateListAnimator="@drawable/touch_raise"
        app:cardBackgroundColor="#303069"
        app:cardCornerRadius="4dp"
        app:cardElevation="5dp"
        app:cardMaxElevation="10dp"
        app:cardPreventCornerOverlap="true"
        app:cardUseCompatPadding="true">

        <TextView
            android:id="@+id/showToast6"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="5dp"
            android:text="显示图片"
            android:textColor="#ffffff"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        android:gradientRadius="80%p"
        android:stateListAnimator="@drawable/touch_raise"
        app:cardBackgroundColor="#303069"
        app:cardCornerRadius="4dp"
        app:cardElevation="5dp"
        app:cardMaxElevation="10dp"
        app:cardPreventCornerOverlap="true"
        app:cardUseCompatPadding="true">

        <TextView
            android:id="@+id/showToast7"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="5dp"
            android:text="图文共存"
            android:textColor="#ffffff"/>

    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:clickable="true"
         android:foreground="?android:attr/selectableItemBackground"
         android:stateListAnimator="@drawable/touch_raise"
         app:cardBackgroundColor="#303069"
         app:cardCornerRadius="4dp"
         app:cardElevation="5dp"
         app:cardMaxElevation="10dp"
         app:cardPreventCornerOverlap="true"
         app:cardUseCompatPadding="true"
         android:gradientRadius="80%p">

         <TextView
             android:id="@+id/showToast8"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center"
             android:padding="5dp"
             android:text="自定义布局"
             android:textColor="#ffffff"/>
     </android.support.v7.widget.CardView>
</LinearLayout>

为了效果好看点使用CardView,也有点阴影和动画效果。

接下来就是代码的示例使用了:

package com.losileeya.toastmaster;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.TextView;
import com.losileeya.toastmaster.view.SuperCustomToast;
import java.lang.ref.WeakReference;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
    SuperCustomToast toast;//自定义Toast
    int i = 0;
    final StringBuffer sb = new StringBuffer("默认Toast");
    final String info = "默认Toast-";
    final String sameString = "相同信息Toast";
    Handler mHandler;
    @BindView(R.id.showToast1)
    TextView showToast1;
    @BindView(R.id.showToast2)
    TextView showToast2;
    @BindView(R.id.showToast3)
    TextView showToast3;
    @BindView(R.id.showToast4)
    TextView showToast4;
    @BindView(R.id.showToast5)
    TextView showToast5;
    @BindView(R.id.showToast6)
    TextView showToast6;
    @BindView(R.id.showToast7)
    TextView showToast7;
    @BindView(R.id.showToast8)
    TextView showToast8;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //绑定视图
        ButterKnife.bind(MainActivity.this);
        //初始化handle
        mHandler = new MyHandler(this);
        //获取自定义toast的实例
        toast = SuperCustomToast.getInstance(getApplicationContext());

    }

    /**
     *  入场动画
     */
    private void showAnimate() {

        // 旋转
        RotateAnimation rAnim = new RotateAnimation(0, 720,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        rAnim.setDuration(500);
        rAnim.setFillAfter(true);
        // 缩放
        ScaleAnimation sAnim = new ScaleAnimation(0, 1, 0, 1,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        sAnim.setDuration(500);
        sAnim.setFillAfter(true);
        // 透明度
        AlphaAnimation aAnim1 = new AlphaAnimation(0, 1);
        aAnim1.setDuration(500);
        aAnim1.setFillAfter(true);
        AnimationSet startAnim = new AnimationSet(false);
        startAnim.addAnimation(rAnim);
        startAnim.addAnimation(aAnim1);
        startAnim.addAnimation(sAnim);
        // 移动
        TranslateAnimation animTrans = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT, 0f,
                Animation.RELATIVE_TO_PARENT, 0f,
                Animation.RELATIVE_TO_SELF, 0f,
                Animation.RELATIVE_TO_SELF, 1f);
        animTrans.setDuration(500);
        animTrans.setFillAfter(true);
        // 透明度
        AlphaAnimation aAnim2 = new AlphaAnimation(1, 0);
        aAnim2.setDuration(500);
        aAnim2.setFillAfter(true);
        AnimationSet endAnim = new AnimationSet(false);
        endAnim.addAnimation(animTrans);
        endAnim.addAnimation(aAnim2);
        toast.show("自定义动画的Toast-" + i++, null, startAnim, endAnim);
    }

    private static class MyHandler extends Handler {
        private WeakReference<MainActivity> activityWeakReference;

        public MyHandler(MainActivity activity) {
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = activityWeakReference.get();
            if (activity != null) {

            }
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        toast.hideToast();
        toast.mView.removeAllViews();
        toast.initView();
    }

    @OnClick({R.id.showToast1, R.id.showToast2, R.id.showToast3, R.id.showToast4, R.id.showToast5, R.id.showToast6, R.id.showToast7,R.id.showToast8})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.showToast1:
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //默认toast
                        toast.show(info + i++);
                    }
                });

                break;
            case R.id.showToast2:
                // 持续5秒的toast
                toast.showSameMsg(sameString, 5000);
                break;
            case R.id.showToast3:
                //带背景色Toast
                toast.setDefaultBackgroundColor(Color.RED, 200);//Toast背景
                toast.setDefaultTextColor(Color.BLUE);//文字颜色
                toast.show("带有背景色Toast");
                break;
            case R.id.showToast4:
                //给Toast添加背景图片
                toast.setDefaultBackgroundResource(R.drawable.bg);
                toast.setDefaultTextColor(Color.BLACK);
                toast.show("背景图片的Toast");
                break;
            case R.id.showToast5:
                //给Toast加动画
                showAnimate();

                break;
            case R.id.showToast6:
                //带图片的Toast
                toast.setDefaultBackgroundResource(R.drawable.frame_bg_theme_light);
                toast.show(R.drawable.tips_smile, "", null, null, null);
                break;
            case R.id.showToast7:
                //图文混合的toast
                toast.setDefaultTextColor(Color.RED);
                toast.setDefaultBackgroundResource(R.drawable.frame_bg_theme_light);
                toast.show(R.drawable.tips_smile, "我笑了", null, null, null);
                break;
            case R.id.showToast8:
                //自定义布局Toast
                toast.setDefaultTextColor(Color.RED);
                toast.show(info + i++, R.layout.super_toast_theme_light,
                        R.id.content_toast, MainActivity.this);
                break;
        }
    }
}

上面的代码呢就是对8种toast的效果的展示,基本上也能适合开发的所有场景了,实在不行的话那么你就使用snackbar咯,效果或许不好,但是也还是能学到一些东西的。

注意:

由于使用的butterknife5.0.1我手贱更新到了8.0.1,差点被坑了

我是这样做的:

compile ‘com.jakewharton:butterknife:8.0.1’

然后代码也能使用:@BindView,@OnClick特么怎么出鬼了,提示unused(木有使用) ,木有办法只能去github上了,然后改成这样

apply plugin: ‘com.neenbedankt.android-apt‘
dependencies {
     compile ‘com.jakewharton:butterknife:8.0.1‘
     apt ‘com.jakewharton:butterknife-compiler:8.0.1‘
   }  

然后工程的也要改:

buildscript {
      repositories {
         jcenter()
    }
    dependencies {
        classpath ‘com.android.tools.build:gradle:2.0.0‘
        classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8‘
    }
}

总结

对toast的源码翻出来一部分,然后跟我们一般的toast的封装还是有些不同,当然封装一下toast对开发的效率还是能够提高的 ,代码中用到了动画有温习了一下,学习就是这样从难到易。

好了,所有的讲解就到这里了,能力有限见丑了,就到这里。感觉不错麻烦帮顶一下,赞一个,哪里有问题指出来。

demo 传送门:ToastMaster

时间: 2024-10-08 15:56:30

android从源码解析并实现各种Toast效果合集的相关文章

Android xUtils3源码解析之数据库模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 配置数据库 DbManager.DaoConfig daoConfig = new DbManager.DaoConfig() .setDbName("test.db") .setDbVersion(1) .setDbOpenListe

Android xUtils3源码解析之注解模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 初始化 public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { su

Android设计模式源码解析之观察者模式

Android设计模式源码解析之观察者模式 本文为 Android 设计模式源码解析 中 观察者模式 分析 Android系统版本: 2.3 分析者:Mr.Simple,分析状态:未完成,校对者:Mr.Simple,校对状态:未开始 1. 模式介绍 模式的定义 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新. 模式的使用场景 关联行为场景.需要注意的是,关联行为是可拆分的,而不是"组合"关系: 事件多级触发场景: 跨系统的消息交换

Android -- AsyncTask源码解析

1,前段时间换工作的时候,关于AsyncTask源码这个点基本上大一点的公司都会问,所以今天就和大家一起来总结总结.本来早就想写这篇文章的,当时写<Android -- 从源码解析Handle+Looper+MessageQueue机制>的时候就是想为这篇文章做铺垫的,因为AsyncTask说里面还是使用的handle,所以先就写了handle这一篇.记得15年底去美团面试的时候,面试官就问我既然存在handle为什么google还要出AsyncTask(毕竟底层还是用的handle+Exec

Android EventBus源码解析, 带你深入理解EventBus

上一篇带大家初步了解了EventBus的使用方式,详见:Android EventBus实战 没听过你就out了,本篇博客将解析EventBus的源码,相信能够让大家深入理解该框架的实现,也能解决很多在使用中的疑问:为什么可以这么做?为什么这么做不好呢? 1.概述 一般使用EventBus的组件类,类似下面这种方式: [java] view plain copy public class SampleComponent extends Fragment { @Override public vo

Android AsyncTask 源码解析

1. 官方介绍 public abstract class AsyncTask extends Object  java.lang.Object    ? android.os.AsyncTask<Params, Progress, Result> AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish resul

Android xUtils3源码解析之图片模块

初始化 x.Ext.init(this); public static void init(Application app) { TaskControllerImpl.registerInstance(); if (Ext.app == null) { Ext.app = app; } } public final class TaskControllerImpl implements TaskController { public static void registerInstance()

Android EventBus源码解析 带你深入理解EventBus

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus的使用方式,详见:Android EventBus实战 没听过你就out了,本篇博客将解析EventBus的源码,相信能够让大家深入理解该框架的实现,也能解决很多在使用中的疑问:为什么可以这么做?为什么这么做不好呢? 1.概述 一般使用EventBus的组件类,类似下面这种方式: public cl

Android setContentView()源码解析

前言 在Activity中一般第一句就是调用setContentView(R.layout.XXX),但这其中系统做了那些工作? 我们知道,在ClassLoader装载了MainActivity之后,首先创建了Application,之后依次调用Application对象的onAttach和onCreate()方法.然后顺序调用第一个Activity的onAttach和onCreate()方法.大概有个印象即可,后文会涉及到.具体参考:Launcher启动应用程序流程源码解析. 新建测试工程Te