android 自己通过WindowManager实现Toast
来源:互联网 发布:方阵和矩阵的区别 编辑:程序博客网 时间:2024/06/08 15:00
前言:关于android Toast你了解多少?Toast的实现原理是什么?如何实现自己的Toast?现在我就带你实现自己的Toast!我们可以实现以下功能:
1.自定义Toast的样式,可以增加button,checkbox等等
2.可以完成控制Toast的显示时间已经显示位置,并且可以动态的改变Toast的位置
3.可以完成控制Toast的显示动画和消失动画,可能还有意向不到的收获,实现一些复杂动画
1.Toast 原理
看android源码我们可以知道,Toast的底层实现是通过WindowManager实现的我相信大家都对WindowManager有了一定的了解,如果不了解自行了解。查找源码Toast可以看到有一个叫做mTN的成员变量,这个变量至关重要Toast的显示和隐藏都是通过这个变量处理的,下面贴出TN的源码就可以发现Toast的真相;
private static class TN extends ITransientNotification.Stub { final Runnable mShow = new Runnable() { @Override public void run() { handleShow(); } }; final Runnable mHide = new Runnable() { @Override 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; int mX, mY; float mHorizontalMargin; float mVerticalMargin; View mView; View mNextView; WindowManager 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.format = PixelFormat.TRANSLUCENT; params.windowAnimations = com.android.internal.R.style.Animation_Toast; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; /// M: [ALPS00517576] Support multi-user params.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; } /** * schedule handleShow into the right thread */ @Override public void show() { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.post(mShow); } /** * schedule handleHide into the right thread */ @Override 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; Context context = mView.getContext().getApplicationContext(); String packageName = mView.getContext().getOpPackageName(); if (context == null) { context = mView.getContext(); } mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); // We can resolve the Gravity here by using the Locale for getting // the layout direction final Configuration config = mView.getContext().getResources().getConfiguration(); final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection()); 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; mParams.packageName = packageName; 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; } } }
现在大家明白了吧!
2.废话不多说了,现在开始实现我们自己的Toast。先上源码:
import android.content.Context;import android.graphics.PixelFormat;import android.os.Handler;import android.view.Gravity;import android.view.View;import android.view.WindowManager;import android.widget.RelativeLayout;import android.widget.TextView;public class CustomizeToast { public static final int LENGTH_LONG = 3500; public static final int LENGTH_SHORT = 2000; private WindowManager mWindowManager; private WindowManager.LayoutParams mWindowParams; private Context mContext; private Handler mHandler; private RelativeLayout container; private TextView defaultToastView; private String content; private long duration = LENGTH_SHORT; private boolean isAdd; private final Runnable dismissRunnable = new Runnable() { @Override public void run() { dismiss(); } }; public CustomizeToast(Context context) { Context ctx = context.getApplicationContext(); if (ctx == null) { ctx = context; } this.mContext = ctx; init(); } private void init() { mHandler = new Handler(); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mWindowParams = new WindowManager.LayoutParams(); mWindowParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mWindowParams.alpha = 1.0f; mWindowParams.width = WindowManager.LayoutParams.MATCH_PARENT; mWindowParams.height = WindowManager.LayoutParams.MATCH_PARENT; mWindowParams.format = PixelFormat.TRANSLUCENT; mWindowParams.type = WindowManager.LayoutParams.TYPE_TOAST; mWindowParams.packageName = mContext.getPackageName(); container = new RelativeLayout(mContext); } public void show() { //这个defaultView实现的比较粗糙,请各位自己实现好看的Toast RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams((int) (Utils.getDeviceWidth(mContext) * 0.9), RelativeLayout.LayoutParams.WRAP_CONTENT); lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE); lp.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE); lp.bottomMargin = (int) (Utils.getDeviceHeight(mContext) * 0.15); if (defaultToastView == null) { defaultToastView = new TextView(mContext); } defaultToastView.setText(content); defaultToastView.setGravity(Gravity.CENTER); defaultToastView.setMaxWidth((int) (Utils.getDeviceWidth(mContext) * 0.9)); defaultToastView.setBackgroundColor(mContext.getResources().getColor(R.color.grey)); defaultToastView.setPadding(0, 10, 0, 10); show(defaultToastView, lp, true, duration); } public void show(View view, RelativeLayout.LayoutParams lp, boolean autoRemove, long duration) { dismiss(); mWindowManager.addView(container, mWindowParams); container.addView(view, lp); isAdd = true; if (autoRemove) { mHandler.postDelayed(dismissRunnable, duration); } } public void dismiss() { if (isAdd) { isAdd = false; container.removeAllViews(); mWindowManager.removeView(container); mHandler.removeCallbacks(dismissRunnable); } } public CustomizeToast setContent(String content) { this.content = content; return this; } public CustomizeToast setDuration(long duration) { this.duration = duration; return this; } public static class Toast { public static CustomizeToast makeText(Context context, String content, long duration) { CustomizeToast customizeToast = new CustomizeToast(context); customizeToast.setContent(content); customizeToast.setDuration(duration); return customizeToast; } }}
思路大概是这样的:我们定义一个WindowManager充满屏幕,然后在WindowManager里放一个顶层容器RelativeLayout,然后我们想要的显示的View是放在Relative里面。这样我们就可以随意的控制自己想要显示的View,这样我们就可以完成非常复杂的动画效果,我们也可以实现360清理球的效果。
3.关于使用:
你可以像使用系统Toast一样的调用,例如:
CustomizeToast.Toast.makeText(getContext(), “中华人民共和国”, CustomizeToast.LENGTH_SHORT).show();
同时,如果有更加复杂的需求,你可以调用像使用一般的class一样的使用它,这样使用更加灵活,例如:
CustomizeToast toast = new CustomizeToast(this);
//toast.show(View view, RelativeLayout.LayoutParams lp, boolean autoRemove, long duration) ;
//view 可以通过一个layout文件定义一个复杂的View
//lp 可以控制view 的初始位置
//autoRemove 代表是否自动消失
//duration 如果autoRemove为false,表示显示的时间
//然后,可以直接对View 进行操作。
好了,源码都告诉你了,就这样散了吧!
- android 自己通过WindowManager实现Toast
- iOS通过UIAlertView实现Android Toast效果
- 实现自己的Toast
- 通过GridView实现Toast
- 安卓-使用WindowManager实现类似Toast效果
- 自定义Toast之WindowManager
- 通过WindowManager实现拖动悬浮框
- 通过WindowManager实现可拖拽的View
- android WindowManager实现悬浮窗口
- Android 自定义Toast实现
- Android Toast源码实现
- Toast分析——实现自己的Toast
- Toast分析——实现自己的Toast
- Toast分析——实现自己的Toast,toast分析实现
- Android之定制自己的Toast
- Android中如何自己定义吐司(Toast)
- Android之悬浮窗口实现(WindowManager)
- Android之悬浮窗口实现(WindowManager)
- iOS开发 UI UIScrollView和UIPageController
- Scrapy 示例 —— Web 爬虫框架
- 用户邮箱进行注册思路
- 常见的页面之间的传值
- iOS学习之路-简单汤姆猫
- android 自己通过WindowManager实现Toast
- Nginx简介
- ldr与adr的区别
- 算法洗脑系列(8篇)——第五篇 分治思想
- 华为机试——单词倒排
- Ubuntu挂载共享文件和虚拟硬盘
- linux下源码安装nginx
- 投稿时Cover Letter的重要性
- 指针的指针...