Toast自定义显示时长的解决方案-阿里云开发者社区

开发者社区> 非花非雾> 正文

Toast自定义显示时长的解决方案

简介:
+关注继续查看

版权声明:本文为博主原创文章,未经博主允许不得转载。
关于Toast,大家可能熟的不能再熟了,但是都知道它有一个缺点,就是没有办法控制显示时长,默认有俩种状态,
Toast.LENGTH_LONG 默认显示3.5秒
Toast.LENGTH_SHORT 默认显示2秒。

源码就不说了,直接说解决方式:


(1)、通过源码发现只要避免了让他加入系统维修的Toast队列中,那么就可以让我们来操作

他的显示还有隐藏,那么就只能通过反射来实现了:

代码:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
package com.duoku.platform.single.view;

import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**

  • Created by chenpengfei_d on 2016/9/1.
    */

public class DkToast {

private static Toast mToast;  
private static Context mContext;  
private static TextView mTextView;  
private static long duration;  
private static DkToast mDkToast;  
private static final int SHOW = 1;  
private static final int HIDE = 0;  
private static Object mTN;  
private static Method mShow;  
private static Method mHide;  
private static Field mViewFeild;  
private static Handler handler  = new Handler(){  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        switch (msg.what){  

            case SHOW:  
                handler.sendEmptyMessageDelayed(HIDE,duration);  
                break;  

            case HIDE:  
                hide();  
                break;  
        }  
    }  
};  
public static DkToast makeDkToast(Context context,String msg,long dur) {  

    mDkToast = new DkToast();  
    mContext = context;  
    duration = dur;  
    mToast = new Toast(mContext);  
    mTextView = new TextView(mContext);  
    mTextView.setText(msg);  
    mTextView.setTextSize(18);  
    mTextView.setTextColor(Color.RED);  
    mToast.setView(mTextView);  
    mToast.setGravity(Gravity.CENTER,0,0);  
    reflectToast();  
    return mDkToast;  
}  

public static void reflectToast(){  
    Field field = null;  
    try {  
        field = mToast.getClass().getDeclaredField("mTN");  
        field.setAccessible(true);  
        mTN = field.get(mToast);  
        mShow = mTN.getClass().getDeclaredMethod("show");  
        mHide = mTN.getClass().getDeclaredMethod("hide");  
        mViewFeild = mTN.getClass().getDeclaredField("mNextView");  
        mViewFeild.setAccessible(true);  

    } catch (NoSuchFieldException e) {  
        e.printStackTrace();  
    } catch (IllegalAccessException e) {  
        e.printStackTrace();  
    } catch (IllegalArgumentException e) {  
        e.printStackTrace();  
    } catch (NoSuchMethodException e1) {  
        e1.printStackTrace();  
    }  

}  
public static void show(){  

    try {  
        //android4.0以上就要以下处理  
        if(Build.VERSION.SDK_INT >14) {  
            Field mNextViewField = mTN.getClass().getDeclaredField("mNextView");  
            mNextViewField.setAccessible(true);  
            LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
            View v = mToast.getView();  
            mNextViewField.set(mTN, v);  
            Method method = mTN.getClass().getDeclaredMethod("show", null);  
            method.invoke(mTN, null);  
        }  
        mShow.invoke(mTN, null);  
    }catch (Exception e){  
        e.printStackTrace();  
    }  

    handler.sendEmptyMessage(SHOW);  
}  

private static void hide(){  
    try {  
        mHide.invoke(mTN, null);  
    } catch (IllegalAccessException e) {  
        e.printStackTrace();  
    } catch (IllegalArgumentException e) {  
        e.printStackTrace();  
    } catch (InvocationTargetException e) {  
        e.printStackTrace();  
    }  
}  

}
上边是一个完整的Toast,直接就能使用,调用简单:

实例:
DkToast.makeDkToast(this,"显示效果",100000).show();
位置调整,已经自定义View须要自己加了
(2)第二种方式,使用过桌面悬浮窗的都应该知道,通过WindowManager可以实现类似360小火箭的悬浮效果
,那么我们也可以使用这个来进行Toast的显示:
不了解WindowManager实现悬浮窗的可以看下边资料:
http://blog.csdn.net/u012808234/article/details/52209713

Toast实现的代码:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
package com.duoku.platform.demo.single;

import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

/**

  • Created by chenpengfei_d on 2016/9/2.
    */

