Android Toast 解析以及减少“无意义的”toast
2016-02-01 17:06
603 查看
本文系作者原创,转载请附原文地址:http://blog.csdn.net/a774057695/article/details/50619599
也要先致歉一下,行文的时候是在word中编辑的,拉上来的时候也不高兴调格式惹。。。
呵呵哒:
这一篇略显无聊,这么说是有道理的,toast这东西真的没什么好讲的,扒着指头数数也就这么几样东西:
1. 自定义样式
2. 自定义位置
3. 主线程中调用show()方法显示,非主线程需要开启looper
然而还是想拉出来溜一圈,为什么呢,测试总是很疯狂,甚至让你有不想使用toast的想法。然而toast又是那么友好的一个widget,在界面上重写一个提示内容显示区又不是产品设计师要的效果。
关键问题呢,是toast实在是太简单太基础了,以至于忘掉要去看一下它的源码。
说到这里要提一下为什么要写这个的原因了,近期手头上有个项目,测试总是吐槽,我这里点了很多下,提示语怎么不消失……大概隔了一分钟吧,提示语没了。内心满满的崩溃,屏幕和你有仇啊,点几十下!!!
总之就是想让我们改,但是我不想改,因为改了也没什么意思啊,关键是没有明确的预期效果。产品设计的时候确实会有部分时间间隔很小的连续提示(虽然应该去避免这些问题,可咱不想搞产品的活)。
可能有些朋友已经想说重写一个类似toast的widget了,奉劝你不要。因为没有明确的需求。
正文:
这里直接进入正题:从设计的角度来说,为了使得交互更加友好,应当将一些重要的信息反馈给用户,按照信息的重要性(以及牵涉到的后续操作),一般有三种提示形式:
1. toast
2. 视图上安置一块信息显示区
3. dialog形式
这三种形式中,1和2对用户的打扰程度是较小的,dialog的打扰程度很大,一般来说,当你必须要用户来做出决定时才会使用dialog形式。
而1和2相比较:Toast维护较为方便,2需要在布局上进行额外的处理,而2的优点在于,开发者可以根据实际需求来维护提示的显示状态。
一般而言,2使用的并不是很多,尽管他的样式可以和应用的主题很相近,原因在于你需要在布局上额外设计一个显示区,比较费神,往往登录界面(或者说需要验证用户输入)的场景才会使用。
而toast的一个问题就是,系统会将toast放置到队列中然后维护其显示。所以出现了测试人员疯狂的点击(产生同样的toast),这条toast会显示很长时间。所以,我们真正的需求(或者封住测试的嘴巴的方案)就是在显示过程中如果紧跟着出现了相同的toast,就不要显示了(这条toast可能是反复操作导致的,让它滚蛋,而其他内容的toast还是保守一些,让它去排队吧)。
然而这个需求想直接使用toast的代码来实现还是不怎么可行的。
可能有些人会有这样一种思路:取消之前的显示,更新新的显示。首先给个粗糙的结论,体验可能不会太好。他们想的是cancel()方法来简单粗暴的取消一个toast的显示,然而这样会带来还没看完,刷新了的问题,以及遗漏一些提示。
可能您已经对toast的源码感兴趣了,我们先看一下api中大致的内容,再看源码,再看看如何实现我们的目的
Constants
Public Constructors
Public Methods
内容真的很少,set和get一堆属性,maketext是利用几个重要的参数构建并返回一个toast实例,show()方法和cancel()方法。那我们看源码,分析一下toast的显示和消失。
从show方法和cancel方法,我们发现了有个内部类TN,成员函数中有如下两个:
分别对应这一个toast的显示和消失(隐藏),使用了hanler,关键之处在于mShow和mHide两个Runnable接口实例的内容。
取出最重要的部分:
再把话头转回来,显示的时候,调用了
这里呢,和大家先道个歉,刚才实在是太在意cancel了,理应先看show的。这个service从哪里来的呢?
这样我们就知道了,用AIDL定义了一个接口,
而他的实现,没错,就是在TN类。所以说,显示使用了IPC,而且,这么多年的经验不看更深的源码都知道这个toast被放到queue中了。同样,隐藏也是需要用到ipc的,而且应当有回调的。
好了,先不看源码了。我们之前说简单粗暴的cancel掉体验可能不好,我们可以这样尝试:
代码不长,一眼就看明白了,需要注意的是,只有在Toast已经显示,同时当前新增的toast内容和上一条一致时,我们才忽略它。另外,在非UI线程调用的时候注意Looper。
然后呢,就突然衍生了一个自定义显示时长的toast的想法:
可以发现,基本都是从toast中抄出来的。先写个模板放在这里,根据实际需求,我们还能玩很大花样,(毕竟对toast类我们玩不了什么花样了)
就到这里吧,本文系作者原创,转载请附原文地址:http://blog.csdn.net/a774057695/article/details/50619599
也要先致歉一下,行文的时候是在word中编辑的,拉上来的时候也不高兴调格式惹。。。
呵呵哒:
这一篇略显无聊,这么说是有道理的,toast这东西真的没什么好讲的,扒着指头数数也就这么几样东西:
1. 自定义样式
2. 自定义位置
3. 主线程中调用show()方法显示,非主线程需要开启looper
然而还是想拉出来溜一圈,为什么呢,测试总是很疯狂,甚至让你有不想使用toast的想法。然而toast又是那么友好的一个widget,在界面上重写一个提示内容显示区又不是产品设计师要的效果。
关键问题呢,是toast实在是太简单太基础了,以至于忘掉要去看一下它的源码。
说到这里要提一下为什么要写这个的原因了,近期手头上有个项目,测试总是吐槽,我这里点了很多下,提示语怎么不消失……大概隔了一分钟吧,提示语没了。内心满满的崩溃,屏幕和你有仇啊,点几十下!!!
总之就是想让我们改,但是我不想改,因为改了也没什么意思啊,关键是没有明确的预期效果。产品设计的时候确实会有部分时间间隔很小的连续提示(虽然应该去避免这些问题,可咱不想搞产品的活)。
可能有些朋友已经想说重写一个类似toast的widget了,奉劝你不要。因为没有明确的需求。
正文:
这里直接进入正题:从设计的角度来说,为了使得交互更加友好,应当将一些重要的信息反馈给用户,按照信息的重要性(以及牵涉到的后续操作),一般有三种提示形式:
1. toast
2. 视图上安置一块信息显示区
3. dialog形式
这三种形式中,1和2对用户的打扰程度是较小的,dialog的打扰程度很大,一般来说,当你必须要用户来做出决定时才会使用dialog形式。
而1和2相比较:Toast维护较为方便,2需要在布局上进行额外的处理,而2的优点在于,开发者可以根据实际需求来维护提示的显示状态。
一般而言,2使用的并不是很多,尽管他的样式可以和应用的主题很相近,原因在于你需要在布局上额外设计一个显示区,比较费神,往往登录界面(或者说需要验证用户输入)的场景才会使用。
而toast的一个问题就是,系统会将toast放置到队列中然后维护其显示。所以出现了测试人员疯狂的点击(产生同样的toast),这条toast会显示很长时间。所以,我们真正的需求(或者封住测试的嘴巴的方案)就是在显示过程中如果紧跟着出现了相同的toast,就不要显示了(这条toast可能是反复操作导致的,让它滚蛋,而其他内容的toast还是保守一些,让它去排队吧)。
然而这个需求想直接使用toast的代码来实现还是不怎么可行的。
可能有些人会有这样一种思路:取消之前的显示,更新新的显示。首先给个粗糙的结论,体验可能不会太好。他们想的是cancel()方法来简单粗暴的取消一个toast的显示,然而这样会带来还没看完,刷新了的问题,以及遗漏一些提示。
可能您已经对toast的源码感兴趣了,我们先看一下api中大致的内容,再看源码,再看看如何实现我们的目的
Constants
int | LENGTH_LONG | Show the view or text notification for a long period of time. |
int | LENGTH_SHORT | Show the view or text notification for a short period of time. |
Public Constructors
Toast(Context context) Construct an empty Toast object. |
Public Methods
void | cancel() Close the view if it's showing, or don't show it if it isn't showing yet. |
int | getDuration() Return the duration. |
int | getGravity() Get the location at which the notification should appear on the screen. |
float | getHorizontalMargin() Return the horizontal margin. |
float | getVerticalMargin() Return the vertical margin. |
View | getView() Return the view. |
int | getXOffset() Return the X offset in pixels to apply to the gravity's location. |
int | getYOffset() Return the Y offset in pixels to apply to the gravity's location. |
static Toast | makeText(Context context, int resId, int duration) Make a standard toast that just contains a text view with the text from a resource. |
static Toast | makeText(Context context, CharSequence text, int duration) Make a standard toast that just contains a text view. |
void | setDuration(int duration) Set how long to show the view for. |
void | setGravity(int gravity, int xOffset, int yOffset) Set the location at which the notification should appear on the screen. |
void | setMargin(float horizontalMargin, float verticalMargin) Set the margins of the view. |
void | setText(int resId) Update the text in a Toast that was previously created using one of the makeText() methods. |
void | setText(CharSequence s) Update the text in a Toast that was previously created using one of the makeText() methods. |
void | setView(View view) Set the view to show. |
void | show() Show the view for the specified duration. |
内容真的很少,set和get一堆属性,maketext是利用几个重要的参数构建并返回一个toast实例,show()方法和cancel()方法。那我们看源码,分析一下toast的显示和消失。
/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import android.app.INotificationManager; import android.app.ITransientNotification; import android.content.Context; import android.content.res.Resources; import android.graphics.PixelFormat; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; /** * A toast is a view containing a quick little message for the user. The toast class * helps you create and show those. * {@more} * * <p> * When the view is shown to the user, appears as a floating view over the * application. It will never receive focus. The user will probably be in the * middle of typing something else. The idea is to be as unobtrusive as * possible, while still showing the user the information you want them to see. * Two examples are the volume control, and the brief message saying that your * settings have been saved. * <p> * The easiest way to use this class is to call one of the static methods that constructs * everything you need and returns a new Toast object. * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For information about creating Toast notifications, read the * <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> developer * guide.</p> * </div> */ public class Toast { static final String TAG = "Toast"; static final boolean localLOGV = false; /** * Show the view or text notification for a short period of time. This time * could be user-definable. This is the default. * @see #setDuration */ public static final int LENGTH_SHORT = 0; /** * Show the view or text notification for a long period of time. This time * could be user-definable. * @see #setDuration */ public static final int LENGTH_LONG = 1; final Context mContext; final TN mTN; int mDuration; View mNextView; /** * Construct an empty Toast object. You must call {@link #setView} before you * can call {@link #show}. * * @param context The context to use. Usually your {@link android.app.Application} * or {@link android.app.Activity} object. */ public Toast(Context context) { mContext = context; mTN = new TN(); mTN.mY = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.toast_y_offset); } /** * Show the view for the specified duration. */ public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext.getPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } } /** * Close the view if it's showing, or don't show it if it isn't showing yet. * You do not normally have to call this. Normally view will disappear on its own * after the appropriate duration. */ public void cancel() { mTN.hide(); // TODO this still needs to cancel the inflight notification if any } /** * Set the view to show. * @see #getView */ public void setView(View view) { mNextView = view; } /** * Return the view. * @see #setView */ public View getView() { return mNextView; } /** * Set how long to show the view for. * @see #LENGTH_SHORT * @see #LENGTH_LONG */ public void setDuration(int duration) { mDuration = duration; } /** * Return the duration. * @see #setDuration */ public int getDuration() { return mDuration; } /** * Set the margins of the view. * * @param horizontalMargin The horizontal margin, in percentage of the * container width, between the container's edges and the * notification * @param verticalMargin The vertical margin, in percentage of the * container height, between the container's edges and the * notification */ public void setMargin(float horizontalMargin, float verticalMargin) { mTN.mHorizontalMargin = horizontalMargin; mTN.mVerticalMargin = verticalMargin; } /** * Return the horizontal margin. */ public float getHorizontalMargin() { return mTN.mHorizontalMargin; } /** * Return the vertical margin. */ public float getVerticalMargin() { return mTN.mVerticalMargin; } /** * Set the location at which the notification should appear on the screen. * @see android.view.Gravity * @see #getGravity */ public void setGravity(int gravity, int xOffset, int yOffset) { mTN.mGravity = gravity; mTN.mX = xOffset; mTN.mY = yOffset; } /** * Get the location at which the notification should appear on the screen. * @see android.view.Gravity * @see #getGravity */ public int getGravity() { return mTN.mGravity; } /** * Return the X offset in pixels to apply to the gravity's location. */ public int getXOffset() { return mTN.mX; } /** * Return the Y offset in pixels to apply to the gravity's location. */ public int getYOffset() { return mTN.mY; } /** * Make a standard toast that just contains a text view. * * @param context The context to use. Usually your {@link android.app.Application} * or {@link android.app.Activity} object. * @param text The text to show. Can be formatted text. * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or * {@link #LENGTH_LONG} * */ 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; } /** * Make a standard toast that just contains a text view with the text from a resource. * * @param context The context to use. Usually your {@link android.app.Application} * or {@link android.app.Activity} object. * @param resId The resource id of the string resource to use. Can be formatted text. * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or * {@link #LENGTH_LONG} * * @throws Resources.NotFoundException if the resource can't be found. */ public static Toast makeText(Context context, int resId, int duration) throws Resources.NotFoundException { return makeText(context, context.getResources().getText(resId), duration); } /** * Update the text in a Toast that was previously created using one of the makeText() methods. * @param resId The new text for the Toast. */ public void setText(int resId) { setText(mContext.getText(resId)); } /** * Update the text in a Toast that was previously created using one of the makeText() methods. * @param s The new text for the Toast. */ public void setText(CharSequence s) { if (mNextView == null) { throw new RuntimeException("This Toast was not created with Toast.makeText()"); } TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message); if (tv == null) { throw new RuntimeException("This Toast was not created with Toast.makeText()"); } tv.setText(s); } // ======================================================================================= // All the gunk below is the interaction with the Notification Service, which handles // the proper ordering of these system-wide. // ======================================================================================= private static INotificationManager sService; static private INotificationManager getService() { if (sService != null) { return sService; } sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification")); return sService; } private static class TN extends ITransientNotification.Stub { final Runnable mShow = new Runnable() { public void run() { handleShow(); } }; final Runnable mHide = new Runnable() { public void run() { handleHide(); // Don't do this in handleHide() because it is also invoked by handleShow() mNextView = null; } }; private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); final Handler mHandler = new Handler(); int mGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; int mX, mY; float mHorizontalMargin; float mVerticalMargin; View mView; View mNextView; WindowManagerImpl mWM; TN() { // XXX This should be changed to use a Dialog, with a Theme.Toast // defined that sets up the layout params appropriately. final WindowManager.LayoutParams params = mParams; params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; params.format = PixelFormat.TRANSLUCENT; params.windowAnimations = com.android.internal.R.style.Animation_Toast; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); } /** * schedule handleShow into the right thread */ public void show() { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.post(mShow); } /** * schedule handleHide into the right thread */ public void hide() { if (localLOGV) Log.v(TAG, "HIDE: " + this); mHandler.post(mHide); } public void handleShow() { if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView=" + mNextView); if (mView != mNextView) { // remove the old view if necessary handleHide(); mView = mNextView; mWM = WindowManagerImpl.getDefault(); final int gravity = mGravity; mParams.gravity = gravity; if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { mParams.horizontalWeight = 1.0f; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { mParams.verticalWeight = 1.0f; } mParams.x = mX; mParams.y = mY; mParams.verticalMargin = mVerticalMargin; mParams.horizontalMargin = mHorizontalMargin; if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this); mWM.addView(mView, mParams); trySendAccessibilityEvent(); } } private void trySendAccessibilityEvent() { AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mView.getContext()); if (!accessibilityManager.isEnabled()) { return; } // treat toasts as notifications since they are used to // announce a transient piece of information to the user AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); event.setClassName(getClass().getName()); event.setPackageName(mView.getContext().getPackageName()); mView.dispatchPopulateAccessibilityEvent(event); accessibilityManager.sendAccessibilityEvent(event); } public void handleHide() { if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView); 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) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } mView = null; } } } } |
从show方法和cancel方法,我们发现了有个内部类TN,成员函数中有如下两个:
/** * schedule handleShow into the right thread */ publicvoid show() { if (localLOGV) Log.v(TAG,"SHOW: " + this); mHandler.post(mShow); } /** * schedule handleHide into the right thread */ publicvoid hide() { if (localLOGV) Log.v(TAG,"HIDE: " + this); mHandler.post(mHide); } |
final RunnablemShow = new Runnable() { publicvoid run() { handleShow(); } }; final RunnablemHide = new Runnable() { publicvoid run() { handleHide(); // Don't do this in handleHide() because it is also invoked by handleShow() mNextView = null; } }; publicvoid handleShow() { if (localLOGV) Log.v(TAG,"HANDLE SHOW: " + this +" mView=" + mView + " mNextView=" + mNextView); if (mView !=mNextView) { // remove the old view if necessary handleHide(); mView = mNextView; mWM = WindowManagerImpl.getDefault(); finalint gravity = mGravity; mParams.gravity = gravity; if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { mParams.horizontalWeight = 1.0f; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { mParams.verticalWeight = 1.0f; } mParams.x =mX; mParams.y = mY; mParams.verticalMargin = mVerticalMargin; mParams.horizontalMargin = mHorizontalMargin; if (mView.getParent() !=null) { if (localLOGV) Log.v(TAG,"REMOVE! " + mView +" in " + this); mWM.removeView(mView); } if (localLOGV) Log.v(TAG,"ADD! " + mView +" in " + this); mWM.addView(mView, mParams); trySendAccessibilityEvent(); } } privatevoid trySendAccessibilityEvent() { AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mView.getContext()); if (!accessibilityManager.isEnabled()) { return; } // treat toasts as notifications since they are used to // announce a transient piece of information to the user AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); event.setClassName(getClass().getName()); event.setPackageName(mView.getContext().getPackageName()); mView.dispatchPopulateAccessibilityEvent(event); accessibilityManager.sendAccessibilityEvent(event); } publicvoid handleHide() { if (localLOGV) Log.v(TAG,"HANDLE HIDE: " + this +" mView=" + mView); 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) { if (localLOGV) Log.v(TAG,"REMOVE! " + mView +" in " + this); mWM.removeView(mView); } mView = null; } } |
mWM = WindowManagerImpl.getDefault(); //显示 mWM.addView(mView,mParams); //隐藏 if (mView.getParent() !=null) { if (localLOGV) Log.v(TAG,"REMOVE! " + mView +" in " + this); mWM.removeView(mView); } |
再把话头转回来,显示的时候,调用了
service.enqueueToast(pkg, tn, mDuration); |
INotificationManager service = getService(); // staticprivate INotificationManager getService() { if (sService !=null) { returnsService; } sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification")); returnsService; } |
/** @hide */ oneway interface ITransientNotification { void show(); void hide(); } |
好了,先不看源码了。我们之前说简单粗暴的cancel掉体验可能不好,我们可以这样尝试:
/** * @ClassName: MToast * @Description:TODO * @date 2016年2月1日下午1:26:43 * * @author leobert.lan * @version 1.0 */ publicclass MToast { privatestatic String preMsg =""; privatefinal staticint shortTime = 2000; privatefinal staticint longTime = 3500; privatestatic booleanonShowing = false; privatestatic Object syncLock =new Object(); privatestatic ArrayList<ToastJob> toastList =new ArrayList<ToastJob>(); @SuppressLint("ShowToast") publicstatic void show(Context c,int sRid, Duration d) { int duration = 0; long time = 0; switch (d) { casel: duration = Toast.LENGTH_LONG; time = longTime; break; cases: duration = Toast.LENGTH_SHORT; time = shortTime; break; default: break; } String msg = c.getResources().getString(sRid); if (msg.equals(preMsg) &&onShowing) return; preMsg = msg; ToastJob job = new ToastJob(); job.setToast(Toast.makeText(c, msg, duration)); job.setTime(time); synchronized (syncLock) { toastList.add(job); callShow(); } } privatestatic synchronizedvoid callShow() { synchronized (syncLock) { if (!onShowing &&toastList.size() > 0) { toastList.get(0).getToast().show(); onShowing =true; new Handler().postDelayed(new Runnable() { @Override publicvoid run() { onShowing =false; callShow(); } }, toastList.get(0).getTime()); toastList.remove(0); } } } publicenum Duration { l,s; } staticclass ToastJob { private Toasttoast; privatelong time; public Toast getToast() { returntoast; } publicvoid setToast(Toast toast) { this.toast = toast; } publiclong getTime() { returntime; } publicvoid setTime(long time) { this.time = time; } } } |
然后呢,就突然衍生了一个自定义显示时长的toast的想法:
import java.util.Timer; import java.util.TimerTask; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.PixelFormat; import android.view.Gravity; import android.view.View; import android.view.WindowManager; import android.widget.Toast; /** * @ClassName: CustomToast * @Description:TODO * @date 2016年2月1日下午4:40:21 * * @author leobert.lan * @version 1.0 */ publicclass CustomToast { private WindowManagermWM; /** * time:自定义的显示时间 */ privatelong time; /** * mView:toast的view */ private ViewmView; /** * params:toast的布局参数 */ private WindowManager.LayoutParamsparams; /** * timer:计时任务,控制 */ private Timertimer; @SuppressLint("ShowToast") private CustomToast(Context context, String text,long time) { mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); timer =new Timer(); Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); mView = toast.getView(); params =new WindowManager.LayoutParams(); params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; params.windowAnimations = toast.getView().getAnimation().INFINITE; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; params.y = 100; this.time = time; } publicstatic CustomToast makeText(Context context, String text,long time) { CustomToast toastCustom = new CustomToast(context, text, time); return toastCustom; } publicvoid show() { mWM.addView(mView,params); timer.schedule(new TimerTask() { @Override publicvoid run() { mWM.removeView(mView); } }, time); } publicvoid cancel() { mWM.removeView(mView); timer.cancel(); } } |
就到这里吧,本文系作者原创,转载请附原文地址:http://blog.csdn.net/a774057695/article/details/50619599
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories