You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从资源文件使用自定义Transition?自定义Rotate转场定义咨询

嘿,我来帮你搞定这个自定义Rotate Transition的问题,让它能正常在XML转场资源里使用~

要让自定义Transition支持XML资源文件解析,核心是要满足系统的实例化规则,同时补全转场逻辑的必要方法。下面是完整的实现步骤和代码:

第一步:完善自定义Rotate Transition类

必须实现带Context和AttributeSet参数的构造方法(这是XML解析时系统实例化类的关键入口),同时补全转场所需的属性捕获、动画创建逻辑:

public class Rotate extends Transition {
    // 定义转场属性的唯一标识,用于保存状态
    private static final String PROPNAME_ROTATION = "com.your.package:rotate:rotation";
    private static final String[] TRANSITION_PROPERTIES = {PROPNAME_ROTATION};

    // 可配置的旋转参数
    private float mStartAngle = 0f;
    private float mEndAngle = 360f;
    private float mPivotX = 0.5f; // 默认以视图中心为旋转轴X
    private float mPivotY = 0.5f; // 默认以视图中心为旋转轴Y

    // 代码中直接实例化时用的空构造
    public Rotate() {}

    // 关键:XML解析时的构造方法,必须实现
    public Rotate(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 解析XML中的属性(包括系统自带和自定义属性)
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RotateTransition);
        
        // 复用系统的duration和interpolator属性
        setDuration(a.getInt(R.styleable.RotateTransition_android_duration, (int) getDuration()));
        int interpolatorId = a.getResourceId(R.styleable.RotateTransition_android_interpolator, 0);
        if (interpolatorId != 0) {
            setInterpolator(AnimationUtils.loadInterpolator(context, interpolatorId));
        }
        
        // 解析自定义的旋转参数
        mStartAngle = a.getFloat(R.styleable.RotateTransition_startAngle, mStartAngle);
        mEndAngle = a.getFloat(R.styleable.RotateTransition_endAngle, mEndAngle);
        mPivotX = a.getFloat(R.styleable.RotateTransition_pivotX, mPivotX);
        mPivotY = a.getFloat(R.styleable.RotateTransition_pivotY, mPivotY);
        
        a.recycle();
    }

    @Override
    public String[] getTransitionProperties() {
        return TRANSITION_PROPERTIES;
    }

    // 捕获转场开始时的视图状态
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        transitionValues.values.put(PROPNAME_ROTATION, transitionValues.view.getRotation());
    }

    // 捕获转场结束时的视图状态
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        transitionValues.values.put(PROPNAME_ROTATION, mEndAngle);
    }

    // 创建实际的旋转动画
    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        if (startValues == null || endValues == null) return null;
        
        View targetView = endValues.view;
        float startRotation = (float) startValues.values.get(PROPNAME_ROTATION);
        float endRotation = (float) endValues.values.get(PROPNAME_ROTATION);
        
        if (startRotation == endRotation) return null;
        
        // 设置旋转轴心(基于视图自身尺寸的比例)
        targetView.setPivotX(targetView.getWidth() * mPivotX);
        targetView.setPivotY(targetView.getHeight() * mPivotY);
        
        // 创建属性动画
        ObjectAnimator rotateAnim = ObjectAnimator.ofFloat(targetView, View.ROTATION, startRotation, endRotation);
        rotateAnim.setDuration(getDuration());
        rotateAnim.setInterpolator(getInterpolator());
        
        return rotateAnim;
    }
}
第二步:定义自定义属性(可选但推荐)

在res/values/attrs.xml中声明自定义属性,这样可以在XML里配置旋转角度、轴心等参数:

<resources>
    <declare-styleable name="RotateTransition">
        <!-- 复用系统自带的duration和interpolator属性 -->
        <attr name="android:duration"/>
        <attr name="android:interpolator"/>
        <!-- 自定义旋转参数 -->
        <attr name="startAngle" format="float"/>
        <attr name="endAngle" format="float"/>
        <attr name="pivotX" format="float"/>
        <attr name="pivotY" format="float"/>
    </declare-styleable>
</resources>
第三步:修改XML转场资源文件

有两种方式让系统识别你的自定义Transition:

方式1:直接使用全类名(简单直接)

修改res/transition/rotate_transition.xml,用自定义类的完整包路径作为标签名:

<?xml version="1.0" encoding="utf-8"?>
<com.your.package.Rotate 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:duration="275"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    app:startAngle="0"
    app:endAngle="360"/>

方式2:注册别名,继续用<rotate>标签

如果你想保留原来的<rotate>标签,可以在Application的onCreate方法中注册Transition工厂:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        TransitionInflater inflater = TransitionInflater.from(this);
        inflater.setFactory((className, context, attrs) -> {
            // 当系统解析<rotate>标签时,实例化我们的自定义类
            if ("rotate".equals(className)) {
                return new Rotate(context, attrs);
            }
            // 其他转场交给系统处理
            return TransitionInflater.from(context).createTransition(className, context, attrs);
        });
    }
}

这样你就可以继续使用原来的XML写法:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="275"
    android:interpolator="@android:interpolator/accelerate_decelerate"/>
第四步:使用自定义转场

在Activity或Fragment中加载并使用这个转场:

// Activity转场示例
Transition rotateTransition = TransitionInflater.from(this).inflateTransition(R.transition.rotate_transition);
getWindow().setExitTransition(rotateTransition);

// Fragment转场示例
MyFragment fragment = new MyFragment();
fragment.setEnterTransition(rotateTransition);
getSupportFragmentManager().beginTransaction()
        .replace(R.id.container, fragment)
        .commit();

内容的提问来源于stack exchange,提问作者Vladimir Petrovski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:33:29