Android
1.1 背景相关与系统架构分析 1.2 开发环境搭建 1.2.1 使用Eclipse + ADT + SDK开发Android APP 1.2.2 使用Android Studio开发Android APP 1.3 SDK更新不了问题解决 1.4 Genymotion模拟器安装 1.5.1 Git使用教程之本地仓库的基本操作 1.5.2 Git之使用GitHub搭建远程仓库 1.6 .9(九妹)图片怎么玩 1.7 界面原型设计 1.8 工程相关解析(各种文件,资源访问) 1.9 Android程序签名打包 1.11 反编译APK获取代码&资源 2.1 View与ViewGroup的概念 2.2.1 LinearLayout(线性布局) 2.2.2 RelativeLayout(相对布局) 2.2.3 TableLayout(表格布局) 2.2.4 FrameLayout(帧布局) 2.2.5 GridLayout(网格布局) 2.2.6 AbsoluteLayout(绝对布局) 2.3.1 TextView(文本框)详解 2.3.2 EditText(输入框)详解 2.3.3 Button(按钮)与ImageButton(图像按钮) 2.3.4 ImageView(图像视图) 2.3.5.RadioButton(单选按钮)&Checkbox(复选框) 2.3.6 开关按钮ToggleButton和开关Switch 2.3.7 ProgressBar(进度条) 2.3.8 SeekBar(拖动条) 2.3.9 RatingBar(星级评分条) 2.4.1 ScrollView(滚动条) 2.4.2 Date & Time组件(上) 2.4.3 Date & Time组件(下) 2.4.4 Adapter基础讲解 2.4.5 ListView简单实用 2.4.6 BaseAdapter优化 2.4.7ListView的焦点问题 2.4.8 ListView之checkbox错位问题解决 2.4.9 ListView的数据更新问题 2.5.0 构建一个可复用的自定义BaseAdapter 2.5.1 ListView Item多布局的实现 2.5.2 GridView(网格视图)的基本使用 2.5.3 Spinner(列表选项框)的基本使用 2.5.4 AutoCompleteTextView(自动完成文本框)的基本使用 2.5.5 ExpandableListView(可折叠列表)的基本使用 2.5.6 ViewFlipper(翻转视图)的基本使用 2.5.7 Toast(吐司)的基本使用 2.5.8 Notification(状态栏通知)详解 2.5.9 AlertDialog(对话框)详解 2.6.0 其他几种常用对话框基本使用 2.6.1 PopupWindow(悬浮框)的基本使用 2.6.2 菜单(Menu) 2.6.3 ViewPager的简单使用 2.6.4 DrawerLayout(官方侧滑菜单)的简单使用 3.1.1 基于监听的事件处理机制 3.2 基于回调的事件处理机制 3.3 Handler消息传递机制浅析 3.4 TouchListener PK OnTouchEvent + 多点触碰 3.5 监听EditText的内容变化 3.6 响应系统设置的事件(Configuration类) 3.7 AnsyncTask异步任务 3.8 Gestures(手势) 4.1.1 Activity初学乍练 4.1.2 Activity初窥门径 4.1.3 Activity登堂入室 4.2.1 Service初涉 4.2.2 Service进阶 4.2.3 Service精通 4.3.1 BroadcastReceiver牛刀小试 4.3.2 BroadcastReceiver庖丁解牛 4.4.1 ContentProvider初探 4.4.2 ContentProvider再探——Document Provider 4.5.1 Intent的基本使用 4.5.2 Intent之复杂数据的传递 5.1 Fragment基本概述 5.2.1 Fragment实例精讲——底部导航栏的实现(方法1) 5.2.2 Fragment实例精讲——底部导航栏的实现(方法2) 5.2.3 Fragment实例精讲——底部导航栏的实现(方法3) 5.2.4 Fragment实例精讲——底部导航栏+ViewPager滑动切换页面 5.2.5 Fragment实例精讲——新闻(购物)类App列表Fragment的简单实现 6.1 数据存储与访问之——文件存储读写 6.2 数据存储与访问之——SharedPreferences保存用户偏好参数 6.3.1 数据存储与访问之——初见SQLite数据库 6.3.2 数据存储与访问之——又见SQLite数据库 7.1.1 Android网络编程要学的东西与Http协议学习 7.1.2 Android Http请求头与响应头的学习 7.1.3 Android HTTP请求方式:HttpURLConnection 7.1.4 Android HTTP请求方式:HttpClient 7.2.1 Android XML数据解析 7.2.2 Android JSON数据解析 7.3.1 Android 文件上传 7.3.2 Android 文件下载(1) 7.3.3 Android 文件下载(2) 7.5.1 WebView(网页视图)基本用法 7.5.2 WebView和JavaScrip交互基础 7.5.3 Android 4.4后WebView的一些注意事项 7.5.4 WebView文件下载 7.5.5 WebView缓存问题 7.5.6 WebView处理网页返回的错误码信息 7.6.1 Socket学习网络基础准备 7.6.2 基于TCP协议的Socket通信(1) 7.6.3 基于TCP协议的Socket通信(2) 7.6.4 基于UDP协议的Socket通信 8.1.1 Android中的13种Drawable小结 Part 1 8.1.2 Android中的13种Drawable小结 Part 2 8.1.3 Android中的13种Drawable小结 Part 3 8.2.1 Bitmap(位图)全解析 Part 1 8.2.2 Bitmap引起的OOM问题 8.3.1 三个绘图工具类详解 8.3.2 绘图类实战示例 8.3.3 Paint API之—— MaskFilter(面具) 8.3.4 Paint API之—— Xfermode与PorterDuff详解(一) 8.3.5 Paint API之—— Xfermode与PorterDuff详解(二) 8.3.6 Paint API之—— Xfermode与PorterDuff详解(三) 8.3.7 Paint API之—— Xfermode与PorterDuff详解(四) 8.3.8 Paint API之—— Xfermode与PorterDuff详解(五) 8.3.9 Paint API之—— ColorFilter(颜色过滤器)(1/3) 8.3.10 Paint API之—— ColorFilter(颜色过滤器)(2-3) 8.3.11 Paint API之—— ColorFilter(颜色过滤器)(3-3) 8.3.12 Paint API之—— PathEffect(路径效果) 8.3.13 Paint API之—— Shader(图像渲染) 8.3.14 Paint几个枚举/常量值以及ShadowLayer阴影效果 8.3.15 Paint API之——Typeface(字型) 8.3.16 Canvas API详解(Part 1) 8.3.17 Canvas API详解(Part 2)剪切方法合集 8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash 8.4.1 Android动画合集之帧动画 8.4.2 Android动画合集之补间动画 8.4.3 Android动画合集之属性动画-初见 8.4.4 Android动画合集之属性动画-又见 9.1 使用SoundPool播放音效(Duang~) 9.2 MediaPlayer播放音频与视频 10.1 TelephonyManager(电话管理器) 10.2 SmsManager(短信管理器) 10.3 AudioManager(音频管理器) 10.4 Vibrator(振动器) 10.5 AlarmManager(闹钟服务) 10.6 PowerManager(电源服务) 10.7 WindowManager(窗口管理服务) 10.8 LayoutInflater(布局服务) 10.9 WallpaperManager(壁纸管理器) 10.10 传感器专题(1)——相关介绍 10.11 传感器专题(2)——方向传感器 10.12 传感器专题(3)——加速度/陀螺仪传感器

10.7 WindowManager(窗口管理服务)

本节引言:

本节给大家带来的Android给我们提供的系统服务中的——WindowManager(窗口管理服务), 它是显示View的最底层,Toast,Activity,Dialog的底层都用到了这个WindowManager, 他是全局的!该类的核心无非:调用addView,removeView,updateViewLayout这几个方法 来显示View以及通过WindowManager.LayoutParams这个API来设置相关的属性!

本节我们就来探讨下这个WindowManager在实际开发中的一些应用实例吧~

官方API文档:WindowManager


1.WindowManager的一些概念:

1)WindowManager介绍

Android为我们提供的用于与窗口管理器进行交互的一个API!我们都知道App的界面都是 由一个个的Acitivty组成,而Activity又由View组成,当我们想显示一个界面的时候, 第一时间想起的是:Activity,对吧?又或者是Dialog和Toast。

但是有些情况下,前面这三者可能满足不了我们的需求,比如我们仅仅是一个简单的显示 用Activity显得有点多余了,而Dialog又需要Context对象,Toast又不可以点击... 对于以上的情况我们可以利用WindowManager这个东东添加View到屏幕上, 或者从屏幕上移除View!他就是管理Android窗口机制的一个接口,显示View的最底层!


2)如何获得WindowManager实例

