解决华为 setRotationX/setRotationY View 不显示的问题

1,843 阅读4分钟

因为业务需要,我们 APP 的有一个翻卡牌的场景,使用到 ROTATION_XROTATION_Y 两个属性动画,在我们 APP 上线后收到了用户的反馈:在 华为荣耀8P20 等设备上出现,点击卡牌没有翻牌动画且整个 View 都消失不见的情况。
我们立即找来一部 华为荣耀8 进行测试,发现确实存在用户所反馈的问题。

我们反复调试发现,问题出自这两个 API: setRotationX(rotationX)setRotationY(rotationY) ,只要 rotationXrotationY 的值为非 0的数字,View 就会消失不间。
我们在网上搜索了各种关键字,有不少开发者也遇到了同样的问题,提供的解决方案基本确定,就是以 Animation 替代 ObjectAnimator 动画。我们做了多种尝试,感觉也只能这么干了。

就在我们准备改动画的时候,在 Stack Overflow 找到了下面这个提问: stackoverflow.com/questions/4…

这个回答没有被提问者选中为答案,我们差点也错过了。

看一下 setCameraDistance(distance) 的文档。 developer.android.com/reference/a…

这段话翻译过来是:distance 的值以“深度像素”来表示。 distance 的默认值取决于屏幕密度。例如,在中等密度的屏幕上,distance 的默认值为 1280。在高等密度的屏幕上,distance 的默认值为 1920

意思就是说:distance 的取值是 12801920 其中之一了。
那么 distance 的取值到底是哪一个呢?

文档中给出了 setCameraDistance(distance) 使用方法:如果想要指定一个 distance 的值,并且保持在各种屏幕密度上的效果一致,可以使用以下公式:

float scale = context.getResources().getDisplayMetrics().density;
view.setCameraDistance(distance * scale);

我们先把 distance 的值设为 1280,在 荣耀8 上进行测试:翻牌动画出来了,View 也没有消失。

float scale = context.getResources().getDisplayMetrics().density;
view.setCameraDistance(1280 * scale);

我们再把 distance 的值设为 1920 进行测试,发现跟 1280 的值效果是一样的。

那么这个值到底是 1280 还是 1920 呢?
我们发现网上没有任何与 setCameraDistance(distance)深度像素 有关的资料,怎么办?

我们找来了几部机器,根据上面的公式进行反推。
下面的测试代码会打印出机器的 distance 值:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

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

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                test(v);
            }
        });
    }

    private void test(View v) {
        float cameraDistance = v.getCameraDistance();
        float distance = cameraDistance / getResources().getDisplayMetrics().density;
        Log.d(TAG, "default distance:" + distance);
    }
}

华为荣耀8:Android 7.0,distance 的值为 -0.0

一加5:Android 10,distance 的值为 1280

小米4:Android 6.0,distance 的值为 1280

三星Galaxy Note8:Android 9.0,distance 的值为 1280

谷歌Pixel 3:Android 10,distance 的值为 1280

我们做了一个表格:

机型 尺寸 系统版本 分辨 屏幕比例 PPI distance
华为荣耀8 5.2 Android 7.0 1920*1080 16:9 423 -0.0
一加5 5.5 Android 10 1920*1080 16:9 441 1280
小米4 5.0 Android 6.0 1920*1080 16:9 441 1280
三星Galaxy Note 8 6.3 Android 9.0 2960*1440 18.5:9 522 1280
谷歌 Pixel 3 5.5 Android 10 2160*1080 18:9 439 1280

可以看到,荣耀8 上的 distance 值是 -0.0,而其他机器上的值都是 1280

为什么是负的呢?我们看一下系统源码:

可以看出到,setCameraDistance(distance)native 设值的时候做了负数运算的,所以在读值的时候也会做负数运算取正值。
那么 荣耀8 读值为 -0.0,就是说 native 层的值是 0

而且跟据我们上面的测试表格可以看出,distance 的取值跟屏幕尺寸、手机系统、分辨率、屏幕比例、PPI 都没有关系,基本可以确定 distance 的默认值就是 1280 了。

惊喜的是,我们把手上的 荣耀8 机器升级到 Android 8.0 后似乎修复了这个问题,打印出来的结果也是:1280

至于为什么在 P20 Android10 的机器上还会出现这种问题,我们也不知为何,但是根据安装了我们修复包的用户的视频反馈显示,设置 distance 的值为 1280 后的确修复了这个问题,效果也是一致的。

至于 distance 的值在什么场景下设为 1920,我没找到相关资料。如果有朋友知道这个值的使用场景,可以在评论里面回复一下。