项目实战:WindowManager中removeView的那些坑-随心所欲removeView
来源:互联网 发布:在线购物商城系统源码 编辑:程序博客网 时间:2024/05/29 12:39
正所谓没有遇到过类似于not attached to window manager、Android removeView view must not be null的开发者不是好工程师,今天我们就来看看WindowManager中removeView的那些坑。
其实也没必要细说它有多坑,只要避过这些坑即可。
经过这一段时间的项目开发,小结一条真理:
不要在像Activity这样有生命周期的东东里乱写东西,不然莫名其妙的被销毁,然后新建,但早已经是物是人非,最后就是状态异常、空指针各种Crash各种坑。
我们要实现的效果是:点击显示Button-addView,点击消除Button-removeView,
哈哈,不要喷,我想removeView可是想在哪就在哪,随心所欲,游离于Activity的生命周期之外~
直接上代码,将WindowManager抽离出来即可,一个小Demo:
首先添加相应权限:
<span style="font-size:18px;"> <!-- 显示顶层浮窗 --> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /></span>
<span style="font-size:18px;"><?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" > <Button android:id="@+id/show_btn_main" android:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="true" android:text="button_show" android:textSize="18sp" /> <Button android:id="@+id/remove_btn_main" android:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="true" android:text="button_hide" android:textSize="18sp" /></LinearLayout></span>
<span style="font-size:18px;">/** * Created by 权兴权意 on 2016/9/19. */import android.content.Context;import android.graphics.PixelFormat;import android.view.Gravity;import android.view.WindowManager;import android.view.WindowManager.LayoutParams;import android.widget.ImageView;import android.widget.Toast;public class WindowUtils { private static ImageView mView = null; private static WindowManager mWindowManager = null; private static Context mContext = null; public static void showWindow(final Context context) { Toast.makeText(context,"showPopupWindow",Toast.LENGTH_SHORT).show(); // 获取应用的Context mContext = context.getApplicationContext(); // 获取WindowManager mWindowManager = (WindowManager) mContext .getSystemService(Context.WINDOW_SERVICE); mView = new ImageView(context); mView.setImageResource(R.mipmap.ic_launcher); final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); // 类型 params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; // 设置flag // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件 params.flags = params.flags|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 不设置这个弹出框的透明遮罩显示为黑色 params.format = PixelFormat.TRANSLUCENT; // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口 // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按 params.width = LayoutParams.WRAP_CONTENT; params.height = LayoutParams.WRAP_CONTENT; params.gravity = Gravity.CENTER; mWindowManager.addView(mView, params); } public static void removeWindow() { mWindowManager.removeViewImmediate(mView); }}</span>
/** * Created by 权兴权意 on 2016/9/19. */importimport android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity { private Button show_btn_main; private Button remove_btn_main; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); show_btn_main = (Button) findViewById(R.id.show_btn_main); show_btn_main.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { WindowUtils.showWindow(MainActivity.this); } }); remove_btn_main = (Button) findViewById(R.id.remove_btn_main); remove_btn_main.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { WindowUtils.removeWindow(); } }); }}
ok,大功告成,看看效果吧。
最后,提一下removeView和removeViewImmediate的区别:
问题及原因:
我们在做UI相关的代码时有时候会碰到WindowLeak,也就是所谓的窗体泄露,泄露的原因是因为androidUI操作在主线程中操作,但是我们会需要在一些线程或者异步任务中操作UI界面元素的需求,那么这个时候可能会出现类似问题。我在做浮动窗口的时候碰到了这个问题,浮动窗口需要用到WindowManager,windowManger又是一个activity的一个变量,它依存于Activity,当横竖屏切换或者activity销毁的时候这个变量会销毁。销毁的时候导致windowmanager通过AddView()方法添加的View没有依存,导致窗体泄露。那么问题来了,为什么这里会泄露了?
解决方法:
我在onDestroy()里面调用了removeView方法,想要避免窗体泄露,但是这个方法并不管用,后来换成removeViewImmediate()就解决了这个问题,原因就是两个方法设计到线程同步问题,removeViewImmediate()是通知View立刻调用View.onDetachWindow(),这说明这个方法是通过一个监听或者观察者来实现的,因为线程的同步跟异步问题导致activity销毁了,但view还没有被remove完,于是就产生了所谓的窗体泄露。说到这里,我想大家也能明白这两个方法的区别了。
话说回来,最好的方法还是抽离。
- 项目实战:WindowManager中removeView的那些坑-随心所欲removeView
- android WindowManager中removeView(View v)与removeViewImmediate(View v)的区别
- WindowManager removeView抛出异常android.view.WindowLeaked
- View.GONE 和 removeView的区别
- 简单的removeView和AddView测试
- 在onAnimationEnd方法中调用 viewgroup.removeview,报 nullpointerexception异常的解决方法
- Android开发中,调用removeView报NullpointerException错
- windowmanger的addview,removeView方法出现的异常
- 对话框show问题,需要removeView,
- You must call removeView() on the child's parent first错误的解决方法
- 关于异常“The specified child already has a parent. You must call removeView"的解决
- Android关于You must call removeView() on the child's parent first的解决
- removeView(view)不能移除控件问题
- You must call removeView() on the child'
- Animation导致removeView无效(源码分析)
- View[6] inflate()、addView()removeView() 及 LayoutParams
- 嵌套Fragment的使用及遇到The specified child already has a parent. You must call removeView()问题的解决
- 嵌套Fragment的使用及遇到The specified child already has a parent. You must call removeView()问题的解决
- “个性化视频推荐”算法的Storm实现方案
- HDU5900 QSC and Master(区间dp)
- 为什么Scrum永远不会可行
- bzoj 1026: [SCOI2009]windy数 (数位dp)
- 超像素分割技术发展情况梳理(Superpixel Segmentation)
- 项目实战:WindowManager中removeView的那些坑-随心所欲removeView
- 创建maven工程
- 汇编语言寻址方式
- cuda 程序
- PAT 1074. Reversing Linked List (25)(链表反转)
- HDU 5901 Count Primes (模板 + 数论知识)——2016 ACM/ICPC Asia Regional Shenyang Online
- Java数据结构之双向链表
- JavaScript的核心
- win7安装iis及web配置教程