public class WindowToast {

private WindowManager mWindowManager;  
private View view;  
WindowManager.LayoutParams params;  
private static final int SHOW = 0;  
private static final int HIDE = 1;  
private int duration ;  
private Handler handler = new Handler(){  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  

        switch (msg.what){  
            case SHOW:  
                handler.sendEmptyMessageDelayed(HIDE,duration);  
                break;  
            case HIDE:  
                hide();  

                break;  
        }  
    }  
};  

public  WindowToast makeToast(Context context ,String msg ,int duration){  
    mWindowManager  = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
     params = new WindowManager.LayoutParams();  
    params.type = WindowManager.LayoutParams.TYPE_TOAST;  
    params.windowAnimations = android.R.style.Animation_Toast;  
    params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;  
    params.format = PixelFormat.TRANSLUCENT;  
    params.width  = WindowManager.LayoutParams.WRAP_CONTENT;  
    params.height = WindowManager.LayoutParams.WRAP_CONTENT;  
    params.gravity = Gravity.BOTTOM;  
    view = new TextView(context);  
    TextView textView = (TextView) view;  
    textView.setText(msg);  
    textView.setTextColor(Color.BLACK);  
    textView.setTextSize(18);  
    this.duration = duration;  
    return this;  
}  

public WindowToast setGravity(int type){  
    params.gravity = type;  
    return this;  
}  
public WindowToast setView(View view) {  
    this.view = view;  
    return this;  
}  

public void show(){  
    if(view == null){  
        throw  new RuntimeException("setView must have been called");  
    }  
    mWindowManager.addView(view,params);  
    handler.sendEmptyMessage(SHOW);  
}  

public void hide(){  

    mWindowManager.removeView(view);  
}  

}


简单的实现了一个Toast的功能,可以自定义显示时长。

关于4.0以上手机反射不起作用,可以使用这个。
(3)第三种,其实跟第二种很类似,只是我们仿照Toast源码来自己构建Toast队列,进行Toast