获得WindowManager对象:

WindowManager wManager = getApplicationContext().getSystemService(Context. WINDOW_ SERVICE);

获得WindowManager.LayoutParams对象,为后续操作做准备

WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();

2.WindowManager使用实例:

实例1:获取屏幕宽高

在Android 4.2前我们可以用下述方法获得屏幕宽高:

public static int[] getScreenHW(Context context) {
    WindowManager manager = (WindowManager)context
    .getSystemService(Context.WINDOW_SERVICE);
    Display display = manager.getDefaultDisplay();
    int width = display.getWidth();
    int height = display.getHeight();
    int[] HW = new int[] { width, height };
    return HW;
}

而上述的方法在Android 4.2以后就过时了,我们可以用另一种方法获得屏幕宽高:

public static int[] getScreenHW2(Context context) {
    WindowManager manager = (WindowManager) context.
    getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics dm = new DisplayMetrics();
    manager.getDefaultDisplay().getMetrics(dm);
    int width = dm.widthPixels;
    int height = dm.heightPixels;
    int[] HW = new int[] { width, height };
    return HW;
}

然后我们可以再另外写两个获取宽以及高的方法,这里以第二种获得屏幕宽高为例:

public static int getScreenW(Context context) {
    return getScreenHW2(context)[0];
}

public static int getScreenH(Context context) {
    return getScreenHW2(context)[1];
}

当然,假如你不另外写一个工具类的话,你可以直接直接获取,比如:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        WindowManager wManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wManager.getDefaultDisplay().getMetrics(dm);
        Toast.makeText(MainActivity.this, "当前手机的屏幕宽高:" + dm.widthPixels + "*" +
                dm.heightPixels, Toast.LENGTH_SHORT).show();
    }
}

运行结果


实例2:设置窗口全屏显示

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        getSupportActionBar().hide();

运行结果


实例3:保持屏幕常亮

public void setKeepScreenOn(Activity activity,boolean keepScreenOn)  
{  
    if(keepScreenOn)  
    {  
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
    }else{  
        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
    }  
} 

实例4:简单悬浮框的实现

运行效果图

实现代码

首先我们需要一个后台的Service在后台等待我们的操作,比如完成悬浮框的绘制移除等, 于是乎我们定义一个Service:MyService.java: 我们需要一个创建悬浮框View的一个方法:

private void createWindowView() {
    btnView = new Button(getApplicationContext());
    btnView.setBackgroundResource(R.mipmap.ic_launcher);
    windowManager = (WindowManager) getApplicationContext()
            .getSystemService(Context.WINDOW_SERVICE);
    params = new WindowManager.LayoutParams();

    // 设置Window Type
    params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
    // 设置悬浮框不可触摸
    params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    // 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应
    params.format = PixelFormat.RGBA_8888;
    // 设置悬浮框的宽高
    params.width = 200;
    params.height = 200;
    params.gravity = Gravity.LEFT;
    params.x = 200;
    params.y = 000;
    // 设置悬浮框的Touch监听
    btnView.setOnTouchListener(new View.OnTouchListener() {
        //保存悬浮框最后位置的变量
        int lastX, lastY;
        int paramX, paramY;

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    lastX = (int) event.getRawX();
                    lastY = (int) event.getRawY();
                    paramX = params.x;
                    paramY = params.y;
                    break;
                case MotionEvent.ACTION_MOVE:
                    int dx = (int) event.getRawX() - lastX;
                    int dy = (int) event.getRawY() - lastY;
                    params.x = paramX + dx;
                    params.y = paramY + dy;
                    // 更新悬浮窗位置
                    windowManager.updateViewLayout(btnView, params);
                    break;
            }
            return true;
        }
    });
    windowManager.addView(btnView, params);
    isAdded = true;
}

