写在前面
对于Toast的使用,相信只要是使用过android的童鞋都不会陌生,它是不需要和用户进行交互的一个提示框。接下来,让我们一步步自定义Toast,全方位的玩转Toast,实现它的不同显示需求。从此再也不怕提示的各种变态需求。~
先来看看效果图,苦逼的华为手机,4.4版本,没root,只能连上电脑,再通过录制电脑屏幕上的手机画面录屏,求推荐好方法录屏。~

1.最基本的Toast
Toast.makeText(getApplicationContext(),"最基本的Toast",Toast.LENGTH_SHORT).show();
1
这个不用多讲。

2.自定义位置的Toast
通过Toast类自带的定义位置的方法来设置toast出现的位置。

Toast toast=Toast.makeText(getApplicationContext(),"自定义位置的Toast",Toast.LENGTH_SHORT);
 /**
 *Toast.setGravity(gravity,xOffset,yOffset);
 *@gravity:toast的位置
 *@xOffset:相对于gravity x方向上的偏移量。yOffset:相对于gravity y方向的偏移量。
 */
                toast.setGravity(Gravity.LEFT,50,0);
                toast.show();

3.带图片的toast
讲了前面的两种最基本的Toast,我们现在先来看看toast.markText的源码,看看toast到底是如何显示的。
源码位置:frameworks/base/core/java/Android/widght/Toast.java (Toast#makeText())
其中要增加的view的布局方式在:frameworks/base/core/res/res/layout/transient_notification.xml。(里面只有一个TextView没啥讲的。)

public static Toast makeText(Context context, CharSequence text, int duration) {
        Toast result = new Toast(context);
        LayoutInflater inflate = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
        TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
        tv.setText(text);
        result.mNextView = v;
        result.mDuration = duration;
        return result;
    }

从上面源码可以得到的东西:1.new一个Toast类,获得当前的context;2.LayoutInflater,动态载入layout的类,将view的布局载入。3.获得view中的textview中的文字信息。4.设置toast的view和duration属性。5.返回toast.从而实现了toast的markText方法。
ok!,搞懂了toast.markText这部分的源码,我们就可以设置自定义view的toast.
1.创建一个custom_toast.xml,一个LinearLayout,水平方向,ImageView+TextView。设置它们的各种属性。(代码很简单,就不贴了。)
2.自定义Toast.直接上代码

Toast customToast = new Toast(MainActivity.this.getApplicationContext());
 //获得view的布局
                View customView = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_toast,null);
                ImageView img = (ImageView) customView.findViewById(R.id.iv);
                TextView tv = (TextView) customView.findViewById(R.id.tv);
                //设置ImageView的图片
                img.setBackgroundResource(R.drawable.ab);
                //设置textView中的文字
                tv.setText("我是带图片的自定义位置的toast");
                //设置toast的View,Duration,Gravity最后显示
                customToast.setView(customView);
                customToast.setDuration(Toast.LENGTH_SHORT);
                customToast.setGravity(Gravity.CENTER,0,0);
                customToast.show();

4.自定义View带动画超高级的Toast.
其实,这个就是3.带图片的toast的加强版。将里面其中的图片,改换成我们自定义的view,通过自定义view,来实现多种多样的Toast.
1.创建自定义的view.CustomToastView继承View.
整体的Custom的结构(下文会有具体实现代码):

public class CustomToastView extends View {
   //a.初始化其中的一些变量。
   ......
   //a.实现CustomToastView的3个构造函数
   ......
   //b.初始化画笔的参数和矩形参数
   .....

}

a.初始化其中的一些变量,实现3个构造函数。

public class CustomToastView extends View {
     //矩形,设置toast布局时用
    RectF rectF =new RectF();
    //属性动画
    ValueAnimator valueAnimator;
    float mAnimatedValue = 0f;
    //自定义view的画笔
    private Paint mPaint;

private float mWidth = 0f; //view的宽
    private float mEyeWidth = 0f; //笑脸的眼睛半径
    private float mPadding = 0f;  //view的偏移量。
    private float endAngle = 0f; //圆弧结束的度数

//是左眼还是右眼
    private boolean isSmileLeft = false;
    private boolean isSmileRight = false;

public CustomToastView(Context context) {
        super(context);
    }
    public CustomToastView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public CustomToastView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    .......
}

b.设置画笔的参数以及矩形的参数。

