窗体的使用,在当前窗体显示,在主屏幕上显示窗体,解决获取宽高为0的问题(仿360手机卫士1)

     TextView view=new TextView(this);
        view.setText("自定义吐司");
        view.setTextSize(25);
        view.setTextColor(Color.parseColor("#000000"));
        //窗体的参数设置
        WindowManager wm=(WindowManager) getSystemService(WINDOW_SERVICE);
        //定义参数
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
         params.height = WindowManager.LayoutParams.WRAP_CONTENT;
         params.width = WindowManager.LayoutParams.WRAP_CONTENT;
         //与窗体左上角对齐
         params.gravity=Gravity.TOP | Gravity.LEFT;
         
         //指定窗体距离左边100 上边100个像素点,可以实现跟随手指移动的效果,为控件设置触摸事件,wm.updateViewLayout();
         params.x=100;
         params.y=100;
         params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  //不可有焦点
                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE  //不可以触摸
                 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; //不锁屏
         params.format = PixelFormat.TRANSLUCENT; //半透明
         //能够在手机当前屏幕显示(哪怕当前屏幕的活动不是本应用),需要加权限 
         //<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
         params.type =WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; //设置类型为系统级别的
         
         wm.addView(view, params);//将视图和参数添加到窗体,会一直显示在该窗体所关联的活动的屏幕上
        //wm.removeView(view);//调用这个方法就可以让视图消失

 窗体是显示在创建这个窗体的界面上的,所以如果想显示在桌面上,需要显示桌面的时候创建窗体,那么也就只能在后台创建了

注意:获取到窗体需要显示的布局的时候,直接去获取这个布局的宽高可能会为0,

下面指定这个窗体显示在屏幕中间位置,主活动省略,只是在activity中开启这个服务,然后销毁该活动

/**
 * 创建悬浮窗体
 */
public class FloatWindowService extends Service {  
      
    /** 
     * 用于在线程中创建或移除悬浮窗。 
     */  
    private Handler handler = new Handler();  
  
    /** 
     * 定时器,定时进行检测当前应该创建还是移除悬浮窗。 
     */  
    private Timer timer;

    private WindowManager manager;

    private View view;  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        return null;  
    }  
  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        // 开启定时器,每隔0.5秒刷新一次  
        if (timer == null) {  
            timer = new Timer();  
            timer.scheduleAtFixedRate(new RefreshTask(), 0, 500);  
        }  
        return super.onStartCommand(intent, flags, startId);  
    }  
  
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        // Service被终止的同时也停止定时器继续运行  
        timer.cancel();  
        timer = null;  
    }  
  
    class RefreshTask extends TimerTask {  
  
        @Override  
        public void run() {  
            // 当前界面是桌面,且没有悬浮窗显示,则创建悬浮窗。  
            if (isHome() && view == null) {  
                handler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        createBigWindow();
                    }  
                });  
            }  
            // 当前界面不是桌面,且有悬浮窗显示,则移除悬浮窗。  
            else if (!isHome() && view!=null) {  
                handler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        System.out.println("移除");
                        manager.removeView(view);
                        view=null;
                    }  
                });  
            }  
            
        }  
  
    }  
    
    /**
     * 创建一个大窗口显示在屏幕上,创建系统类型的窗体需要权限:
     * <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
     */
    public void createBigWindow(){
        manager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);  
        int screenWidth = manager.getDefaultDisplay().getWidth();
        int screenHeight = manager.getDefaultDisplay().getHeight();
        view = View.inflate(this, R.layout.item_layout, null);
        view.findViewById(R.id.close);
        view.findViewById(R.id.back);
        
        LayoutParams bigWindowParams = new LayoutParams();  
        //屏幕正中间,   注意:直接view.getWidth()会为0
        int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);//标记为未指明的  
        int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);   
        view.measure(w, h);   
        int height =view.getMeasuredHeight();  
        int width =view.getMeasuredWidth();//这样才能得到这个布局的宽高 
        bigWindowParams.x = screenWidth / 2 - width / 2;  
        bigWindowParams.y = screenHeight / 2 - height / 2;  
        System.out.println("屏幕宽:"+screenWidth+",x="+ bigWindowParams.x+",view宽=" + view.getMeasuredWidth()/ 2 );
        bigWindowParams.type = LayoutParams.TYPE_PHONE;//设置类型 
        bigWindowParams.format = PixelFormat.RGBA_8888;  
        bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;//以左上角为参考
        //当这个窗体不可获取焦点,就不能点击屏幕外的东西
        //bigWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE;  
        //设置窗体的宽高,以左上角为参考
        bigWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        bigWindowParams.height =WindowManager.LayoutParams.WRAP_CONTENT;
        manager.addView(view, bigWindowParams);
        System.out.println("创建了窗体");
    }
  
    /** 
     * 判断当前界面是否是桌面 ,先获取桌面应用的程序包名,然后判断当前显示活动包名是否包含在内
     */  
    private boolean isHome() {  
        ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);  
        List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);  
        return getHomes().contains(rti.get(0).topActivity.getPackageName());  
    }  
  
    /** 
     * 获得属于桌面的应用的应用包名称 
     * @return 返回包含所有桌面应用的包名的字符串列表 
     */  
    private List<String> getHomes() {  
        List<String> names = new ArrayList<String>();  
        PackageManager packageManager = this.getPackageManager();  
        Intent intent = new Intent(Intent.ACTION_MAIN);  
        intent.addCategory(Intent.CATEGORY_HOME);  
        List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,  
                PackageManager.MATCH_DEFAULT_ONLY);  
        for (ResolveInfo ri : resolveInfo) {  
            names.add(ri.activityInfo.packageName);  
            //属于桌面的应用:com.android.launcher(启动器)
           // System.out.println("属于桌面的应用:"+ri.activityInfo.packageName);
        }  
        return names;  
    }
 
} 