然后我们只需在OnCreate( )方法中调用上述的createWindowView( )方法即可启动加载悬浮框, 但是我们发现了一点:这玩意貌似关不掉啊,卧槽,好吧,接下来我们就要分析下需求了!

当处于手机的普通界面,即桌面的时候,这玩意才显示,而当我们启动其他App时,这个悬浮框应该 消失不见,当我们推出app又回到桌面,这个悬浮框又要重新出现!

那么我们首先需要判断App是否位于桌面,于是乎我们再加上下述代码:

/**  
 * 判断当前界面是否是桌面  
 */    
public boolean isHome(){    
    if(mActivityManager == null) {  
        mActivityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);    
    }  
    List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);    
    return homeList.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);    
    }  
    return names;    
}  

好了,接下来我们需要每隔一段时间来进行一系列的判断,比如:是否在桌面,是否已加载悬浮框, 否则加载;否则,如果加载了,就将这个悬浮框移除!这里我们使用handler~,因为不能在子线程直接 更新UI,所以,你懂的,所以我们自己写一个handler来完成上述的操作:

//定义一个更新界面的Handler  
private Handler mHandler = new Handler() {  
    @Override  
    public void handleMessage(Message msg) {  
        switch(msg.what) {  
        case HANDLE_CHECK_ACTIVITY:  
            if(isHome()) {  
                if(!isAdded) {  
                    windowManager.addView(btnView, params);  
                    isAdded = true;  
                new Thread(new Runnable() {  
                    public void run() {  
                        for(int i=0;i<10;i++){  
                            try {  
                                Thread.sleep(1000);  
                            } catch (InterruptedException e) {e.printStackTrace();}  
                            Message m = new Message();  
                            m.what=2;  
                            mHandler.sendMessage(m);  
                        }  
                    }  
                }).start();}  
            } else {  
                if(isAdded) {  
                    windowManager.removeView(btnView);  
                    isAdded = false;  
                }  
            }  
            mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 0);  
            break;  
        }  
    }  
}; 

最后要做的一件事,就是重写Service的onStartCommand( )方法了,就是做判断,取出Intent中的 数据,判断是需要添加悬浮框,还是要移除悬浮框!

@Override  
public int onStartCommand(Intent intent, int flags, int startId) {  
    int operation = intent.getIntExtra(OPERATION, OPERATION_SHOW);  
    switch(operation) {  
    case OPERATION_SHOW:  
        mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);  
        mHandler.sendEmptyMessage(HANDLE_CHECK_ACTIVITY);  
        break;  
    case OPERATION_HIDE:  
        mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);  
        break;  
    }  
    return super.onStartCommand(intent, flags, startId);  
} 

好的,至此,主要的工作就完成了,接下来就是一些零碎的东西了,用一个Activity 来启动这个Service:MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_on;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
    }

    private void bindViews() {
        btn_on = (Button) findViewById(R.id.btn_on);
        btn_on.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_on:
                Intent mIntent = new Intent(MainActivity.this, MainService.class);
                mIntent.putExtra(MainService.OPERATION, MainService.OPERATION_SHOW);
                startService(mIntent);
                Toast.makeText(MainActivity.this, "悬浮框已开启~", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

接着AndroidManifest.xml加上权限,以及为MainService进行注册:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.GET_TASKS" />

<service android:name=".MainService"/>

好了,逻辑还是比较容易理解的~大家自己再看看吧~


3.文献扩展:

从第四个实例中,你可能留意到了:WindowManager.LayoutParams这个东东,这是一个标记, 比如全屏~时间关系就不一一列举出来了,可以到官网或者下述链接中查看:

官方文档:WindowManager.LayoutParams

Android系统服务-WindowManager

另外,假如你对上述的悬浮框有兴趣,想更深入的研究,可见郭大叔(郭霖)的博客:

Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现


本节小结:

本节我们对Android系统服务中的WindowManager进行了学习,前面三个实例可能 实际开发中会用得多一点,建议将第一个示例写成一个工具类,毕竟屏幕宽高用得 蛮多的~至于悬浮框那个能看懂就看懂,没看懂耶没什么~实际开发很少叫你弄个 悬浮框吧...嗯,好的,本节就到这里,谢谢~

© 2021 jiaocheng.bubufx.com  联系我们
ICP备案:鲁ICP备09046678号-3