的整体控制,当然Toast的实现还是WindowManager(代码不难就不自己写了,网上一位大神解决MIUI系统不显示写的):
(地址:http://caizhitao.com/2016/02/09/android-toast-compat/
[java] view plain copy print?在CODE上查看代码片派生到我的代码片
package io.github.zhitaocai.toastcompat.toastcompat;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import io.github.zhitaocai.toastcompat.util.DisplayUtil;

/**

  • @author zhitao
  • @since 2016-01-21 14:33
    */

public class MIUIToast implements IToast {

private static Handler mHandler = new Handler();  

/** 
 * 维护toast的队列 
 */  
private static BlockingQueue<MIUIToast> mQueue = new LinkedBlockingQueue<MIUIToast>();  

/** 
 * 原子操作:判断当前是否在读取{@linkplain #mQueue 队列}来显示toast 
 */  
protected static AtomicInteger mAtomicInteger = new AtomicInteger(0);  

private WindowManager mWindowManager;  

private long mDurationMillis;  

private View mView;  

private WindowManager.LayoutParams mParams;  

private Context mContext;  

public static IToast makeText(Context context, String text, long duration) {  
    return new MIUIToast(context).setText(text).setDuration(duration)  
            .setGravity(Gravity.BOTTOM, 0, DisplayUtil.dip2px(context, 64));  
}  

public MIUIToast(Context context) {  

    mContext = context;  
    mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);  
    mParams = new WindowManager.LayoutParams();  
    mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
    mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
    mParams.format = PixelFormat.TRANSLUCENT;  
    mParams.windowAnimations = android.R.style.Animation_Toast;  
    mParams.type = WindowManager.LayoutParams.TYPE_TOAST;  
    mParams.setTitle("Toast");  
    mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |  
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;  
    // 默认小米Toast在下方居中  
    mParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;  
}  

/** 
 * Set the location at which the notification should appear on the screen. 
 * 
 * @param gravity 
 * @param xOffset 
 * @param yOffset 
 */  
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)  
@Override  
public IToast setGravity(int gravity, int xOffset, int yOffset) {  

    // We can resolve the Gravity here by using the Locale for getting  
    // the layout direction  
    final int finalGravity;  
    if (Build.VERSION.SDK_INT >= 14) {  
        final Configuration config = mView.getContext().getResources().getConfiguration();  
        finalGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection());  
    } else {  
        finalGravity = gravity;  
    }  
    mParams.gravity = finalGravity;  
    if ((finalGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {  
        mParams.horizontalWeight = 1.0f;  
    }  
    if ((finalGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {  
        mParams.verticalWeight = 1.0f;  
    }  
    mParams.y = yOffset;  
    mParams.x = xOffset;  
    return this;  
}  

@Override  
public IToast setDuration(long durationMillis) {  
    if (durationMillis < 0) {  
        mDurationMillis = 0;  
    }  
    if (durationMillis == Toast.LENGTH_SHORT) {  
        mDurationMillis = 2000;  
    } else if (durationMillis == Toast.LENGTH_LONG) {  
        mDurationMillis = 3500;  
    } else {  
        mDurationMillis = durationMillis;  
    }  
    return this;  
}  

/** 
 * 不能和{@link #setText(String)}一起使用,要么{@link #setView(View)} 要么{@link #setView(View)} 
 * 
 * @param view 
 * 
 * @return 
 */  
@Override  
public IToast setView(View view) {  
    mView = view;  
    return this;  
}  

@Override  
public IToast setMargin(float horizontalMargin, float verticalMargin) {  
    mParams.horizontalMargin = horizontalMargin;  
    mParams.verticalMargin = verticalMargin;  
    return this;  
}  

/** 
 * 不能和{@link #setView(View)}一起使用,要么{@link #setView(View)} 要么{@link #setView(View)} 
 * 
 * @return 
 */  
@Override  
public IToast setText(String text) {  

    // 模拟Toast的布局文件 com.android.internal.R.layout.transient_notification  
    // 虽然可以手动用java写,但是不同厂商系统,这个布局的设置好像是不同的,因此我们自己获取原生Toast的view进行配置  

    View view = Toast.makeText(mContext, text, Toast.LENGTH_SHORT).getView();  
    if (view != null) {  
        TextView tv = (TextView) view.findViewById(android.R.id.message);  
        tv.setText(text);  
        setView(view);  
    }  

    return this;  
}  

@Override  
public void show() {  
    // 1. 将本次需要显示的toast加入到队列中  
    mQueue.offer(this);  

    // 2. 如果队列还没有激活,就激活队列,依次展示队列中的toast  
    if (0 == mAtomicInteger.get()) {  
        mAtomicInteger.incrementAndGet();  
        mHandler.post(mActivite);  
    }  
}  

@Override  
public void cancel() {  
    // 1. 如果队列已经处于非激活状态或者队列没有toast了,就表示队列没有toast正在展示了,直接return  
    if (0 == mAtomicInteger.get() && mQueue.isEmpty()) {  
        return;  
    }  

    // 2. 当前显示的toast是否为本次要取消的toast,如果是的话  
    // 2.1 先移除之前的队列逻辑  
    // 2.2 立即暂停当前显示的toast  
    // 2.3 重新激活队列  
    if (this.equals(mQueue.peek())) {  
        mHandler.removeCallbacks(mActivite);  
        mHandler.post(mHide);  
        mHandler.post(mActivite);  
    }  

    //TODO 如果一个Toast在队列中的等候展示,当调用了这个toast的取消时,考虑是否应该从对队列中移除,看产品需求吧  
}  

private void handleShow() {  
    if (mView != null) {  
        if (mView.getParent() != null) {  
            mWindowManager.removeView(mView);  
        }  
        mWindowManager.addView(mView, mParams);  
    }  
}  

private void handleHide() {  
    if (mView != null) {  
        // note: checking parent() just to make sure the view has  
        // been added...  i have seen cases where we get here when  
        // the view isn't yet added, so let's try not to crash.  
        if (mView.getParent() != null) {  
            mWindowManager.removeView(mView);  
            // 同时从队列中移除这个toast  
            mQueue.poll();  
        }  
        mView = null;  
    }  
}  

private static void activeQueue() {  
    MIUIToast miuiToast = mQueue.peek();  
    if (miuiToast == null) {  
        // 如果不能从队列中获取到toast的话,那么就表示已经暂时完所有的toast了  
        // 这个时候需要标记队列状态为:非激活读取中  
        mAtomicInteger.decrementAndGet();  
    } else {  

        // 如果还能从队列中获取到toast的话,那么就表示还有toast没有展示  
        // 1. 展示队首的toast  
        // 2. 设置一定时间后主动采取toast消失措施  
        // 3. 设置展示完毕之后再次执行本逻辑,以展示下一个toast  
        mHandler.post(miuiToast.mShow);  
        mHandler.postDelayed(miuiToast.mHide, miuiToast.mDurationMillis);  
        mHandler.postDelayed(mActivite, miuiToast.mDurationMillis);  
    }  
}  

private final Runnable mShow = new Runnable() {  
    @Override  
    public void run() {  
        handleShow();  
    }  
};  

private final Runnable mHide = new Runnable() {  
    @Override  
    public void run() {  
        handleHide();  
    }  
};  

private final static Runnable mActivite = new Runnable() {  
    @Override  
    public void run() {  
        activeQueue();  
    }  
};  

}

总结: (1)解决了Toast 无法自定义时长的问题

   (2)解决了4.0以上手机使用反射自定义时长不生效的问题
   (3)解决了Toast显示不灵活的问题

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案
基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案           两个月前做C++课设的时候,电脑编译器编译结果出现了中文乱码,寻求了百度和大神们,都没有解决这个问题,百度上一堆解释是对编译器进行设置之类的,结果没有一个有效果,暑假学习了用Dos来开发Java程序,输入...
1220 0
AOC显示器 显示“输入不支持” 解决方案
前两天,一朋友碰到一个问题。系统重装后,开机显示器上显示“输入不支持”,且伴随一个框,在跳来跳去,很影响工作! 于是,开始根据错误提示在网上搜索,有的说是驱动问题,于是下载驱动重新安装,但是还是不行。
3038 0
Android 自定义Toast,并且勘误Android工具类里面的ToastUtils
前言 相信大部分仁兄在使用系统Toast的时候,都感觉不太尽如人意,因为系统Toast显示的位置比较固定,并且字体颜色等会跟随系统版本变化,那么能不能自己写一个呢,答案是当然的。
933 0
使用JavaScript在HaaS EDU K1上实现文字显示
当前HaaS EDU K1已经支持通过JS轻应用方式进行开发调试了,这块开发板带着OLED屏,而底层的AliOS Things已经支持图形库,所以可以通过轻应用的开发方式,尝试进行GUI相应的开发。
127 0
Qt编写自定义控件54-时钟仪表盘
一、前言 这个控件没有太多的应用场景,主要就是练手,论美观的话比不上之前发过的一个图片时钟控件,所以此控件也是作为一个基础的绘制demo出现在Qt源码中,我们可以在Qt的安装目录下找到一个时钟控件的绘制,甚至还有qml版本,本控件无非就是一个仪表边框加上时钟分钟刻度再加上时分秒指针,打完收工,我是在此基础上增加了可以设置各种颜色,然后鼠标右键可设置四种效果 普通效果/弹簧效果/连续效果/隐藏效果,弹簧效果的意思是秒钟走动的时候,先移动到超过指定位置,然后又重新弹回来,连续效果的意思是将步长减少,一点点的移动,将秒钟的定时器精度调高。
936 0
VS解决方案的“显示所有文件等属性”
VS解决方案属性中的六大金刚: 一、属性 二、显示所有文件 显示所有文件/隐藏某些文件。 三、刷新 四、查看代码 五、视图设计器 ...
781 0
PyQt5在QWidget窗体中显示Qwidget的自定义类(补:完美解决)
【概览】 1、显示原生Qwidget       1)不使用布局(绝对定位)       2)使用布局 2、显示Qwidget的自定义类       1)不使用布局(绝对定位)       2)使用布局   【知识点】 1、显示原生Qwidget 1)不使用布局(绝对定位) 这...
1338 0
开发函数计算的正确姿势———为 PHP 运行时添加自定义扩展
PHP 语言提供了一种扩展机制(Extension),通过 PHP 扩展可以增强语法、调用 C/C++ 实现的库函数以及优化执行性能。PHP 扩展是与平台相关的动态链接库,在 Linux 和 Mac 平台是 .so 文件,在 Windows 平台是 .dll 文件。由于函数计算的开发通常在 Mac 和 Windows 平台,而运行时是 Linux(Debain)环境,所以为函数计算 PHP 运行时添加扩展会遇到由于动态链接库平台相关而导致要么本地无法调试,要么远端无法运行的问题。本文介绍借助 Funcraft 工具提供的模拟环境进行 PHP 扩展的安装、本地运行调试以及构建发布。
401 0
+关注
81
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载