效果如下:  带拖动效果的见:窗体的使用,悬浮窗,仿360手机卫士2

为何我们调用view.getWidth和view.getHeight的时候,返回值都为0呢?????
因为在onCreate方法执行完了,我们定义的控件才会被度量(measure),所以我们在onCreate方法里面通过view.getHeight()获取控件的高度或者宽度肯定是0,因为它自己还没有被度量,也就是说他自己都不知道自己有多高,而你这时候去获取它的尺寸,肯定是不行的.
@Override   
  public void onCreate(Bundle savedInstanceState) {   
      super.onCreate(savedInstanceState);   
      setContentView(R.layout.main);   
      final ImageView imageView = (ImageView) findViewById(R.id.imageview);         
      int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);   
      int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);   
      imageView.measure(w, h);   
      int height =imageView.getMeasuredHeight();  
      int width =imageView.getMeasuredWidth();  
      textView.append("\n"+height+","+width);  
      System.out.println("执行完毕.."+System.currentTimeMillis());   
  }  
//------------------------------------------------方法一   
ViewTreeObserver vto = imageView.getViewTreeObserver();   
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {   
    public boolean onPreDraw() {   
        int height = imageView.getMeasuredHeight();   
        int width = imageView.getMeasuredWidth();   
        textView.append("\n"+height+","+width);   
        return true;   
    }   
}); <pre name="code" class="html">//-----------------------------------------------方法二   
ViewTreeObserver vto2 = imageView.getViewTreeObserver();     
vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {   
    @Override     
    public void onGlobalLayout() {   
        imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);     
        textView.append("\n\n"+imageView.getHeight()+","+imageView.getWidth());   
    }   }); //-----------------------------------------------方法三 

总结:那么需要获取控件的宽高该用那个方法呢?  上面我使用的方法一
方法一: 比其他的两个方法多了一次计算,也就是多调用了一次onMeasure()方法,该方法虽然看上去简单,但是如果要目标控件计算耗时比较大的话,不建议使用,如listView等.
方法二,它的回调方法会调用很多次,并且滑动TextView的时候任然会调用,所以不建议使用.
方法三,比较合适.
当然,实际应用的时候需要根据实际情况而定.

补充:

//获取View宽度的正确姿势,当 view layout 处理完成时,会自动发送消息,通知 UI 线程。借此机制,巧妙获取 View 的宽高属性
        viewPager.post(new Runnable() {
            @Override
            public void run() {
                LogUtil.e("宽度=" + viewPager.getWidth());
            }
        });

8.0悬浮窗的适配:

8.0 实现悬浮窗的注意点

 

posted @ 2016-08-20 14:22  ts-android  阅读(349)  评论(0编辑  收藏