private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.parseColor("#5cb85c"));
        mPaint.setStrokeWidth(dip2px(2));
    }
    private void initRect() {
        rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding);
    }
    //dip转px。为了支持多分辨率手机
 public int dip2px(float dpValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

c.重写onMeasure

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        initPaint();
        initRect();
        mWidth = getMeasuredWidth(); //当前view在父布局里的宽度。即view所占宽度。
        mPadding = dip2px(10);
        mEyeWidth = dip2px(3);
    }

d.重写OnDraw

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setStyle(Paint.Style.STROKE);
        //画微笑弧(从左向右画弧)
        canvas.drawArc(rectF, 180, endAngle, false, mPaint);
        //设置画笔为实心
        mPaint.setStyle(Paint.Style.FILL);
        //左眼
        if (isSmileLeft) {
            canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
        }
        //右眼
        if (isSmileRight) {
            canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
        }
    }

e.自定义View中的动画效果实现

/**
     * startAnim()不带参数的方法
     */
    public void startAnim() {
        stopAnim();
        startViewAnim(0f, 1f, 2000);
    }

/**
     * 停止动画的方法
     *
     */
    public void stopAnim() {
        if (valueAnimator != null) {
            clearAnimation();
            isSmileLeft = false;
            isSmileRight = false;
            mAnimatedValue = 0f;
            valueAnimator.end();
        }
    }
    /**
     * 开始动画的方法
     * @param startF 起始值
     * @param endF   结束值
     * @param time  动画的时间
     * @return
     */
    private ValueAnimator startViewAnim(float startF, final float endF, long time) {
        //设置valueAnimator 的起始值和结束值。
        valueAnimator = ValueAnimator.ofFloat(startF, endF);
        //设置动画时间
        valueAnimator.setDuration(time);
        //设置补间器。控制动画的变化速率
        valueAnimator.setInterpolator(new LinearInterpolator());
        //设置监听器。监听动画值的变化,做出相应方式。
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

mAnimatedValue = (float) valueAnimator.getAnimatedValue();
                //如果value的值小于0.5
                if (mAnimatedValue < 0.5) {
                    isSmileLeft = false;
                    isSmileRight = false;
                    endAngle = -360 * (mAnimatedValue);
                    //如果value的值在0.55和0.7之间
                } else if (mAnimatedValue > 0.55 && mAnimatedValue < 0.7) {
                    endAngle = -180;
                    isSmileLeft = true;
                    isSmileRight = false;
                    //其他
                } else {
                    endAngle = -180;
                    isSmileLeft = true;
                    isSmileRight = true;
                }
                //重绘
                postInvalidate();
            }
        });
        if (!valueAnimator.isRunning()) {
            valueAnimator.start();
        }
        return valueAnimator;
    }

好了,就这么多,自定义view大功告成。

2.toast要用的view的xml。没啥多说的直接上代码。有三个xml.
一个是backgroud_toast.xml(设置view的样式。)

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#FFFFFF"></solid>
    <stroke android:color="#C4CDE0"></stroke>
    <corners android:radius="10dp"></corners>

</shape>

一个text_toast.xml(设置textView的样式)

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#5cb85c"></solid>
    <stroke android:color="#C4CDE0"></stroke>
    <corners
        android:bottomRightRadius="10dp"
        android:topRightRadius="10dp"></corners>

</shape>

一个是smile_toast.xml(显示的toast的view布局方式)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#00000000"
    android:orientation="vertical">
    <LinearLayout
        android:id="@+id/base_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="25dp"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:layout_marginTop="25dp"
        android:background="@drawable/backgroud_toast"
        android:orientation="horizontal">
        <LinearLayout
            android:id="@+id/linearLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center">
            <com.example.yyh.toasttest.CustomToastView
                android:id="@+id/successView"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_gravity="center_vertical|left"
                android:layout_margin="10px"
                android:gravity="center_vertical|left" />
        </LinearLayout>
        <TextView
            android:id="@+id/toastMessage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:padding="10dp"
            android:text="New Text" />
    </LinearLayout>
</LinearLayout>

3.接下来就是在代码中将自定义的toast,加入进去。大功告成。

//在MainActivity中声明CustomToastView
static CustomToastView customToastView;

