Android Toast的学习与简单应用
来源:互联网 发布:杰科网络电视机顶盒r1 编辑:程序博客网 时间:2024/04/30 13:26
关于Toast我们应该很是熟悉了,通过Toast.makeText(context ,text,duration).show() ;就可以很容易显示一个Toast。最近在写一个悬浮框的一个小功能,开始时
啥也不知道,根本无从下手。随后在网上搜了一大堆的资料,各有各的说法,不过大部分都是说添加一个权限(<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />) 并结合WindowManager来显示。这个我测试了一下好像不行(permission denied),当时用的是
Android 5.1 的系统。不知道是不是用法没用对,不管那么多了。最后在Toast身上找到了一线生机,现将学习笔记记录如下:
在使用Toast时可以发现,Toast可以在所有View的最上面出现,也就是它不会被覆盖而一直在最顶层。现在知道了这个特性,那就开始从Toast的源码中去找我们的
答案了
Toast.makeText(context ,text,duration).show() ;这句代码闭着眼都能写出来了吧
先来看一看makeText()方法,源码如下:
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);//将<span style="font-family: Arial, Helvetica, sans-serif;">com.android.internal.R.layout.transient_notification打成View</span> TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message); tv.setText(text); result.mNextView = v; result.mDuration = duration; return result; }其实上面的代码很简单,总结一下就是做了如下几件事:
1、将com.android.internal.R.layout.transient_notification制成view
2、在view找到一个TextView(布局文件里只有一个TextView),用来显示我们传进来的text
3、将view赋值给mNextView ,将传进来的duration赋给成员变量。最后返回一个Toast实例result
接着看看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 } }获得了一个service,看到INotificationManager 这样的东西我就头痛,应该是调用了一个远程的接口,最后在try模块里使用service.enqueueToast(pkg, tn, mDuration);这是个啥呢,其实一开始我也不知道是什么东西,不过看单词enqueue应该可以猜到是加入了一个Toast队列之中。这么猜应该是对的,因为在我们点击
事件来显示Toast时,可以发现点击多少下就会显示几次Toast,而且都是显示完一个才会显示下一个。
好了,show方法就是获得一个远程接口并将Toast加入了一个队列。并没有看到显示的方法啊。别着急,还有一个TN。TN是又是个啥?找出源码看看不就知道了
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 = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 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.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 */ @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(); 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; 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(); } }
<pre name="code" class="html"> 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; } }
一开始看到也是头大得不行,都写的什么呀!一开头写了两个Runnable。也不知道是干什么用的,一个叫mShow,一个叫mHide。往下看在构造中初始化了一些
WindowManager.LayoutManager的参数。
现在开始注意一个叫handleShow的方法,看着就像是用来显示Toast的方法,其实也就是它了。在该方法里初始化了一个WindowManager了。主角终于来了!
中间都是一些参数的设置,不去管它,看看handleShow倒数第二行代码mWM.addView(mView,mParas).注意到handleShow方法的开头有一句mView = mNextView,mNextView不就是makeText()中的TextView么。OK,这里将mView添加进了mWM中了。这样我们的Toast就显示出来了。
再来看看handleHide,只是将mView从mWM中移除了,这样mView就消失了。
注:ITransientNotification.Stub中有两个方法show()和hide().
这里小结一下:
1、inflate一个布局,找到里面的TextView,并给它设置我们传进来的text
2、获取一个Toast队列,并将tn和duration加入到一个队里里面,排队去显示
3、TN中初始化了一些WindowManager.LayoutParams的一些参数,并创建了一个WindowManager对象,将mView添加到WindowManager中。
4、最后就是handHide,将View移除。
通过以上的步骤就完成了Toast的显示和消失。
到了这里有没有发现Toast显示奥妙了?其实很简单,就是先创建一个WindowManager,再配套一个WindowManager.LayoutParams。通过WindowManager.add
来显示一个View(这里的View可以自己随便写,最后inflate成View就OK,或者直接new),通过remove让view消失。
下面就来写一个可拖动的小Demo:
废话就不多说了,直接贴代码:
需要显示的View的布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#000000" > <com.exapmle.windowdemo.ScrollButton android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="悬浮框"/> </LinearLayout>ScrollButton的代码
public class ScrollButton extends Button {private int startX;private int startY;private int moveX;private int moveY;private boolean isClick ;private OnLocationChangeListener mOnLocationChangeListener;private Context context ;public ScrollButton(Context context) {this(context, null);}public ScrollButton(Context context, AttributeSet attrs) {super(context, attrs);this.context = context ;}public interface OnLocationChangeListener {void upDateLocation(int dx, int dy);}public void setFloatWindowChangeManager(OnLocationChangeListener listener) {mOnLocationChangeListener = listener;}@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:startX = (int) event.getRawX();startY = (int) event.getRawY();isClick = true ;break;case MotionEvent.ACTION_MOVE:moveX = (int) event.getRawX();moveY = (int) event.getRawY();if ((Math.abs(moveX - startX)) >= 10 || (Math.abs(moveY - startY) >= 10)) {mOnLocationChangeListener.upDateLocation(moveX - startX, moveY - startY);startX = moveX ;startY = moveY ;isClick = false ;}break;case MotionEvent.ACTION_UP:if (isClick) {Toast.makeText(context, "被点击了", Toast.LENGTH_SHORT).show();}break;}return true ;}}这里将启动悬浮框的代码写在了service中
public class FloatService extends Service implements OnLocationChangeListener {private WindowManager mManager;private WindowManager.LayoutParams mParams;private View view;private com.gemdale.myfloatwindowdemo.ScrollButton btn;@Overridepublic IBinder onBind(Intent intent) {return null;}@SuppressLint({ "InflateParams", "InlinedApi" })@Overridepublic void onCreate() {super.onCreate();addWindowView();}/** * windowManager.addView(view,params) 的显示周期与相应的context的存在相关。activity只能是该activity存在时view显示,销毁,view消失 */private void addWindowView() {mManager = (WindowManager) getApplicationContext().getSystemService(Application.WINDOW_SERVICE);mParams = new WindowManager.LayoutParams();mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;mParams.gravity=Gravity.TOP|Gravity.LEFT ;//使用TYPE_TOASTmParams.type = WindowManager.LayoutParams.TYPE_TOAST;//WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 不抢焦点,若不设置该flag,back事件无效//WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 手机屏幕打开时,改window一直显示//WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 当该window在可以接受触摸屏情况下,让因在该window之外,而发送到后面的window的触摸屏可以支持split touch.//WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 即使在该window在可获得焦点情况下,仍然把该window之外的任何event发送到该window之后的其他window.mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SPLIT_TO UCH | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;initViewConfig() ;mManager.addView(view, mParams);}private void initViewConfig() {view = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.float, null);btn = (com.gemdale.myfloatwindowdemo.ScrollButton) view.findViewById(R.id.btn);btn.setFloatWindowChangeManager(this);}@Overridepublic void onDestroy() {mManager.removeView(view);mParams = null;mManager = null;super.onDestroy();}@Overridepublic void upDateLocation(int dx, int dy) {mParams.x += dx;mParams.y += dy;mManager.updateViewLayout(view, mParams);//不断的去更新mManager的位置}}
最后是MainActivity了,就两个button,
@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.open:Intent intent = new Intent(MainActivity.this, FloatService.class);startService(intent);break;case R.id.close:Intent intent = new Intent(MainActivity.this, FloatService.class);stopService(intent);break;}}到了这里,一个简单的可显示可拖动的悬浮框就完成了。
此篇文章仅为个人的学习笔记,参考了很多大家贡献的文章。若有雷同请勿怪,若有错误请指正,谢谢!
- Android Toast的学习与简单应用
- Toast的简单应用
- 【Android成长之路】Toast的简单应用
- Android学习笔记之Button,Toast,menu的简单用法
- Android学习实践:8.简单的屏幕提示Toast
- Android 中的Toast的应用
- Android中的Toast的应用
- Android--Toast的简单封装
- Android应用开发学习—Toast使用方法
- Android基础教程(三)之-----简单的Button事件响应综合提示控件Toast的应用
- Android基础教程(三)之-----简单的Button事件响应综合提示控件Toast的应用
- Android基础教程(三)之-----简单的Button事件响应综合提示控件Toast的应用
- Android基础教程(三)之-----简单的Button事件响应综合提示控件Toast的应用
- Android基础教程(三)之-----简单的Button事件响应综合提示控件Toast的应用
- Android基础教程之简单的Button事件响应综合提示控件Toast的应用
- 简单的menu与Toast的使用
- android开发之Toast的多种应用
- android中Toast 的简单用法
- BZOJ 3531 SDOI2014 旅行(travel) 树链剖分模板题
- Observable和Observer
- 全排列 枚举
- VC6.0 fatal error C1853: 'Debug/xx.pch' is not a precompiled header
- hdu5014——构造打表找规律
- Android Toast的学习与简单应用
- 解决Android中的SQLite数据库并发访问
- Mac下Cornerstone无法查看SVN日志的问题的解决办法
- Java——JDOM方式生成XML
- Linux:使用awk命令获取文本的某一行,某一列
- gradle使用学习(一)————环境与配置
- arm-linux-gcc.tgz安装
- 【面试笔试算法】Program 2:Amusing Digits(网易游戏笔试题)
- 多维数据模型