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 进行操作。
好了,源码都告诉你了,就这样散了吧!

0 0
原创粉丝点击