Toast smileToast=new Toast(MainActivity.this.getApplicationContext());
               //view布局
                View smileView =LayoutInflater.from(MainActivity.this.getApplicationContext()).inflate(R.layout.smile_toast,null,false);
                TextView text = (TextView) smileView.findViewById(R.id.toastMessage);
                text.setText("我是带动画的toast");
                //给customToastView增加动画效果
                customToastView=(CustomToastView)smileView.findViewById(R.id.smileView);
                customToastView.startAnim();
                //设置text的背景样式
                text.setBackgroundResource(R.drawable.text_toast);
                text.setTextColor(Color.parseColor("#FFFFFF"));
                smileToast.setView(smileView);
                smileToast.setDuration(Toast.LENGTH_SHORT);
                smileToast.show();

是不是脑光一亮~,笑脸的toast只是个启发,有了这个思路我们就可以设置不同的效果显示方式。

5.带出入效果的Toast
大家如果用的小米手机,就会发现,小米手机弹出的toast,有一个从底部上移弹出的效果。这个效果也是比较特别的。我们就来试试也实现下这个效果。
我们将4.自定义View带动画超高级的Toast.进行进一步的扩展,利用悬浮窗的原理,完成从底部弹出toast的效果。(其实查看源码 Toast实质上就是用到了悬浮窗的知识WindowManager.addView;和 mWM.removeView(mView);)来实现Toast的显示和消失的。当然,这里我们不再剖析源码,大家知道就行了
对悬浮窗的知识不是很了解的童鞋,可以去看我的上一篇文章: 仿360加速球。(实现内存释放)
首先,新建一个MiUiToast类。
1.一些需要的变量。这里用到了上面的自定义的view.

//窗口管理类,用来管理Toast的显示和隐藏。
    private WindowManager mWdm;
    //自定义的view.
    private CustomToastView mToastView;
    //toast的参数
    private WindowManager.LayoutParams mParams;
    //是否显示toast.
    private int showTime;
    private boolean mIsShow;//记录当前Toast的内容是否已经在显示
    //要显示的view.
    private final View smileView;

2.相关函数的设置。

//显示时间的设置相关
    public int getShowTime() {
        return showTime;
    }
    public void setShowTime(int showTime) {
        this.showTime = showTime;
    }
    public static MiUiToast MakeText(Context context, String text, int showTime) {
        MiUiToast result = new MiUiToast(context, text, showTime);
        return result;
    }
    private MiUiToast(Context context, String text, int time){
        setShowTime(time);
        mIsShow = false;//记录当前Toast的内容是否已经在显示
        mWdm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        smileView = LayoutInflater.from(context.getApplicationContext()).inflate(R.layout.smile_toast, null, false);
        TextView text1 = (TextView) smileView.findViewById(R.id.toastMessage);
        text1.setText("我是带动画的toast");
        mToastView=(CustomToastView) smileView.findViewById(R.id.smileView);
        mToastView.startAnim();
        text1.setBackgroundResource(R.drawable.text_toast);
        text1.setTextColor(Color.parseColor("#FFFFFF"));
        //设置布局参数
        setParams();
    }
    //toast.布局参数的设置。
    private void setParams() {
        mParams = new WindowManager.LayoutParams();
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.format = PixelFormat.TRANSLUCENT;
        mParams.windowAnimations = R.style.anim_view;//设置进入退出动画效果
        mParams.type = WindowManager.LayoutParams.TYPE_TOAST;
        mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
        mParams.gravity = Gravity.CENTER_HORIZONTAL;
        mParams.y = 250;
    }

//显示Toast.
    public void show(){
        if(!mIsShow){//如果Toast没有显示,则开始加载显示
            mIsShow = true;
            mWdm.addView(smileView, mParams);//将其加载到windowManager上
        }
    }

//取消toast.
    public void cancel(){
        mWdm.removeView(smileView);
        mIsShow = false;
    }

3.在ManiActivity中设置MiUiToast,显示。大功告成。

private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what==007){
                miUitoast.cancel();
            }
        }
    };
 private MiUiToast miUitoast;
  if(miUitoast == null){
                    miUitoast = MiUiToast.MakeText(this, "仿小米Toast", 2000);
                }
              miUitoast.show();
              handler.sendEmptyMessageDelayed(007,miUitoast.getShowTime());

6.关于Toast几个不为人知的秘密(敲黑板,认真脸,加分项啊加分项。)
1.toast的显示时间只有两种可能。我们查看源码可以得知它只有2秒和3.5秒。(long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;)

