您的位置:首页 > 移动开发 > Android开发

Android 窗口添加机制系列3-代码实例

2015-11-21 19:12 603 查看
有了上两篇做基础,那么来点干货

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循环
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android toast