开发的TV应用发现在部分电视上可以显示完整,而其他部分电视显示不全,周围都会遮挡了。

原因

       这是因为部分老的电视有一个overscan的概念,什么叫overscan呐?官方解释如下:

During the evolution of TV technology, overscan originally described an area of TV content outside of a safe zone that most TVs could reliably display. Even on some of today’s HDTV flat screens, areas outside that zone may not be visible.

       样例图如下:

       从图上可以看出,整个展示的界面是外面整个矩形框,而可见区域是内部的矩形框,因此如果你布局的内容紧邻边界,就会导致部分电视不能完整显示整个内容。

解决方案

       上面的问题,我们怎么解决呐?这里给出两种解决方案。

设置调整

       虽然老的电视有overscan的概念,内容显示不全,但是电视可以设置他的显示内容距离电视边框的距离,这里主要依靠人为的手动设置,方式如下:

一般都是选择遥控板的设置,进入设置界面,选择显示,边距调整

       经过上述方式的调整,可以设置内容与边框有一定的距离,这样可以让所有的内容都显示出来。

难点:该方式必须要用户手动调整,如果不调整则显示内容被遮挡体验效果比较差

UI调整

       既然让大家都采用手动调整是比较困难的,那有没有从应用层面来解决该问题的方法,有,肯定有,官方的文档也给出了解决方案:

Build a 5% margin into your TV screen designs to account for overscan area the TV may not display correctly. On a 1920 x 1080 screen, this margin should be a minimum of 27 pixels from the top and bottom edges and a minimum of 48 pixels from the right and left edges of the picture.

       上面的意思是,在设计界面的时候,就空出四周的overscan距离,以1920*1080为例上下27px,左右48px,让显示的内容完全处于安全区域,周围都是无关内容区域。

缺点:如果设计的内容需要靠近边界,比如说底部有一个导航条,可以显示与影藏,这种时候就需要贴近边界进行设计。如果再设计的时候空出一部分,那在overscan的电视上展示较好,无overscan的电视展示就比较差,如果不空,这效果相反

自适应?

       既然上面的两种方案都是有缺点的

  • 全部让用户手动设置不显示
  • 空出部分内容,在有的时候会显示效果比较差

       那有没有一种方法来解决上面面临的问题?能够根据电视状况自动缩放内容,在有overscan的电视上,拿到overscan的值,设置显示内容与边界为overscan的值,在没有overscan的电视上保持内容不调整。

方案探索

       首先我们看代码,看看代码中是否有overscan的值,经过一番查找,在Android中有一个Display类,他包含了很多显示信息,其中有一个字段为DisplayInfo,DisplayInfo包含了其他的一些显示信息。其中有四个字段如下:

/** * Describes the characteristics of a particular logical display. * @hide */public final class DisplayInfo implements Parcelable {    ....    /**     * @hide     * Number of overscan pixels on the left side of the display.     */    public int overscanLeft;    /**     * @hide     * Number of overscan pixels on the top side of the display.     */    public int overscanTop;    /**     * @hide     * Number of overscan pixels on the right side of the display.     */    public int overscanRight;    /**     * @hide     * Number of overscan pixels on the bottom side of the display.     */    public int overscanBottom;    public DisplayInfo() {    }   ....}

       这里我们省略了其他的代码,可以看到包含有overscanLeft, overscanTop,overscanRight,overscanBottom,这就是Overscan的值,到这里感觉有希望。既然有这四个值,那我们获取到这四个值再设置会界面应该就能达到我们想要的效果。

读取overscan

       从上面的代码中我们可以看到DisplayInfo是被hide掉的,四个值也是被hide掉的,说明从正常的先拿到Display,再拿到DisplayInfo,之后再获取四个值是不行的了,那我们反射来获取该值,代码如下:

public static Rect getOverScan() {    if (overScan == null) {        overScan = new Rect();        try {            WindowManager manager = (WindowManager) AppProfile.getContext().getSystemService(Context                    .WINDOW_SERVICE);            Display display = manager.getDefaultDisplay();            Class clazz = Display.class;            Field mDisplayInfo = clazz.getDeclaredField("mDisplayInfo");            mDisplayInfo.setAccessible(true);            Object displayInfo = mDisplayInfo.get(display);            Class displayInfoClazz = Class.forName("android.view.DisplayInfo");            Field overscanLeft = displayInfoClazz.getDeclaredField("overscanLeft");            overScan.left = overscanLeft.getInt(displayInfo);            Field overscanTop = displayInfoClazz.getDeclaredField("overscanTop");            overScan.top = overscanTop.getInt(displayInfo);            Field overscanRight = displayInfoClazz.getDeclaredField("overscanRight");            overScan.right = overscanRight.getInt(displayInfo);            Field overscanBottom = displayInfoClazz.getDeclaredField("overscanBottom");            overScan.bottom = overscanBottom.getInt(displayInfo);        } catch (Exception e) {        }    }    return overScan;}

       首先获取到Display,之后在反射获取DisplayInfo,最后在读取四个值。

       也可以直接反射获取Display下的getOverscanInsets函数, getOverscanInsets函数如下:

 /** * @hide * Return a rectangle defining the insets of the overscan region of the display. * Each field of the rectangle is the number of pixels the overscan area extends * into the display on that side. */public void getOverscanInsets(Rect outRect) {    synchronized (this) {        updateDisplayInfoLocked();        outRect.set(mDisplayInfo.overscanLeft, mDisplayInfo.overscanTop,                mDisplayInfo.overscanRight, mDisplayInfo.overscanBottom);    }}

       反射获取方式如下:

public static Rect getOverScan1() {    Rect overScan = new Rect();    try {        WindowManager manager = (WindowManager) AppProfile.getContext().getSystemService(Context.WINDOW_SERVICE);        Display display = manager.getDefaultDisplay();        Class clazz = Display.class;        Method getOverscanInsets = clazz.getMethod("getOverscanInsets", Rect.class);        getOverscanInsets.invoke(display, overScan);        LogUtil.free(overScan.toString());    } catch (Exception e) {    }    return overScan;}

       我们采用上面方法在debug状态下读取了一下DisplayInfo的内容:

Display id 0: DisplayInfo{"内置屏幕", app 1280 x 720, real 1280 x 720, largest app 1280 x 1255, smallest app 720 x 695, 50.0 fps, rotation0, density 160 (160.0 x 160.0) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE,FLAG_SUPPORTS_PROTECTED_BUFFERS}, DisplayMetrics{density=1.0, width=1280, height=720, scaledDensity=1.0,xdpi=160.0, ydpi=160.0}, isValid=true

       反射后获取,发现并没有overscan的值,很遗憾四个值都是0。可是我测试的电视是有overscan的。代码写错了?

       我采用命令来设置overscan的内容:

adb shell wm overscan  10203040

       分别设置overscan的值为10, 20, 30, 40,之后我再一次获取DisplayInfo的值,内容如下:

Display id 0: DisplayInfo{"内置屏幕", app 1280 x 720, real 1280 x 720, overscan (10,20,30,40), largest app 1280 x 1255, smallest app 720 x 695, 50.0 fps, rotation0, density 160 (160.0 x 160.0) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}, DisplayMetrics{density=1.0, width=1280, height=720, scaledDensity=1.0, xdpi=160.0, ydpi=160.0}, isValid=true

       这里可以看到明确有overscan的内容,并且就是我们命令设置的值,这就尴尬了,系统默认值获取不到,手动设置可以获取。希望之路破灭。

总结

       如果有人实现了自适应的方式,麻烦请告知一下。目前只能采取UI界面调整的方式来避免该问题。

更多相关文章

  1. Android开发之Http通信HttpURLConnection接口
  2. android 将图片内容解析成字节数组,将字节数组转换为ImageView可
  3. Android(安卓)中LayoutInflater.inflate()方法的介绍
  4. android 控件在不同状态下的内容样式与背景样式
  5. android resoult A>B 结束B A显示B中的内容
  6. android的TabActivity
  7. Android(安卓)Studio中使用Gradle和AndroidAnnotations
  8. android 获取一个apk的信息
  9. Android(安卓)文件在SDCard上读写操作

随机推荐

  1. android监听当前应用
  2. Android studio 常见错误以及问题
  3. android 自定义相册 多选
  4. Android版本及API等级关系
  5. java.lang.NullPointerException: Attemp
  6. Duplicate files copied (Android Studio
  7. android 操作文件
  8. Ubuntu 环境编译Kernel和Android
  9. Android更新UI的五种方式
  10. android 之simpleAdapter详解