private void scheduleTimeoutLocked(ToastRecord r)  {  
  mHandler.removeCallbacksAndMessages(r);  
  Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);  
  long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;  
  mHandler.sendMessageDelayed(m, delay);  
}
private static final int LONG_DELAY = 3500; // 3.5 seconds  
private static final int SHORT_DELAY = 2000; // 2 seconds

那如果我们要控制toast的显示时间随意该怎么办呢,认真看过的上面的文章的童鞋,相信已经有了思路。没错,自定义view的toast,利用WindowManager+Handler,确定一定时间来remove这个自定义的Toast.就行了。参考5.带出入效果的Toast
当然还有另一种思路,利用反射拿到show方法,我试了好久,都是有错误,最后寻找原因但是好像在andorid4.0往上,这种方法就用了不了。这里就不讲了。。。
还有一种思路。利用Timer+Handler也可以来控制Toast的显示时间问题。那么开始吧~
新建一个TimeToast类,定义两个Timer,一个是显示Toast,一个是取消Toast.代码不难直接上~

/**
 * Created by yyh on 2016/10/27.
 */
public class TimeToast {
    //定义的显示时间
    private double time;
    private static Handler handler;
    //显示的计时器
    private Timer showTimer;
    //取消的计时器
    private Timer cancelTimer;

private Toast toast;

private TimeToast(){
        showTimer = new Timer();
        cancelTimer = new Timer();
    }
    public void setTime(double time) {
        this.time = time;
    }
    public void setToast(Toast toast){
        this.toast = toast;
    }

public static TimeToast makeText(Context context, String text, double time){
        TimeToast toast1= new TimeToast();
        toast1.setTime(time);
        toast1.setToast(Toast.makeText(context, text, Toast.LENGTH_SHORT));
        handler = new Handler(context.getMainLooper());
        return toast1;
    }
    public void show(){
        toast.show();
        if(time > 2){
            showTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    handler.post(new ShowRunnable());
                }
            }, 0, 1900);
        }
        cancelTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                handler.post(new CancelRunnable());
            }
        }, (long)(time * 1000));
    }
    private class CancelRunnable implements Runnable{
        @Override
        public void run() {
            showTimer.cancel();
            toast.cancel();
        }
    }
    private class ShowRunnable implements Runnable{
        @Override
        public void run() {
            toast.show();
        }
    }
}

之后在ManiActivity中调用这个类就行了

TimeToast timeToast=TimeToast.makeText(getApplicationContext(),"显示时间自定的Toast",6);
                timeToast.show();
1
2
2.Toast显示的问题,当我们连续点击Toast的时候,居然一直在显示,点击30多下,结果这条Toast显示了将近2分钟。这样用户的体验很不好。(原因是因为Toast的管理是在队列中,点击一次,就会产生一个新的Toast,所以要等这个队列中的Toast处理完,这个显示Toast的任务才算结束。) so~ 思路来了,我们可以把Toast改成单例模式,没有Toast再新建它,这样也就解决了连续点击Toast,一直在显示的问题。~
先上新建的单例模式的SingleToast类:

public class SingleToast {
    private static Toast mToast;
    /**双重锁定,使用同一个Toast实例*/
    public static Toast getInstance(Context context){
        if (mToast == null){
            synchronized (SingleToast.class){
                if (mToast == null){
                    mToast = new Toast(context);
                }
            }
        }
        return mToast;
    }
}
1

接着在ManiActivity中进行应用,就是这么随意~ 大功告成~

Toast singleToast=SingleToast.getInstance(getApplicationContext());
                View singleCustomView = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_toast,null);
                ImageView img1 = (ImageView) singleCustomView.findViewById(R.id.iv);
                TextView tv1 = (TextView) singleCustomView.findViewById(R.id.tv);
                img1.setBackgroundResource(R.drawable.ic_launcher);
                tv1.setText("这是第"+num+++"遍 点击我了~~");
                singleToast.setView(singleCustomView);
                singleToast.setDuration(Toast.LENGTH_SHORT);
                singleToast.show();

Toast的高级自定义方式-循序渐进带你了解toast相关推荐

  1. Toast 自定义位置和带图

    1.自定义位置 Toast t = Toast.makeText(Reg_FillContent_Activity.this, "用户已存在!", Toast.LENGTH_SHO ...

  2. Android深入浅出系列之实例应用—弹出消息Toast对象的使用自定义方式(二)

    阅读目录 一:前言 二:实例 三:运行效果 一:前言 Android中定义了一个Toast对象,用以弹出一个窗口来给予用户帮助和提示,和对话框不同的是,Toast并不是以独占方式显示的,它并不会抢夺用 ...

  3. linux运维脚本编写,最强Linux自动化运维 Shell高级脚本编程实战 带习题+项目实战案例+全套配置脚本...

    最强Linux自动化运维 Shell高级脚本编程实战 带习题+项目实战案例+全套配置脚本 大家可以通过参考下面的课程学习目录,就会发现单单只从目录上来分析就知道这是一部非常系统的Shell自动化脚本运 ...

  4. 关于Android项目中的Toast那些动画实现方式

    最近产品给了一个竞品App的Toast动画,希望开发可以去实现它,经过一段时间的深(不)思(停)熟(百)虑(度)之后,发现事情其实并不简单,所以这里记录一下关于Android~Toast动画实现的相关 ...

  5. 3.8 高级检索方式(二)

    上节已经介绍了五种高级检索方式,现在我们来学习另外五种.事实上,今天要介绍的五种高级检索方式有共通之处:都可以解决上节提到的最后一个用户需求:帮助小明快速定位游戏分类,过滤掉其他分类内容. 六.Boo ...

  6. Qt 3D:高级自定义材质QML示例

    Qt自带集成开发环境(IDE),名为Qt Creator.它可以在Linux.OS X和Windows上运行,并提供智能代码完成.语法高亮.集成帮助系统.调试器和剖析器集成,还集成了所有主要的版本控制 ...

  7. 自定义Toast实现多次触发只显示一次toast使用改良

    使用场景描述 当我们处于某个场景,例如一个按钮可以触发toast的显示,当你在多次点击按钮时,会多次触发toast的显示.而调用android原生的toast的makeText的方式所生产的toast ...

  8. 自相关acf_高级自定义字段(ACF)入门

    自相关acf 为什么要使用自定义字段? (Why Use Custom Fields?) I can't begin to express how blown my mind was when I g ...

  9. WORD文档中的表格:自定义方式的批处理,增删行列和内容

    WORD文档中的表格:自定义方式的批处理,增删行列和内容: 大家有没有遇到需要处理WORD文档中的表格,表格多,需要大量的重复性工作: office提供了宏录制方式,可以编辑风格,内容,但是针对增删表 ...

最新文章

  1. linux内核map图
  2. 英特尔:谁说深度学习已死?AI任务挑大梁的是CPU,不是GPU
  3. mnn python例子
  4. python三维图形渲染-基于 pygtk + vtk 实现三维数据可视化(一)
  5. @SentinelResource使用
  6. SAP UI5 ConnectionManager
  7. python中的进程, 线程
  8. matlab 纯数据表格,MATLAB uitable表格数据更新处理
  9. 动物行为检测计算机视觉_基于红外热成像和计算机视觉的动物行为研究系统便是其中一例...
  10. 实现开发板与ubuntu的共享--根文件系统NFS--Samba共享【sky原创】
  11. 计算机网络 课后题答案解析,计算机网络课后习题和答案解析
  12. 如何在程序中安装指定apk文件
  13. 如何解决WIN10提示0x8007007b错误问题
  14. java逻辑与或非_Java基础——逻辑运算符与或非
  15. git add所有文件
  16. 还不快点刷面试题,史上最全Java工程师面试题汇总,全会月薪至少3W
  17. 分布式技术(5)服务高可用:幂等性设计
  18. 支付平台--清算总的详解
  19. 【R】【课程笔记】02+03 基于R软件的计算
  20. 文科生如何就量子物理和相对论谈笑风生? 我读过的10本有料有趣科普书

热门文章

  1. c#操作excel 使用excel自带类库Microsoft.Office.Interop.Excel
  2. “梅西”式核心员工,正在摧毁你的团队
  3. 优麒麟 20.04 LTS Pro 发布 - 以初心,铸匠心
  4. everything changes so quietly
  5. BigBrother的大数据之旅Day 11 hive(2)
  6. Webdings字体和Wingdings字体对照表
  7. 2021四川艺术高考成绩查询,2021四川高考艺术类分数线预测
  8. c语言冗余数据什么意思,冗余是什么意思_冗余解释和意思
  9. 时间触发嵌入式系统设计模式 读书笔记
  10. to tell 和to ask的用法