因为业务需要,我们 APP 的有一个翻卡牌的场景,使用到 ROTATION_X、ROTATION_Y 两个属性动画,在我们 APP 上线后收到了用户的反馈:在 华为荣耀8、P20 等设备上出现,点击卡牌没有翻牌动画且整个 View 都消失不见的情况。
我们立即找来一部 华为荣耀8 进行测试,发现确实存在用户所反馈的问题。
我们反复调试发现,问题出自这两个 API: setRotationX(rotationX)、setRotationY(rotationY) ,只要 rotationX、rotationY 的值为非 0的数字,View 就会消失不间。
我们在网上搜索了各种关键字,有不少开发者也遇到了同样的问题,提供的解决方案基本确定,就是以 Animation 替代 ObjectAnimator 动画。我们做了多种尝试,感觉也只能这么干了。
就在我们准备改动画的时候,在 Stack Overflow 找到了下面这个提问: stackoverflow.com/questions/4…

这个回答没有被提问者选中为答案,我们差点也错过了。
看一下 setCameraDistance(distance) 的文档。
developer.android.com/reference/a…


这段话翻译过来是:distance 的值以“深度像素”来表示。 distance 的默认值取决于屏幕密度。例如,在中等密度的屏幕上,distance 的默认值为 1280。在高等密度的屏幕上,distance 的默认值为 1920。
意思就是说:distance 的取值是 1280 和 1920 其中之一了。
那么 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);
}
}

distance 的值为 -0.0。

distance 的值为 1280。

distance 的值为 1280。

distance 的值为 1280。

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,我没找到相关资料。如果有朋友知道这个值的使用场景,可以在评论里面回复一下。