窗体的使用,悬浮窗,仿360手机卫士2

功能:在桌面上显示一个布局,可以随着手势拖动

主活动:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View layout = findViewById(R.id.btn);
        //绕着空间系的Z抽旋转
        ObjectAnimator rotationZ = ObjectAnimator.ofFloat(layout,"rotation", 0,360);
        //绕着空间系的X抽旋转
        ObjectAnimator rotationX = ObjectAnimator.ofFloat(layout,"rotationX", 0,360);
        //绕着空间系的Y抽旋转
        ObjectAnimator rotationY = ObjectAnimator.ofFloat(layout,"rotationY", 0,360);
        rotationZ.setDuration(3000);
        rotationX.setDuration(3000);
        rotationY.setDuration(3000);
        rotationY.start();
        rotationX.start();
        rotationZ.start();
    }    
    
    /**
     * 单击事件
     */
    public void btnOnClick(View view){
        startService(new Intent(this,FloatWindowService.class));
        finish();
    }
} 

服务类,用于控制悬浮窗的创建于销毁,需要注意状态栏的高度

 

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

    private WindowManager manager;

    private View view;

    private LayoutParams windowParams;//窗体显示的位置的参数

    private int screenWidth;

    private int screenHeight;
  
    @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() {  
                        createWindow();
                    }  
                });  
            }  
            // 当前界面不是桌面,且有悬浮窗显示,则移除悬浮窗。  
            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 createWindow(){
        manager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);  
        screenWidth = manager.getDefaultDisplay().getWidth();
        screenHeight = manager.getDefaultDisplay().getHeight();
        view = View.inflate(this, R.layout.item_layout, null);
        
        windowParams = 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();//这样才能得到这个布局的宽高 
        windowParams.x = screenWidth / 2 - width / 2;  
        windowParams.y = screenHeight / 2 - height / 2;  
        System.out.println("屏幕宽:"+screenWidth+",x="+ windowParams.x+",view宽=" + view.getMeasuredWidth()/ 2 );
        windowParams.type = LayoutParams.TYPE_PHONE;//设置类型 
        windowParams.format = PixelFormat.RGBA_8888;  
        windowParams.gravity = Gravity.LEFT | Gravity.TOP;//以左上角为参考
        //当这个窗体不可获取焦点,才能点击屏幕外的东西,不然焦点都在窗体上,不能点击其他东西
        bigWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE;  
        //设置窗体的宽高,以左上角为参考
        windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        windowParams.height =WindowManager.LayoutParams.WRAP_CONTENT;
        manager.addView(view, windowParams);
        System.out.println("创建了窗体");
        view.setOnTouchListener(this);//为当前布局设置触摸事件,切记要让显示这个布局的窗体不可被触摸,不然总个屏幕都能响应这个触摸了
    }
  
    /** 
     * 判断当前界面是否是桌面 ,先获取桌面应用的程序包名,然后判断当前显示活动包名是否包含在内
     */  
    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;  
    }

    private float firstX;
    private float firstY;
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            firstX = event.getRawX();
            firstY = event.getRawY();
            System.out.println("按下的坐标:"+firstX+","+firstY);
            break;
        case MotionEvent.ACTION_MOVE:
            float moveX = event.getRawX();
            float moveY = event.getRawY();
            windowParams.x = (int) (moveX - firstX) + windowParams.x;
            windowParams.y = (int) (moveY - firstY) + windowParams.y;
            //考虑边界问题
            if(windowParams.x<0){
                windowParams.x = 0;
            }
            if(windowParams.x > screenWidth - v.getWidth()){
                windowParams.x = screenWidth - v.getWidth();
            }
            if(windowParams.y< 0){
                windowParams.y = 0;//起点没有包括状态栏,但是Y抽高度需要减去状态栏的高度
            }
            if(windowParams.y > screenHeight - v.getHeight() - getStatusBarHeight()){
                windowParams.y = screenHeight - v.getHeight()- getStatusBarHeight();
            }
            manager.updateViewLayout(view, windowParams);//更新位置
            firstX = moveX;
            firstY = moveY;
            break;
        case MotionEvent.ACTION_UP:
            System.out.println("抬起");
            break;

        default:
            break;
        }
        return false;
    }
    
     /** 
     * 用于获取状态栏的高度。 
     * @return 返回状态栏高度的像素值。 
     */  
    private int getStatusBarHeight() {  
        int statusBarHeight = 0;//状态栏的高度,注意不是标题栏
        try {  
                Class<?> c = Class.forName("com.android.internal.R$dimen");  
                Object o = c.newInstance();  
                java.lang.reflect.Field field = c.getField("status_bar_height");  
                int x = (Integer) field.get(o);  
                statusBarHeight = getResources().getDimensionPixelSize(x);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        return statusBarHeight;  
    } 
 
} 

 

效果图:

posted @ 2016-09-19 09:46  ts-android  阅读(320)  评论(0编辑  收藏  举报