最近產(chǎn)品提了一個想讓我拿刀的問題昨寞,一個提示文字要在其他app在頂層的時候也顯示棵帽,不能干擾用戶操作儡首,文字必須是可以滾動,時長為30秒(啥啥啥都是啥)器贩,做安卓的都知道toast只能設(shè)置為2s和3.5s颅夺,其它的值都無效,API的文檔雖然寫的第三個參數(shù)是時間蛹稍,但是Framework里作了重定義吧黄,限定了2s和3.5s這兩個值,對應(yīng) Toast.LENGTH_SHORT和Toast.LENGTH_LONG唆姐,但是測試不會管你安卓是怎么限制的拗慨,實現(xiàn)不出就是你自己能力不行,為了保住飯碗奉芦,問遍了度娘及其他的大佬赵抢,終于搞出了一個讓Toast持續(xù)顯示的方案:
```
public void showMyToast(final Toast toast, final int cnt) {
?final Timer timer =new Timer();
? ? timer.schedule(new TimerTask() {
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? toast.show();
? ? }
? ? },0,3000);
? ? new Timer().schedule(new TimerTask() {
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? toast.cancel();
? ? ? ? timer.cancel();
? ? }
? ? }, cnt );
}
```
調(diào)用:
Toast toast=Toast.makeText(RegistActivity.this,"這是可以隨意設(shè)置時間的Toast", Toast.LENGTH_LONG);showMyToast(toast,10*1000);// 設(shè)置顯示時間
你每次要關(guān)的時候我就調(diào)用show()方法,直到我Timer計時完畢声功,網(wǎng)上最傳廣的方法烦却,優(yōu)點是全原生,只需要加個控制類就行先巴,但是立馬被測試打回來了其爵,為啥冒冬?因為其實這只是偽顯示,每3秒都是重新打開的摩渺,所以滾動文字每三秒就回到開始重新輪播简烤,產(chǎn)品的需求是10秒一滾,它滾不了你就給我滾证逻,乐埠,,好吧囚企,既然別人是一種不要你覺得我要我覺得的態(tài)度丈咐,我也只能重新做了。龙宏。棵逊。有發(fā)現(xiàn)zhitaocai大佬為了解決當時MIUI4 toast不顯示的問題把toast重寫了一遍實現(xiàn)了完整的setDuration方法,(github地址:https://github.com/zhitaocai/ToastCompat_Deprecated)不過內(nèi)部的view是寫死的银酗,如果只需要使用普通的toast的話只需引入依賴庫之后直接ToastCompat.make(Contextcontext,Stringtext,longduration).show();
就可以愉快的使用了辆影,但是如果需要像我一樣實現(xiàn)滾動文字和文字前l(fā)ogo的話就必須對MIUItoast類進行重寫,在IToast setText方法中自定義view和內(nèi)容然后setview(view)黍特,具體代碼如下:
```
? ??public class MIUIToastimplements IToast {
private static HandlermHandler =new Handler();
? ? public AutoMarqueeTextViewGeTuiToastUtilMessage;
? ? public ImageViewGeTuiToastUtilImage;
? ? public ViewGeTuiToastUtilView;
? ? /**
* 維護toast的隊列
*/
? ? private static BlockingQueuemQueue =new LinkedBlockingQueue<>();
? ? /**
? ? * 原子操作:判斷當前是否在讀取{@linkplain #mQueue 隊列}來顯示toast
*/
? ? protected static AtomicIntegermAtomicInteger =new AtomicInteger(0);
? ? private WindowManagermWindowManager;
? ? private long mDurationMillis;
? ? private ViewmView;
? ? private WindowManager.LayoutParamsmParams;
? ? private ContextmContext;
? ? public static IToastmakeText(Context context, String text, long duration) {
return new MIUIToast(context).setText(text).setDuration(duration)
.setGravity(Gravity.TOP, 0, DisplayUtil.dip2px(context, 10));
? ? }
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.TOP ;
? ? }
/**
* 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 IToastsetGravity(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 IToastsetDuration(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 IToastsetView(View view) {
mView = view;
return this;
? ? }
@Override
? ? public IToastsetMargin(float horizontalMargin, float verticalMargin) {
mParams.horizontalMargin = horizontalMargin;
? ? ? ? mParams.verticalMargin = verticalMargin;
return this;
? ? }
/**
? ? * 不能和{@link #setView(View)}一起使用,要么{@link #setView(View)} 要么{@link #setView(View)}
*
? ? * @return
? ? */
? ? @Override
? ? public IToastsetText(String text) {
// 模擬Toast的布局文件 com.android.internal.R.layout.transient_notification
// 雖然可以手動用java寫灭衷,但是不同廠商系統(tǒng)次慢,這個布局的設(shè)置好像是不同的,因此我們自己獲取原生Toast的view進行配置
? ? ? ? GeTuiToastUtilView = LayoutInflater.from(mContext).inflate(R.layout.toast_layout, null);
? ? ? ? if (GeTuiToastUtilView !=null) {
GeTuiToastUtilMessage =GeTuiToastUtilView.findViewById(R.id.geTuiToastUtilMessage);
? ? ? ? ? ? GeTuiToastUtilImage = (ImageView)GeTuiToastUtilView.findViewById(R.id.toast_image);
? ? ? ? ? ? GeTuiToastUtilMessage.setTextColor(ScnResourceUtil.getColor(mContext, R.color.scn_color_EBEBEB));
? ? ? ? ? ? GeTuiToastUtilMessage.setTextSize(TypedValue.COMPLEX_UNIT_PX, ScnResourceUtil.getDimen(mContext, R.dimen.dimen_12sp));
? ? ? ? ? ? GeTuiToastUtilMessage.setMaxWidth(ScnResourceUtil.getDimen(mContext,R.dimen.dimen_574dp));
? ? ? ? ? ? GeTuiToastUtilMessage.setScrollStep(ScnResourceUtil.getDimen(mContext,R.dimen.dimen_200dp));
? ? ? ? ? ? GeTuiToastUtilMessage.setMaxLines(1);
? ? ? ? ? ? GeTuiToastUtilMessage.setRndDuration(10);
? ? ? ? ? ? GeTuiToastUtilMessage.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
? ? ? ? ? ? setView(GeTuiToastUtilView);
? ? ? ? ? ? GeTuiToastUtilMessage.setText(text.isEmpty() ?"null" : text);
? ? ? ? ? ? GeTuiToastUtilMessage.post(() ->GeTuiToastUtilMessage.startMarquee());
? ? ? ? ? ? this.setGravity(Gravity.TOP,0,20);
? ? ? ? }
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. 如果隊列已經(jīng)處于非激活狀態(tài)或者隊列沒有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在隊列中的等候展示,當調(diào)用了這個toast的取消時掠械,考慮是否應(yīng)該從對隊列中移除由缆,看產(chǎn)品需求吧
? ? }
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的話,那么就表示已經(jīng)暫時完所有的toast了
// 這個時候需要標記隊列狀態(tài)為:非激活讀取中
? ? ? ? ? ? mAtomicInteger.decrementAndGet();
? ? ? ? }else {
// 如果還能從隊列中獲取到toast的話份蝴,那么就表示還有toast沒有展示
// 1. 展示隊首的toast
// 2. 設(shè)置一定時間后主動采取toast消失措施
// 3. 設(shè)置展示完畢之后再次執(zhí)行本邏輯犁功,以展示下一個toast
? ? ? ? ? ? mHandler.post(miuiToast.mShow);
? ? ? ? ? ? mHandler.postDelayed(miuiToast.mHide, miuiToast.mDurationMillis);
? ? ? ? ? ? mHandler.postDelayed(mActivite, miuiToast.mDurationMillis);
? ? ? ? }
}
private final RunnablemShow =new Runnable() {
@Override
? ? ? ? public void run() {
handleShow();
? ? ? ? }
};
? ? private final RunnablemHide =new Runnable() {
@Override
? ? ? ? public void run() {
handleHide();
? ? ? ? }
};
? ? private final static RunnablemActivite =new Runnable() {
@Override
? ? ? ? public void run() {
activeQueue();
? ? ? ? }
};
}
```
這里感謝非花非霧--大佬的修改方案,這里用的是自定義方案三