Android 窗口添加机制系列3-代码实例
2015-11-21 19:12
603 查看
有了上两篇做基础,那么来点干货
Part1:开发APP时设置Activity全屏常亮的一种办法(设置Activity也就是Activity的Window)
Part2:App开发中弹出软键盘时下面的输入框被软件盘挡住问题的解决办法
在Activity中的onCreate中setContentView之前写如下代码:
Part3:创建悬浮窗口(仿IPhone的小圆点或者魅族的小白点或者360手机卫士的小浮标),退出当前Activity依旧可见的一种实现方法
此时在就算该进程退到后台,那么在手机屏幕的右下角也会有个图标,如果点击该图标,就会启动隐式的activity呢。
有时候我们会发现Toast弹出过多就会延迟显示,因为上面源码分析可以看见Toast.makeText是一个静态工厂方法,每次调用这个方法都会产生一个新的Toast对象,当我们在这个新new的对象上调用show方法就会使这个对象加入到NotificationManagerService管理的mToastQueue消息显示队列里排队等候显示;所以如果我们不每次都产生一个新的Toast对象(使用单例来处理)就不需要排队,也就能及时更新了
连续点击多次10次,那么10个toast依次出现,只有等上一个toast消失了,才显示下一个toast。同时此时可能发生内存泄漏,如果此时点击10次之后,立马退出app,那么这个activity便不能被GC回收,一直需要等到10个toast全部显示完,为了防止内存泄漏,此时可以把this,换成getApplicationContext()
解决方案:
单例的SingleToast,内部持有Toast对象引用
为了防止内存泄漏,不管makeText方法传进来的是context,还是ApplicationContext,都把它转换为应用程序上下文context.getApplicationContext(),所以可以有效的防止内存泄漏
使用SingleToast.makeText(getApplicationContext(), content, Toast.LENGTH_LONG).show(); 因为使用的是同一个单例的toast,所以可以做到toast快速的刷新,而不需要一个个排队显示了
java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
本质原因: dialog/toast内部都持有handler引用
子线程内部,如果没做任何处理,所以Looper.myLooper()当然是null了,所以报异常
解决方案:
缺点:
这样的话,MyThread线程的话,一直就停止不了,被一直阻塞在
WindowManager.LayoutParams分析引出的应用层开发常用经典实例
这里的案例直接引用 Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析 这里的例子Part1:开发APP时设置Activity全屏常亮的一种办法(设置Activity也就是Activity的Window)
public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置Activity的Window为全屏,当然也可以在xml中设置 Window window = getWindow(); WindowManager.LayoutParams windowAttributes = window.getAttributes(); windowAttributes.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN | windowAttributes.flags; window.setAttributes(windowAttributes); //设置Activity的Window为保持屏幕亮 window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_main); } }
Part2:App开发中弹出软键盘时下面的输入框被软件盘挡住问题的解决办法
在Activity中的onCreate中setContentView之前写如下代码:
//你也可以在xml文件中设置,一样的效果 getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE|WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
Part3:创建悬浮窗口(仿IPhone的小圆点或者魅族的小白点或者360手机卫士的小浮标),退出当前Activity依旧可见的一种实现方法
/** * Author : yanbo * Time : 14:47 * Description : 手机屏幕悬浮窗,仿IPhone小圆点 * (未完全实现,只提供思路,如需请自行实现) * Notice : <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> */ public class WindowService extends Service { private WindowManager mWindowManager; private ImageView mImageView; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); //创建悬浮窗 createFloatWindow(); } private void createFloatWindow() { //这里的参数设置上面刚刚讲过,不再说明 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE); //设置window的type layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; //设置效果为背景透明 layoutParams.format = PixelFormat.RGBA_8888; //设置浮动窗口不可聚焦 layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; layoutParams.gravity = Gravity.BOTTOM | Gravity.RIGHT; layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.x = -50; layoutParams.y = -50; mImageView = new ImageView(this); mImageView.setImageResource(android.R.drawable.ic_menu_add); //添加到Window mWindowManager.addView(mImageView, layoutParams); //设置监听 mImageView.setOnTouchListener(touchListener); } @Override public void onDestroy() { super.onDestroy(); if (mImageView != null) { //讲WindowManager时说过,add,remove成对出现,所以需要remove mWindowManager.removeView(mImageView); } } private View.OnTouchListener touchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //模拟触摸触发的事件 Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); return false; } }; }
此时在就算该进程退到后台,那么在手机屏幕的右下角也会有个图标,如果点击该图标,就会启动隐式的activity呢。
单例的toast
上篇文章说过了有时候我们会发现Toast弹出过多就会延迟显示,因为上面源码分析可以看见Toast.makeText是一个静态工厂方法,每次调用这个方法都会产生一个新的Toast对象,当我们在这个新new的对象上调用show方法就会使这个对象加入到NotificationManagerService管理的mToastQueue消息显示队列里排队等候显示;所以如果我们不每次都产生一个新的Toast对象(使用单例来处理)就不需要排队,也就能及时更新了
private int count = 0; @OnClick(R.id.but) void onClick() { String content = "Toast ID:" + count; Toast.makeText(this, content, Toast.LENGTH_SHORT).show(); count++; }
连续点击多次10次,那么10个toast依次出现,只有等上一个toast消失了,才显示下一个toast。同时此时可能发生内存泄漏,如果此时点击10次之后,立马退出app,那么这个activity便不能被GC回收,一直需要等到10个toast全部显示完,为了防止内存泄漏,此时可以把this,换成getApplicationContext()
解决方案:
单例的SingleToast,内部持有Toast对象引用
为了防止内存泄漏,不管makeText方法传进来的是context,还是ApplicationContext,都把它转换为应用程序上下文context.getApplicationContext(),所以可以有效的防止内存泄漏
/** * Created by liaobinbin on 2015/11/21. */ public class SingleToast { private static Toast toast; private static SingleToast singleToast; private SingleToast(Context context, String text, int duration) { toast = Toast.makeText(context.getApplicationContext(), text, duration); } public static SingleToast makeText(Context context, String text, int duration) { if (singleToast == null) { synchronized (SingleToast.class) { if (singleToast == null) { singleToast = new SingleToast(context, text, duration); } } } if (toast != null) { toast.setText(text); } return singleToast; } public void show() { if (toast != null) { toast.show(); } } }
使用SingleToast.makeText(getApplicationContext(), content, Toast.LENGTH_LONG).show(); 因为使用的是同一个单例的toast,所以可以做到toast快速的刷新,而不需要一个个排队显示了
private int count = 0; private Toast toast; @OnClick(R.id.but) void onClick() { String content = "Toast ID:" + count; /*if (toast == null) { toast = Toast.makeText(getApplicationContext(), content, Toast.LENGTH_LONG); }else{ toast.setText(content); } toast.show();*/ SingleToast.makeText(getApplicationContext(), content, Toast.LENGTH_LONG).show(); count++; }
子线程中显示dialog/toast
正常情况下,子线程中显示dialog/toast都会有以下异常java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
本质原因: dialog/toast内部都持有handler引用
private final Handler mHandler = new Handler(); public Handler(Callback callback, boolean async) { ........ mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } ........ }
子线程内部,如果没做任何处理,所以Looper.myLooper()当然是null了,所以报异常
解决方案:
void showDialog() { new Thread("MyThread") { @Override public void run() { Looper.prepare(); //当然线程中调用Looper.myLooper()就不会为null了,此时为当前线程绑定了一个looper对象 dialog = new AlertDialog.Builder(MainActivity.this).setTitle("标题").setMessage("内容").create(); //0.477 dialog.show(); Toast.makeText(MainActivity.this, "haha", Toast.LENGTH_SHORT).show(); Looper.loop(); //别忘了这句,要不然拿不到消息啊,toast/dialog就显示不出来了 //不要在这后面做任何事情,因为执行不到,Looper.loop();内部while循环 } }.start(); }
缺点:
这样的话,MyThread线程的话,一直就停止不了,被一直阻塞在
Looper.loop();了,内部while循环
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories