最近看到折800刷新成功弹出的效果挺好看的,就想着自己也实现一下,下面是折800的刷新效果
感觉这个可以用popupwindow或者toast来实现,就用toast来实现了。布局比较简单,不贴图了,直接贴上代码:
public void showToast() { LayoutInflater inflater = LayoutInflater.from(BaseApplication.get()); View layout = inflater.inflate(R.layout.toast_refresh_layout, null); TextView textView = (TextView)layout.findViewById(R.id.tvrefresh); textView.setText(getResources().getString(R.string.refresh)); Toast toast = new Toast(mContext); toast.setDuration(Toast.LENGTH_SHORT); toast.setGravity(Gravity.TOP|Gravity.FILL_HORIZONTAL, 0 , UnitUtils.dip2pix(mContext,50)); toast.setView(layout); toast.show(); }
有必要对Toast做一下总结了,平时用到toast的时候基本都是
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
简单看了下源码:
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; }
代码很简单,就是inflater一个view,transient_notification这个layout中只有一个textview,所以平时弹出的toast都是文字,然后看一下show方法:
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 } }
看到getservice方法,很明显这是一个系统服务,下面追一下:
static private INotificationManager getService() { if (sService != null) { return sService; } sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification")); return sService; }
写过aidl的应该都知道,这是就是实现一个InotificationManager的代理类,相当于binder中的bpserver,binder的详细讲解可以参考罗升阳的csdn博客。这里也就很清晰了,toast其实和notification通知是一家的,只不过显示形式不同罢了。TN是INotificationManager的实现类:
private static class TN extends ITransientNotification.Stub
抽取一些关键的代码:
//设定位置,默认值就是这个,所以默认的toast是居下的 int mGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
在代码中可以通过设置gravity来控制toast的位置:
Toast toast = Toast.makeText(this, R.string.custom_text, Toast.LENGTH_SHORT); //toast显示到顶部,靠左显示 toast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 100); toast.show();
setGravity这个方法设置toast将要显示的屏幕位置,这个方法传参的含义是:
第一个参数,设定Toast显示位置的锚点,就是Toast这个视图的初始位置。
第二个参数,设定Toast从锚点开始,在屏幕坐标X轴上的偏移量,向右是正数,向左是负数。
第三个参数,设定Toast从锚点开始,在屏幕坐标Y轴上的偏移量,向下时正数,向上市负数。
代码内,我们设置它的锚点是屏幕的左上角,X轴方向偏移为0,Y轴方向,向下偏移100。我们来看看具体的效果图:
接着看源码,toast的layout的参数设置:
final WindowManager.LayoutParams params = mParams; //下面是高和宽,都是wrap,怪不得我在xml自定义toast布局fill还是充满不了 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; //这里是toast的动画效果 params.windowAnimations = com.android.internal.R.style.Animation_Toast; //type就是toast类型的,很多悬浮框都是更改的这个type,一般悬浮框都是TYPE_PHONE或者TYPE_SYSTEM_ALERT params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast");
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(); //设置自定义view的地方,通过setview给mNextView赋值 mView = mNextView; mWM = WindowManagerImpl.getDefault(); final int gravity = mGravity; mParams.gravity = gravity; //下面的是重点,原来gravity设置成FILL_HORIZONTAL才会充满横屏 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(); } }
上面是添加View的代码,比较简单,看一下就行了,而且还有自定义Toast的View的功能。
自定义Toast视图,不用makeText,直接获取Toast对象,设置view界面。
LayoutInflater inflater = getLayoutInflater(); View layout = inflater.inflate(R.layout.custom_toast,null); Toast toast = new Toast(getApplicationContext()); toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0); toast.setDuration(Toast.LENGTH_LONG); toast.setView(layout); toast.show();
说明一下,Toast的show和hide方法实现是基于Handler机制,直接new的Handler对象,所以,当我们在主线程(也就是UI线程中)可以随意调用Toast.makeText方法,因为Android系统帮我们实现了主线程的Looper初始化。但是,如果你想在子线程中调用Toast.makeText方法,就必须先进行Looper初始化了,不然就会报出java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 。
public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } //默认调用当前线程的looper mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; }
最后说明一点,Toast的动画效果,也就是上面设置的默认com.android.internal.R.style.Animation_Toast,如果需要更改动画,我没有找到比较好的版本,暂时只能自己写一个自定义的Toast来代替,源码地址:
https://github.com/xuwt/CustomerToast
Toast源码解析参考:http://blog.csdn.net/wzy_1988/article/details/43341761