Android中RotateDrawable搭配Vector Drawable出现旋转异常问题
RotateDrawable嵌套Vector Drawable旋转时的异常问题解决
我之前也碰到过一模一样的问题!当用RotateDrawable包裹VectorDrawable做0°到360°的完整旋转时,确实会在90°(level=2500)和270°(level=7500)这两个临界点出现图形错位/拉伸的异常,而换成PNG这类光栅Drawable就完全正常。
问题复现的测试代码
先把测试相关的代码整理成规范格式:
1. RotateDrawable文件(res/drawable/rotatable_info.xml)
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="360" android:drawable="@drawable/ic_info_black_24dp" />
2. 布局文件
<LinearLayout android:orientation="vertical" android:layout_height="wrap_content" android:layout_width="match_parent"> <ImageView style="@style/style" android:id="@+id/ImageView00" /> <ImageView style="@style/style" android:id="@+id/ImageView01" /> <!-- ... 省略中间的ImageView ... --> <ImageView style="@style/style" android:id="@+id/ImageView19" /> </LinearLayout>
3. 引用的样式(res/values/styles.xml)
<style name="style"> <item name="android:layout_width">32dp</item> <item name="android:layout_height">32dp</item> <item name="android:src">@drawable/rotatable_info</item> </style>
4. 测试代码
int n = -550; ((RotateDrawable) findViewById(R.id.ImageView00).getDrawable()).mutate().setLevel(n+=550); ((RotateDrawable) findViewById(R.id.ImageView01).getDrawable()).mutate().setLevel(n+=550); // ... 省略中间的ImageView设置 ... ((RotateDrawable) findViewById(R.id.ImageView19).getDrawable()).mutate().setLevel(n+=550);
5. 测试用的VectorDrawable(res/drawable/ic_info_black_24dp.xml)
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="#FF000000" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/> </vector>
异常原因分析
这个问题本质是Android系统中RotateDrawable与VectorDrawable的渲染兼容性bug:
- VectorDrawable是基于矢量路径计算渲染的,RotateDrawable在处理90°/270°这类垂直/水平旋转临界点时,内部的变换矩阵计算会出现偏差,导致矢量路径的坐标映射错误,最终表现为图形错位、拉伸。
- 而光栅Drawable(如PNG)是像素级渲染,旋转时直接对像素矩阵做变换,不存在矢量路径的计算逻辑,所以不会出现这个问题。
解决办法
给你三个可行的解决方案,按推荐程度排序:
1. 推荐:用AnimatedVectorDrawable实现旋转
用AnimatedVectorDrawable替代RotateDrawable来控制旋转,绕开系统RotateDrawable的bug。
步骤1:修改VectorDrawable,添加可动画的group
修改ic_info_black_24dp.xml,把路径包裹在一个命名的group里:
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <group android:name="rotationGroup"> <path android:fillColor="#FF000000" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/> </group> </vector>
步骤2:创建旋转动画文件(res/anim/rotate_360.xml)
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:propertyName="rotation" android:valueFrom="0" android:valueTo="360" android:valueType="floatType" android:repeatCount="infinite" android:interpolator="@android:anim/linear_interpolator"/>
步骤3:创建AnimatedVectorDrawable文件(res/drawable/animated_rotate_info.xml)
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_info_black_24dp"> <target android:name="rotationGroup" android:animation="@anim/rotate_360" /> </animated-vector>
步骤4:在代码中使用
把样式里的android:src改成@drawable/animated_rotate_info,然后启动动画:
AnimatedVectorDrawable avd = (AnimatedVectorDrawable) findViewById(R.id.ImageView00).getDrawable(); avd.start();
如果需要手动控制旋转角度(而非自动动画),可以直接用ObjectAnimator操作ImageView的rotation属性:
ObjectAnimator.ofFloat(imageView, "rotation", targetAngle).start();
2. 简单降级:将VectorDrawable转为PNG
如果你的场景不需要矢量图的缩放优势,可以直接在Android Studio中把VectorDrawable导出为PNG:
- 右键点击VectorDrawable文件 →
Convert to PNG - 用RotateDrawable包裹这个PNG,就不会出现旋转异常了。
3. 自定义Drawable:手动处理旋转矩阵
如果必须用RotateDrawable的level控制逻辑,可以自定义一个Drawable,手动处理旋转矩阵:
public class RotateVectorDrawable extends Drawable { private final VectorDrawable mVectorDrawable; private float mRotation = 0f; private final RectF mBounds = new RectF(); public RotateVectorDrawable(VectorDrawable vectorDrawable) { mVectorDrawable = vectorDrawable; } @Override public void draw(@NonNull Canvas canvas) { mBounds.set(getBounds()); canvas.save(); // 以Drawable中心为旋转点 canvas.rotate(mRotation, mBounds.centerX(), mBounds.centerY()); mVectorDrawable.setBounds((int) mBounds.left, (int) mBounds.top, (int) mBounds.right, (int) mBounds.bottom); mVectorDrawable.draw(canvas); canvas.restore(); } // 根据level计算旋转角度(level 0→10000对应0°→360°) public void setLevel(int level) { mRotation = (level / 10000f) * 360f; invalidateSelf(); } @Override public void setAlpha(int alpha) { mVectorDrawable.setAlpha(alpha); } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { mVectorDrawable.setColorFilter(colorFilter); } @Override public int getOpacity() { return mVectorDrawable.getOpacity(); } }
使用时:
VectorDrawable vectorDrawable = (VectorDrawable) getDrawable(R.drawable.ic_info_black_24dp); RotateVectorDrawable rotateDrawable = new RotateVectorDrawable(vectorDrawable); imageView.setImageDrawable(rotateDrawable); rotateDrawable.setLevel(2500); // 对应90°,不会出现异常
内容的提问来源于stack exchange,提问作者Mir-Ismaili
相关产品推荐
相关产品推荐

