精华内容
下载资源
问答
  • 获取屏幕颜色

    2015-10-10 16:01:22
    实时获取屏幕颜色,有源代码。 鼠标单击可保存一次颜色RGB值,并能显示鼠标在屏幕上的坐标值。 可在任务栏上点右键关闭窗口。
  • 实现原理:创建一个背景透明的Activity, 点击屏幕获取坐标信息并显示。在悬浮窗中调用该Activity,可以获取所有界面的坐标信息。 package sc.tool.screen; import sc.tool.component.ActivityComponent; ...

    工具下载:


    实现原理:创建一个背景透明的Activity, 点击屏幕时获取坐标信息并显示。在悬浮窗中调用该Activity,可以获取所有界面的坐标信息。

    
    package sc.tool.screen;
    
    import sc.tool.component.ActivityComponent;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Looper;
    import android.view.MotionEvent;
    import android.view.View;
    
    
    /** 获取屏幕点击处的坐标 */
    public class GetClickPostion extends ActivityComponent
    {
    	/** 显示当前Activity */
    	public static void Show(Context context)
    	{
    //		context.finish();	// 关闭context对应的Activity
    		
    //		final Class cls = GetClickPostion.class;
    //		new Handler(Looper.getMainLooper()).post(new Runnable()
    //		{
    //			@Override
    //			public void run()
    //			{
    //				
    //			}
    //		});
    		
    		Intent intent = new Intent(context, GetClickPostion.class);
    		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    		context.startActivity(intent);
    		
    		
    		// context.startActivityForResult(intent, CODE_ClickPostion);
    	}
    	
    	// ----------------
    	
    	@Override
    	public void Init(Bundle savedInstanceState)
    	{
    		this.setContentView("screen_postion");
    		Click("buttonReset");
    	}
    	
    	@Override
    	public void Click(String viewId)
    	{
    		if (viewId.equals("buttonReset"))
    		{
    			this.LinearLayout("linear1").setVisibility(View.INVISIBLE);
    			this.LinearLayout("linear2").setVisibility(View.INVISIBLE);
    			isSet = false;
    		}
    		else if (viewId.equals("buttonFinish"))
    		{
    			Intent data = new Intent();
    			data.putExtra("x", x + "");
    			data.putExtra("y", y + "");
    			this.setResult(CODE_ClickPostion, data);	// 返回选取的坐标
    			this.finish();
    		}
    	}
    	
    	public boolean dispatchTouchEvent(MotionEvent event)
    	{
    		switch (event.getAction())
    		{
    			case MotionEvent.ACTION_DOWN:
    				setPosition((int) event.getX(), (int) event.getY());
    				break;
    			
    			case MotionEvent.ACTION_UP:
    				break;
    		}
    		return super.dispatchTouchEvent(event);
    		
    	}
    	
    	boolean isSet = false;
    	int x;
    	int y;
    	public static final int CODE_ClickPostion = 50001;
    	
    	public void setPosition(int x, int y)
    	{
    		if (!isSet)
    		{
    			isSet = true;
    			LinearLayout("linear1").setVisibility(View.VISIBLE);
    			LinearLayout("linear2").setVisibility(View.VISIBLE);
    			this.EditText("editText1").setText("" + x + ", " + y);
    		}
    	}
    	
    }
    

    AndroidManifest.xml添加配置:

    <activity
                android:name="sc.tool.screen.GetClickPostion"
                android:configChanges="orientation|keyboardHidden|screenSize"
                android:theme="@style/AppTheme2" />

    styles.xml配置:

    <resources>
    
        <style name="AppTheme2" parent="android:Theme.Translucent.NoTitleBar.Fullscreen">
            <item name="android:windowFullscreen">false</item>
        </style>
        
    </resources>

    完整源码下载

    展开全文
  • Android Screen Brightness 屏幕亮度 的获取和修改 一、获取系统的屏幕亮度值 二、修改App屏幕亮度(仅当前应用) 三、修改系统屏幕亮度(所有应用) 一、获取系统的屏幕亮度值 系统屏幕亮度值在(0~255)之间...

    Android Screen Brightness 屏幕亮度 的获取和修改

    一、获取系统的屏幕亮度值
    二、修改App屏幕亮度(仅当前应用)
    三、修改系统屏幕亮度(所有应用)

    一、获取系统的屏幕亮度值

    系统屏幕亮度值在(0~255)之间,获取方法很简单,只需要调用以下方法即可。

    /**
         * 1.获取系统默认屏幕亮度值 屏幕亮度值范围(0-255)
         * **/
        private int getScreenBrightness(Context context) {
            ContentResolver contentResolver = context.getContentResolver();
            int defVal = 125;
            return Settings.System.getInt(contentResolver,
                    Settings.System.SCREEN_BRIGHTNESS, defVal);
        }

    修改屏幕亮度包含两种:
    1.修改App屏幕亮度(仅当前应用)
    2.修改系统屏幕亮度(所有应用)

    二、修改App屏幕亮度(仅当前应用)

    修改自身 APP 亮度很简单,只需要在 Activity OnCreate方法调用如下代码即可。

        /**
         * 2.设置 APP界面屏幕亮度值方法
         * **/
        private void setAppScreenBrightness(int birghtessValue) {
            Window window = getWindow();
            WindowManager.LayoutParams lp = window.getAttributes();
            lp.screenBrightness = birghtessValue / 255.0f;
            window.setAttributes(lp);
        }

    三、修改系统屏幕亮度(所有应用)

    修改系统 Settings 中的屏幕亮度,由于会影响到所有APP,需要申请修改Settings的权限

    <uses-permission * android:name="android.permission.WRITE_SETTINGS"/>

    同时,需要取消光感自动调节屏幕亮度的功能,设置为手动调节模式,否则光感传感器会随着光照强度的变化修改系统屏幕亮度,并且非系统签名的APP,需要用户手动授权后才可以修改背光亮度。

    关闭光感,设置手动调节背光模式实现方法如下:

        /**
         * 3.关闭光感,设置手动调节背光模式
         * 
         * SCREEN_BRIGHTNESS_MODE_AUTOMATIC 自动调节屏幕亮度模式值为1
         * 
         * SCREEN_BRIGHTNESS_MODE_MANUAL 手动调节屏幕亮度模式值为0
         * **/
        public void setScreenManualMode(Context context) {
            ContentResolver contentResolver = context.getContentResolver();
            try {
                int mode = Settings.System.getInt(contentResolver,
                        Settings.System.SCREEN_BRIGHTNESS_MODE);
                if (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
                    Settings.System.putInt(contentResolver,
                            Settings.System.SCREEN_BRIGHTNESS_MODE,
                            Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
                }
            } catch (Settings.SettingNotFoundException e) {
                e.printStackTrace();
            }
        }

    非系统签名应用,引导用户手动授权修改Settings 权限

    非系统签名应用,无法直接修改Settings,需要引导用户手动授权。
    引导用户手动授权

    
        /**
         * 4.非系统签名应用,引导用户手动授权修改Settings 权限
         * **/
        private static final int REQUEST_CODE_WRITE_SETTINGS = 1000;
    
        private void allowModifySettings() {
            // Settings.System.canWrite(MainActivity.this)
            // 检测是否拥有写入系统 Settings 的权限
            if (!Settings.System.canWrite(MainActivity.this)) {
                AlertDialog.Builder builder = new AlertDialog.Builder(this,
                        android.R.style.Theme_Material_Light_Dialog_Alert);
                builder.setTitle("请开启修改屏幕亮度权限");
                builder.setMessage("请点击允许开启");
                // 拒绝, 无法修改
                builder.setNegativeButton("拒绝",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Toast.makeText(MainActivity.this,
                                        "您已拒绝修系统Setting的屏幕亮度权限", Toast.LENGTH_SHORT)
                                        .show();
                            }
                        });
                builder.setPositiveButton("去开启",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // 打开允许修改Setting 权限的界面
                                Intent intent = new Intent(
                                        Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri
                                                .parse("package:"
                                                        + getPackageName()));
                                startActivityForResult(intent,
                                        REQUEST_CODE_WRITE_SETTINGS);
                            }
                        });
                builder.setCancelable(false);
                builder.show();
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            // TODO Auto-generated method stub
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    // Settings.System.canWrite方法检测授权结果
                    if (Settings.System.canWrite(getApplicationContext())) {
                        // 5.调用修改Settings屏幕亮度的方法 屏幕亮度值 200
                        ModifySettingsScreenBrightness(MainActivity.this, 200);
                        Toast.makeText(this, "系统屏幕亮度值" + getScreenBrightness(this),
                                Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(MainActivity.this, "您已拒绝修系统Setting的屏幕亮度权限",
                                Toast.LENGTH_SHORT).show();
                    }
                }
            }
        }
    

    修改Setting 中屏幕亮度值 实现

    拥有系统签名的应用可以直接调用此方法修改系统屏幕亮度,非系统签名应用,只有用户授权后才可以修改。

    /**
         * 5.修改Setting 中屏幕亮度值
         * 
         * 修改Setting的值需要动态申请权限 <uses-permission
         * android:name="android.permission.WRITE_SETTINGS"/>
         * **/
        private void ModifySettingsScreenBrightness(Context context,
                int birghtessValue) {
            // 首先需要设置为手动调节屏幕亮度模式
            setScreenManualMode(context);
    
            ContentResolver contentResolver = context.getContentResolver();
            Settings.System.putInt(contentResolver,
                    Settings.System.SCREEN_BRIGHTNESS, birghtessValue);
        }

     

    整体实现如下:

    package com.example.test;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.ContentResolver;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Bundle;
    import android.provider.Settings;
    import android.view.Window;
    import android.view.WindowManager;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //获取屏幕亮度
            getScreenBrightness(this);
            Toast.makeText(this, "系统屏幕亮度值" + getScreenBrightness(this),
                    Toast.LENGTH_SHORT).show();
            // 设置APP 屏幕亮度后,系统Setting亮度将对此app 不生效
            setAppScreenBrightness(100);
            allowModifySettings();
            setContentView(R.layout.activity_main);
    
        }
    
        /**
         * 1.获取系统默认屏幕亮度值 屏幕亮度值范围(0-255)
         * **/
        private int getScreenBrightness(Context context) {
            ContentResolver contentResolver = context.getContentResolver();
            int defVal = 125;
            return Settings.System.getInt(contentResolver,
                    Settings.System.SCREEN_BRIGHTNESS, defVal);
        }
    
        /**
         * 2.设置 APP界面屏幕亮度值方法
         * **/
        private void setAppScreenBrightness(int birghtessValue) {
            Window window = getWindow();
            WindowManager.LayoutParams lp = window.getAttributes();
            lp.screenBrightness = birghtessValue / 255.0f;
            window.setAttributes(lp);
        }
    
        /**
         * 3.关闭光感,设置手动调节背光模式
         * 
         * SCREEN_BRIGHTNESS_MODE_AUTOMATIC 自动调节屏幕亮度模式值为1
         * 
         * SCREEN_BRIGHTNESS_MODE_MANUAL 手动调节屏幕亮度模式值为0
         * **/
        public void setScreenManualMode(Context context) {
            ContentResolver contentResolver = context.getContentResolver();
            try {
                int mode = Settings.System.getInt(contentResolver,
                        Settings.System.SCREEN_BRIGHTNESS_MODE);
                if (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
                    Settings.System.putInt(contentResolver,
                            Settings.System.SCREEN_BRIGHTNESS_MODE,
                            Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
                }
            } catch (Settings.SettingNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 4.非系统签名应用,引导用户手动授权修改Settings 权限
         * **/
        private static final int REQUEST_CODE_WRITE_SETTINGS = 1000;
    
        private void allowModifySettings() {
            // Settings.System.canWrite(MainActivity.this)
            // 检测是否拥有写入系统 Settings 的权限
            if (!Settings.System.canWrite(MainActivity.this)) {
                AlertDialog.Builder builder = new AlertDialog.Builder(this,
                        android.R.style.Theme_Material_Light_Dialog_Alert);
                builder.setTitle("请开启修改屏幕亮度权限");
                builder.setMessage("请点击允许开启");
                // 拒绝, 无法修改
                builder.setNegativeButton("拒绝",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Toast.makeText(MainActivity.this,
                                        "您已拒绝修系统Setting的屏幕亮度权限", Toast.LENGTH_SHORT)
                                        .show();
                            }
                        });
                builder.setPositiveButton("去开启",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // 打开允许修改Setting 权限的界面
                                Intent intent = new Intent(
                                        Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri
                                                .parse("package:"
                                                        + getPackageName()));
                                startActivityForResult(intent,
                                        REQUEST_CODE_WRITE_SETTINGS);
                            }
                        });
                builder.setCancelable(false);
                builder.show();
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            // TODO Auto-generated method stub
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    // Settings.System.canWrite方法检测授权结果
                    if (Settings.System.canWrite(getApplicationContext())) {
                        // 5.调用修改Settings屏幕亮度的方法 屏幕亮度值 200
                        ModifySettingsScreenBrightness(MainActivity.this, 200);
                        Toast.makeText(this, "系统屏幕亮度值" + getScreenBrightness(this),
                                Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(MainActivity.this, "您已拒绝修系统Setting的屏幕亮度权限",
                                Toast.LENGTH_SHORT).show();
                    }
                }
            }
        }
    
        /**
         * 5.修改Setting 中屏幕亮度值
         * 
         * 修改Setting的值需要动态申请权限 <uses-permission
         * android:name="android.permission.WRITE_SETTINGS"/>
         * **/
        private void ModifySettingsScreenBrightness(Context context,
                int birghtessValue) {
            // 首先需要设置为手动调节屏幕亮度模式
            setScreenManualMode(context);
    
            ContentResolver contentResolver = context.getContentResolver();
            Settings.System.putInt(contentResolver,
                    Settings.System.SCREEN_BRIGHTNESS, birghtessValue);
        }
    }
    展开全文
  • 聊聊获取屏幕高度这件事

    万次阅读 多人点赞 2020-12-22 08:00:48
    说起获取屏幕高度,不知道你是如何理解这个高度范围的?是以应用显示区域高度作为屏幕高度还是手机屏幕的高度。

    Navigation Deer

    问题的起因是我发现 PopupWindow弹出位置不正确时发现的。其实早在两年多前,我就发现我手上的小米MIX2s 获取屏幕高度不正确,后面参考V2EX 的这篇帖子处理了。最近又一次做到类似功能,发现小米、vivo都出现了问题。所以有了今天的内容。

    1.回顾过去

    说起获取屏幕高度,不知道你是如何理解这个高度范围的?是以应用显示区域高度作为屏幕高度还是手机屏幕的高度。

    那么我们先看一下平时使用获取高度的方法:

    public static int getScreenHeight(Context context) {
    	WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    	Display display = wm.getDefaultDisplay();
    	DisplayMetrics dm = new DisplayMetrics();
    	display.getMetrics(dm);
    	return dm.heightPixels;
    }
    
    //或
    public static int getScreenHeight(Context context) {
    	WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    	Point point = new Point();
    	wm.getDefaultDisplay().getSize(point);
    	return point.y;
    }
    
    // 或
    public static int getScreenHeight(Context context) {
    	return context.getResources().getDisplayMetrics().heightPixels;
    }
    // 貌似还有更多的方法
    

    以上三种效果一致,只是写法略有不同。

    当然你或许使用的是这种:

    public static int getScreenHeight(Context context) {
    	WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    	Display display = wm.getDefaultDisplay();
    	DisplayMetrics dm = new DisplayMetrics();
    	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
    		display.getRealMetrics(dm);
    	} else {
    		display.getMetrics(dm);
    	}
    	return dm.heightPixels;
    }
    // 其他几种写法大同小异
    ...
    

    这个方法判断了系统大于等于Android 4.2时,使用getRealMetricsgetRealSize)来获取屏幕高度。那么这里发生了什么,为什么会这样?

    其实在Andoird 4.0时,引入了虚拟导航键,如果你继续使用getMetrics之类的方式,获取的高度是去除了导航栏的高度的。

    当时因为在4.0和4.2之间还没有的getRealMetrics这个方法,所以甚至需要添加下面的适配代码:

    try {
         heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
    } catch (Exception e) {
    }
    

    现在不会还有人适配4.4甚至5.0一下的机子了吧,不会吧不会吧。。。所以历史的包袱可以去掉了。

    在这里插入图片描述

    上面方法名都是getScreenHeight,可是这个高度范围到底和你需要的是否一致。这个需要开发时注意,我的习惯是ScreenHeight指应用显示的高度,不包括导航栏(非全屏下),RealHeight来指包含导航栏和状态栏的高度(getRealMetrics)。

    PS:以前也使用过AndroidUtilCode这个工具库,里面将前者方法名定义为getAppScreenHeight,后者为getScreenHeight。也是很直观的方法。

    下文中我会以自己的习惯,使用ScreenHeightRealHeight来代表两者。

    我印象中华为手机很早就使用了虚拟导航键,如下图(图片来源):

    华为手机
    比较特别的是,当时华为的导航栏还可以显示隐藏,注意图中左下角的箭头。点击可以隐藏,上滑可以显示。即使这样,使用getScreenHeight也可以准确获取高度,隐藏了ScreenHeight就等于RealHeight

    上述的这一切在“全面屏”时代没有到来之前,没有什么问题。

    2.立足当下

    小米MIX的发布开启了全面屏时代(16年底),以前的手机都是16:9的,记得雷布斯在发布会上说过,他们费了很大的力气说服了谷歌去除了16:9的限制(从Android 7.0开始)
    MIX 2发布会
    MIX 2发布会

    全面屏手机是真的香,不过随之也带来适配问题。首当其冲的就是刘海屏,各家有各自的获取刘海区域大小的方法。主要原因还是国内竞争的激烈,各家为了抢占市场,先于谷歌定制了自己的方案。这一点让人想起了万恶的动态权限适配。。。

    其实在刘海屏之下,还隐藏一个导航栏的显示问题,也就是本篇的重点。全面屏追求更多的显示区域,随之带来了手势操作。在手势操作模式下,导航栏是隐藏状态。

    本想着可以和上面提到的华为一样,隐藏获取的就是RealHeight,显示就是减去导航栏高度的ScreenHeight。然而现实并不是这样,下表是我收集的一些全面屏手机各高度的数据。

    机型系统ScreenHeightRealHeightNavigationBarStatusBar是否有刘海
    vivo Z3xFuntouch OS_10(Android 10)2201(2075)228012684
    Xiaomi MIX 2sMIUI 12(Android 10)2030(2030)216013076
    Redmi Note 8ProMIUI 11.0.3(Android 10)2134(2134)234013076
    Redmi K30 5GMIUI 12.0.3(Android 10)2175(2175)240013095
    Honor 10 LiteEMUI 10(Android 10)2259(2139)234012081
    华为畅享 20EMUI 10.1.1(Android 10)1552(1472)16008048
    OPPO Find XColorOS 7.1(Android 10)2340(2208)234013296
    OnePlus 6H2OS 10.0.8(Android 10)2201(2159,2075)2280126(42)80

    ScreenHeight一栏中括号内表示显示导航栏时获取的屏幕高度。

    大致的规律总结如下:

    • 在有刘海的手机上,ScreenHeight不包含状态栏高度。
    • 小米手机在隐藏显示导航栏时,ScreenHeight不变,且不包含导航栏高度。

    其中vivo手机,屏幕高度加状态栏高度大于真实高度(2201 + 84 > 2280)。本以为差值79是刘海高度,但查看vivo文档后发现,vivo刘海固定27dp(81px),也还是对不上。。。

    一加6最奇怪,三种设置模式。使用侧边全屏手势时底部有一个小条,NavigationBar高度变为42。(2159 + 42 = 2075 + 126 = 2201)也就是说这种模式也属于有导航栏的情况。

    在这里插入图片描述

    这时如果你需要获取准确的ScreenHeight,只有通过RealHeight - NavigationBar来实现了。

    所以首先需要判断当前导航栏是否显示,再来决定是否减去NavigationBar高度。

    先看看老牌的判断方法如下:

    public boolean isNavigationBarShow(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            Point realSize = new Point();
            display.getSize(size);
            display.getRealSize(realSize);
            return realSize.y!=size.y;
        } else {
            boolean menu = ViewConfiguration.get(this).hasPermanentMenuKey();
            boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
            if(menu || back) {
                return false;
            }else {
                return true;
            }
        }
    }
    
    

    此方法通过比较ScreenHeightRealHeight是否相等来判断。如果对比上面表中的数据,那只有OPPO Find X可以判断成功。也有一些方法通过ScreenHeightRealHeight差值来计算导航栏高度。显然这些方法已无法再使用。

    所以搜索了一下相关信息,得到了下面的代码:

    	/**
         * 是否隐藏了导航键
         *
         * @param context
         * @return
         */
        public static boolean isNavBarHide(Context context) {
            try {
                String brand = Build.BRAND;
                // 这里做判断主要是不同的厂商注册的表不一样
                if (!StringUtils.isNullData(brand) && (Rom.isVivo() || Rom.isOppo())) {
                    return Settings.Secure.getInt(context.getContentResolver(), getDeviceForceName(), 0) != 0;
                } else if (!StringUtils.isNullData(brand) && Rom.isNokia()) {
                    //甚至 nokia 不同版本注册的表不一样, key 还不一样。。。
                    return Settings.Secure.getInt(context.getContentResolver(), "swipe_up_to_switch_apps_enabled", 0) == 1
                            || Settings.System.getInt(context.getContentResolver(), "navigation_bar_can_hiden", 0) != 0;
                } else
                    return Settings.Global.getInt(context.getContentResolver(), getDeviceForceName(), 0) != 0;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
     
        /**
         * 各个手机厂商注册导航键相关的 key
         *
         * @return
         */
        public static String getDeviceForceName() {
            String brand = Build.BRAND;
            if (StringUtils.isNullData(brand))
                return "navigationbar_is_min";
            if (brand.equalsIgnoreCase("HUAWEI") || "HONOR".equals(brand)) {
                return "navigationbar_is_min";
            } else if (Rom.isMiui()||Rom.check("XIAOMI")) {
                return "force_fsg_nav_bar";
            } else if (Rom.isVivo()) {
                return "navigation_gesture_on";
            } else if (Rom.isOppo()) {
                return "hide_navigationbar_enable";
            } else if (Rom.check("samsung")) {
                return "navigationbar_hide_bar_enabled";
            } else if (brand.equalsIgnoreCase("Nokia")) {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
                    return "navigation_bar_can_hiden";
                } else {
                    return "swipe_up_to_switch_apps_enabled";
                }
            } else {
                return "navigationbar_is_min";
            }
        }
    
    

    可以看到包含了华为、小米、vivo、oppo 、三星甚至诺基亚的判断。这就是适配的现实状况,不要妄想寻找什么通用方法,老老实实一个个判断吧。毕竟幺蛾子就是这些厂家搞出来的,厂家魔改教你做人。

    这种方法在上面的测试机中都亲测准确有效。

    不过这个判断方法不够严谨,比如其他品牌手机使用此方法,那么结果都是false。用这样的结果来计算高度显得不够严谨。

    根据前面提到问题发生的原因是全面屏带来的(7.0及以上)。所以我们可以先判断是否是全面屏手机(屏幕长宽比例超过1.86以上),然后判断是否显示导航栏,对于不确定的机型,我们还是使用原先的ScreenHeight。尽量控制影响范围。

    我整理的代码如下(补充了锤子手机判断):

    /**
     * @author weilu
     **/
    public class ScreenUtils {
    
    	private static final String BRAND = Build.BRAND.toLowerCase();
    
    	public static boolean isXiaomi() {
    		return Build.MANUFACTURER.toLowerCase().equals("xiaomi");
    	}
    
    	public static boolean isVivo() {
    		return BRAND.contains("vivo");
    	}
    
    	public static boolean isOppo() {
    		return BRAND.contains("oppo") || BRAND.contains("realme");
    	}
    
    	public static boolean isHuawei() {
    		return BRAND.contains("huawei") || BRAND.contains("honor");
    	}
    
    	public static boolean isOneplus(){
    		return BRAND.contains("oneplus");
    	}
    	
    	public static boolean isSamsung(){
    		return BRAND.contains("samsung");
    	}
    	
    	public static boolean isSmartisan(){
    		return BRAND.contains("smartisan");
    	}
    
    	public static boolean isNokia() {
    		return BRAND.contains("nokia");
    	}
    	
    	public static boolean isGoogle(){
    		return BRAND.contains("google");
    	}
    
    
    	public static int getRealScreenHeight(Context context) {
    		WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    		Display display = wm.getDefaultDisplay();
    		DisplayMetrics dm = new DisplayMetrics();
    		display.getRealMetrics(dm);
    		return dm.heightPixels;
    	}
    	
    	public static int getRealScreenWidth(Context context) {
    		WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    		Display display = wm.getDefaultDisplay();
    		DisplayMetrics dm = new DisplayMetrics();
    		display.getRealMetrics(dm);
    		return dm.widthPixels;
    	}
    
    	public static int getScreenHeight(Context context) {
    		WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    		Display display = wm.getDefaultDisplay();
    		DisplayMetrics dm = new DisplayMetrics();
    		display.getMetrics(dm);
    		return dm.heightPixels;
    	}
    
    
    	/**
    	 * 判断设备是否显示NavigationBar
    	 *
    	 * @return 其他值 不显示 0显示 -1 未知
    	 */
    	public static int isNavBarHide(Context context) {
    		// 有虚拟键,判断是否显示
    		if (isVivo()) {
    			return vivoNavigationEnabled(context);
    		}
    		if (isOppo()) {
    			return oppoNavigationEnabled(context);
    		}
    		if (isXiaomi()) {
    			return xiaomiNavigationEnabled(context);
    		}
    		if (isHuawei()) {
    			return huaWeiNavigationEnabled(context);
    		}
    		if (isOneplus()) {
    			return oneplusNavigationEnabled(context);
    		}
    		if (isSamsung()) {
    			return samsungNavigationEnabled(context);
    		}
    		if (isSmartisan()) {
    			return smartisanNavigationEnabled(context);
    		}
    		if (isNokia()) {
    			return nokiaNavigationEnabled(context);
    		}
    		if (isGoogle()) {
    			// navigation_mode 三种模式均有导航栏,只是高度不同。
    			return 0;
    		}
    		return 2;
    	}
    
    	/**
    	 * 判断当前系统是使用导航键还是手势导航操作
    	 *
    	 * @param context
    	 * @return 0 表示使用的是虚拟导航键,1 表示使用的是手势导航,默认是0
    	 */
    	public static int vivoNavigationEnabled(Context context) {
    		return Settings.Secure.getInt(context.getContentResolver(), "navigation_gesture_on", 0);
    	}
    
    	public static int oppoNavigationEnabled(Context context) {
    		return Settings.Secure.getInt(context.getContentResolver(), "hide_navigationbar_enable", 0);
    	}
    
    	public static int xiaomiNavigationEnabled(Context context) {
    		return Settings.Global.getInt(context.getContentResolver(), "force_fsg_nav_bar", 0);
    	}
    
    	private static int huaWeiNavigationEnabled(Context context) {
    		return Settings.Global.getInt(context.getContentResolver(), "navigationbar_is_min", 0);
    	}
    
    	/**
    	 * @param context
    	 * @return 0虚拟导航键  2为手势导航
    	 */
    	private static int oneplusNavigationEnabled(Context context) {
    		int result = Settings.Secure.getInt(context.getContentResolver(), "navigation_mode", 0);
    		if (result == 2) {
    			// 两种手势 0有按钮, 1没有按钮
    			if (Settings.System.getInt(context.getContentResolver(), "buttons_show_on_screen_navkeys", 0) != 0) {
    				return 0;
    			}
    		}
    		return result;
    	}
    
    	public static int samsungNavigationEnabled(Context context) {
    		return Settings.Global.getInt(context.getContentResolver(), "navigationbar_hide_bar_enabled", 0);
    	}
    
    	public static int smartisanNavigationEnabled(Context context) {
    		return Settings.Global.getInt(context.getContentResolver(), "navigationbar_trigger_mode", 0);
    	}
    
    	public static int nokiaNavigationEnabled(Context context) {
    		boolean result = Settings.Secure.getInt(context.getContentResolver(), "swipe_up_to_switch_apps_enabled", 0) != 0
    				|| Settings.System.getInt(context.getContentResolver(), "navigation_bar_can_hiden", 0) != 0;
    
    		if (result) {
    			return 1;
    		} else {
    			return 0;
    		}
    	}
    
    
    	public static int getNavigationBarHeight(Context context){
    		int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    		if (resourceId > 0) {
    			return context.getResources().getDimensionPixelSize(resourceId);
    		}
    		return 0;
    	}
    
    	private static boolean isAllScreenDevice(Context context) {
    		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
    			// 7.0放开限制,7.0以下都不为全面屏
    			return false;
    		} else {
    			int realWidth = getRealScreenWidth(context);
    			int realHeight = getRealScreenHeight(context);
    
    			float width;
    			float height;
    			if (realWidth < realHeight) {
    				width = realWidth;
    				height = realHeight;
    			} else {
    				width = realHeight;
    				height = realWidth;
    			}
    			// Android中默认的最大屏幕纵横比为1.86
    			return height / width >= 1.86f;
    		}
    	}
    
    	/**
    	 * 获取去除导航栏高度的剩余高度(含状态栏)
    	 * @param context
    	 * @return
    	 */
    	public static int getScreenContentHeight(Context context) {
    
    		if (isAllScreenDevice(context)) {
    
    			int result = isNavBarHide(context);
    
    			int result = isNavBarHide(context);
    
    			if (result == 0) {
    				return getRealScreenHeight(context) - getNavigationBarHeight(context);
    			} else if (result == -1){
    				// 未知
    				return getScreenHeight(context);
    			} else {
    				return getRealScreenHeight(context);
    			}
    		} else {
    			return getScreenHeight(context);
    		}
    
    	}
    }
    

    有人会问,这些key都是哪里来的?毕竟我在厂商文档也没有翻到。

    我能想到的办法是查看SettingsProvider,它是提供设置数据的Provider,分有GlobalSystemSecure三种类型,上面代码中可以看到不同品牌存放在的类型都不同。我们可以通过adb命令查看所有数据,根据navigation等关键字去寻找。比如查看Secure的数据:

    	adb shell settings list secure
    

    或者:

    	ContentResolver cr = context.getContentResolver();
    	Uri uri = Uri.parse("content://settings/secure/");
    	Cursor cursor = cr.query(uri, null, null, null, null);
    	while (cursor.moveToNext()) {
    		String name = cursor.getString(cursor.getColumnIndex("name"));
    		String value = cursor.getString(cursor.getColumnIndex("value"));
    		Log.d("settings:", name + "=" + value);
    	}
    	cursor.close();		
    

    这样如果有上面兼容不到的机型,可以使用这个方法适配。也欢迎你的补充反馈。

    费了这么大的劲获取到了准确的高度,可能你会说,还不如直接获取ContentView的高度:

    	public static int getContentViewHeight(Activity activity) {
    		View contentView = activity.getWindow().getDecorView().findViewById(android.R.id.content);
    		return contentView.getHeight();
    	}
    

    这个结果和上述计算的高度一致,唯一的限制是需要在onWindowFocusChanged之后调用,否则高度为0。这个我们可以根据实际情况自行选用。


    2021-04-06补充:

    发现在Android 11中可以使用WindowMetrics获取窗口边界距四边的距离大小,我们使用bottom获取导航栏真实高度:

    	@RequiresApi(api = Build.VERSION_CODES.Q)
    	public static int getRealNavHeight(Context context) {
    		WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    		WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
    		WindowInsets windowInsets = windowMetrics.getWindowInsets();
    		Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout());
    		return insets.bottom;
    	}
    

    这样一来可以不需要判断当前导航栏是否显示来获取高度了。不过只适用于Android 11及以上,对于中间的8、9、10看来还需要以上的方法处理。

    3.已知问题

    • 网上有许多同类代码,发现会将vivo和oppo都使用navigation_gesture_on这一个key。我在oppo Find x中发现此key并不存在,不知是否和系统版本有关。如果是的话,又需要判断oppo的系统版本了。

    • 上面提到的获取导航栏高度的方法在部分手机中无效,无效的原因是因为导航栏隐藏时,获取高度就为0。所以判断是否显示导航栏是关键。

    • 刘海的出现,很多人会吐槽丑,所以厂家想到了隐藏刘海的方式(掩耳盗铃),比如下面是Redmi K30的设置页面:

    设置刘海显示页

    第二种没啥特别,就是状态栏强制为黑色。这里我怀疑因为这个设置,导致在有刘海的手机上,ScreenHeight不包含状态栏高度。

    最糟糕的是第三种,隐藏后状态栏在刘海外。例如Redmi K30在开启后,ScreenHeight 为2174,RealHeight为2304,而关闭时为2175 和 2400。这下连万年不变的RealHeight也变化了,这太不real了,大家自行体会。不过目前发现未影响适配方案,不知其他手机如何。

    对于是否隐藏刘海,其实也是有各家的判断的,比如小米:

    	// 0:显示刘海,1:隐藏刘海
    	Settings.Global.getInt(context.getContentResolver(), "force_black", 0);
    
    • 有些App会使用修改density的屏幕适配方案,这会影响获取导航栏高度的方法。比如130px的导航栏适配后获取到的是136px。所以这里需要使用getSystem中的density转换回去:
    	public static int getNavigationBarHeight(Context context){
    		int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    		if (resourceId > 0) {
    			int height = context.getResources().getDimensionPixelSize(resourceId);
    			// 兼容屏幕适配导致density修改
    			float density = context.getResources().getDisplayMetrics().density;
    			if (DENSITY != density) {
    				return dpToPx(px2dp(context, height));
    			}
    			return height;
    		}
    		return 0;
    	}
    
    	public static final float DENSITY = Resources.getSystem().getDisplayMetrics().density;
    
    	public static int dpToPx(int dpValue) {
    		return (int) (dpValue * DENSITY + 0.5f);
    	}
    
    	public static int px2dp(Context context, int px) {
    		return (int) (px / context.getResources().getDisplayMetrics().density + 0.5);
    	}
    

    getSystem源码如下:

    	/**
         * Return a global shared Resources object that provides access to only
         * system resources (no application resources), is not configured for the
         * current screen (can not use dimension units, does not change based on
         * orientation, etc), and is not affected by Runtime Resource Overlay.
         */
        public static Resources getSystem() {
            synchronized (sSync) {
                Resources ret = mSystem;
                if (ret == null) {
                    ret = new Resources();
                    mSystem = ret;
                }
                return ret;
            }
        }
    

    它不受资源覆盖的影响,我们可以通过它将值转换回来。

    4.展望未来

    本篇看似聊的获取高度这件事,其实伴随导航栏的发展演进,核心是是如何判断导航栏是否显示。

    通过上面的介绍,总结一下就是在“全面屏时代”,如果你想获取屏幕高度,就不要使用ScreenHeight了。否则会出现UI展示上的问题。而且这种问题,线上也不会崩溃,难以发现。以前在支付宝中就发现过 PopupWindow弹出高度不正确的问题,过了好久才修复了。

    至于屏幕宽度,也不清楚随着折叠屏、环绕屏的到来会不会造成影响。但愿不要吧,碎片化原来越严重了。。。

    最后,如果本文对你有启发有帮助,点个赞和收藏可好?

    参考

    展开全文
  • 问题描述:每次写到显示二维码的时候,可能会因为用户屏幕亮度过暗导致扫码困难,所以需要打开二维码的时候显示为最高亮度,关闭时恢复原来亮度。 实现逻辑:使用uni官方提供的接口 uni.getScreenBrightness获取屏幕...

    问题描述:每次写到显示二维码的时候,可能会因为用户屏幕亮度过暗导致扫码困难,所以需要打开二维码的时候显示为最高亮度,关闭时恢复原来亮度。

    实现逻辑:使用uni官方提供的接口 uni.getScreenBrightness获取屏幕亮度,再使用uni.setScreenBrightness接口设置屏幕亮度。

    	//获取屏幕亮度
    	getScreenBrightness(){
    		//注意uni.getScreenBrightness为异步接口,所以需要使用Promise封装为异步执行
    		return new Promise(function(resolve, reject){
    			uni.getScreenBrightness({
    			    success: function (res) {
    			        console.log('屏幕亮度值:' + res.value);
    					resolve(res.value)
    			    },
    				fail: function (err) {
    					reject(0.5);//如果获取失败设置亮度为中间值
    				}
    			});
    		})
    	},
    	
    	//修改屏幕亮度
    	updateScreenBrightness(valueNum){
    		uni.setScreenBrightness({
    		    value: valueNum,
    		    success: function () {
    		        console.log('成功修改屏幕亮度为',valueNum);
    		    }
    		});
    	},
    

    调用

    	//打开二维码
    	openCode(){
    		this.getScreenBrightness().then(fucntion(res){
    			console.log('当前亮度为',res);
    			uni.setStorageSync('Brightness_key',res)
    		})catch((err) => {
    			uni.setStorageSync('Brightness_key',err)
    		});
    		this.updateScreenBrightness(1);//设置亮度为最高
    	},
    	
    	//关闭二维码
    	closeCode(){
    		let Brightness=uni.getStorageSync('Brightness_key');
    		this.updateScreenBrightness(Brightness);//把屏幕亮度调回以前亮度
    	}
    
    展开全文
  • 承接上文[Python]从安卓手机获取屏幕截图 首先准备好环境,然后就是adb shell啦 adb视频 adb shell screenrecord /sdcard/123.mp4(保存到SDCard) adb pull /sdcard/123.mp4 /Users/guojie/Desktop/123.mp4(保存到...
  • 调节屏幕亮度-获取屏幕亮度-监听亮度改变工具类功能简介java代码 Android技术生活-QQ交流群:723592501 更多其他页面-自定义View-实用功能合集:点击查看 功能简介 获取系统屏幕亮度,app屏幕亮度,手动调节亮度...
  • 获取屏幕方向

    2011-01-04 23:22:00
    Configuration newConfig = getResources().get... //键盘没关闭屏幕方向为横屏 }else if(newConfig.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES){ //键盘关闭屏幕方向为竖屏 }
  • 使用java进行GUI设计时,为了能够适配不同分辨率的屏幕,需要获取当前屏幕的分辨率: Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension dimension = toolkit.getScreenSize(); System.out.println...
  • 1.获取屏幕宽度,并赋值给view wx.getSystemInfo({ success: function (res) { // console.log(res.windowWidth) 屏幕宽度 that.setData({ windowWidth: res.windowWidth }) } }) 2.最近遇到自定义弹窗滚动...
  • 通过下面的函数获取Bitmap格式的屏幕截图:public Bitmap myShot(Activity activity) { // 获取windows中最顶层的view View view = activity.getWindow().getDecorView(); view.buildDrawingCache(); // 获取状态...
  • android 获取手机屏幕状态(唤醒睡眠)

    千次阅读 2013-07-24 15:06:58
    前面写过一篇手机屏幕状态监听的文章:手机屏幕状态监听 广播只有在触发的时候才会做一次,有些时候要知道当前手机屏幕处于什么状态。... * 判断手机屏幕是否锁定 * @param c * @return */ publi
  • Java代码 方法一: Java代码 收藏代码 在Activity的onCreate()中: PowerManager pm = (PowerManager) ... C、关闭屏幕长亮只需要将对象释放掉 wakeLock.release(); 当开启屏幕长亮后记得在不需要的时候将对象释放。
  • public void onCreate(Bundle savedInstanceState) {  final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_O...
  • 程序前台透明拖动窗体至想点击位置 并单击该位置 程序将自动点击该位置两次, 有源码方便更改次数,并且可以获取点击位置的坐标,做此软件意图为实现自动点击刷访问量程序有待完善
  • 此方法是通过view的方式获取当前activity的屏幕截图,并不是framebuffer的方式,所以有一定的局限性。但是这种方法相对简单,容易理解。 首先通过下面的函数获取Bitmap格式的屏幕截图: 1 public Bitmap myShot...
  • 此方法是通过view的方式获取当前activity的屏幕截图,并不是framebuffer的方式,所以有一定的局限性。但是这种方法相对简单,容易理解。 首先通过下面的函数获取Bitmap格式的屏幕截图: public Bitmap ...
  • 本文主要介绍重写自带的一些方法,拾取屏幕和窗口坐标信息 效果如下: 分析: 定时服务:在固定一段时间后执行相关的函数方法,例如这里表示的是在0秒后执行self.giveHelp这个方法 QTimer.singleShot...
  • Configuration newConfig = getResources().get...屏幕方向为横屏 }else if(newConfig.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES){ //键盘关闭屏幕方向为竖屏 }
  • 瞎聊时刻:  在移动开发中,手机模拟是很重要的。例如:今天我写了一个很牛逼的页面。在手机上模拟出了很好的效果。我想把它分享给我的好友。怎么把手机上的图给弄出来呢...今天我给你们推荐一个获取手机屏幕快照的方
  • 下了一个可以旋转屏幕角度的功能,当时用的是RK3128测试的,杠杠的,代码如下 ... * 获取屏幕的旋转角度 * * @param context * @return */ public static int getScreenRoate(Context context) { try { i...
  • 用微软的SystemParametersInfo方法请求SPI_GETSCREENSAVEACTIVE来获取屏幕保护程序是否开启,我们会发现当第一次程序启动(计算机屏保未开启的情况下)返回的确实是FALSE,也就是说返回的状态没问题,当你将计算机...
  • //orientation=获取横竖屏keyboardHidden=隐藏软键盘screenSize=屏幕高宽的尺寸 android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="portrait" android:label=...
  • 获取控件屏幕坐标 由于 Typora 软件是多进程,并且使用工具 Pywinauto 查看其 control_type 是 Pane,不能像网上介绍的 notepad 的样例那样去使用,因此这里使用工具 PyAutoGUI,根据界面屏幕坐标去操作 Typora 软件...
  • 网上的博客说的都是从本地或者网络上获取图片,没找到有很好能解决我的问题的博客,所以在解决问题以后在这里记录一下解决方法供以后遇到这个问题的朋友参考并总结一下banner用法。 1.修改图片加载器 由于我原来...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 136,239
精华内容 54,495
关键字:

屏幕是否关闭获取