无需使用Fragment,能否仅让Activity部分区域实现滑入滑出转场?
当然可以实现!不用Fragment也能搞定Activity局部区域的滑入滑出转场,我给你几个经过项目验证的实用方案,完美匹配你不想搞“上帝Activity”的需求:
方案一:手动控制View动画+无默认转场
这是最直接的思路,核心是让两个Activity的固定区域视觉上完全一致,只对各自的滑动区域执行动画,同时禁用系统默认的Activity转场动画,营造出只有局部滑动的效果。
具体步骤:
- 布局统一:在所有需要这种转场的Activity布局中,拆分出「固定区域」和「滑动区域」。比如用ConstraintLayout,固定区域占满屏幕除了底部滑动区的位置,滑动区域固定在底部,高度设为
wrap_content或固定值。确保所有Activity的固定区域布局、样式、尺寸完全相同。 - 自定义滑动逻辑:不用xml动画文件也可以,直接用代码动态创建属性动画更灵活。
- 启动新Activity时的动画控制:
在当前Activity中,先让滑动区域执行滑出动画,动画结束后启动目标Activity,并禁用系统默认转场:View slidingContainer = findViewById(R.id.sliding_container); slidingContainer.animate() .translationY(slidingContainer.getHeight()) .setDuration(300) .setInterpolator(new AccelerateDecelerateInterpolator()) .withEndAction(() -> { Intent intent = new Intent(this, TargetActivity.class); startActivity(intent); // 取消系统默认的Activity切换动画 overridePendingTransition(0, 0); }); - 目标Activity的入场动画:
在目标Activity的onCreate方法中,先把滑动区域初始位置设到屏幕外,再执行滑入动画:View slidingContainer = findViewById(R.id.sliding_container); // 初始状态让滑动区域在屏幕外 slidingContainer.setTranslationY(slidingContainer.getHeight()); slidingContainer.animate() .translationY(0) .setDuration(300) .setInterpolator(new AccelerateDecelerateInterpolator()) .start(); - 返回时的动画处理:
重写目标Activity的onBackPressed,先执行滑动区域滑出动画,结束后finish并禁用默认转场:
同时在原Activity的@Override public void onBackPressed() { View slidingContainer = findViewById(R.id.sliding_container); slidingContainer.animate() .translationY(slidingContainer.getHeight()) .setDuration(300) .setInterpolator(new AccelerateDecelerateInterpolator()) .withEndAction(() -> { super.onBackPressed(); overridePendingTransition(0, 0); }); }onRestart方法中,触发滑动区域的滑入动画,保证返回时的流畅性。
方案二:自定义系统转场动画(针对局部区域)
如果想利用系统的Activity转场框架,也可以通过自定义转场动画,只让滑动区域参与动画,固定区域保持静态。
具体做法:
- 创建自定义的
Transition类,重写createAnimator方法,只对指定的滑动区域View创建动画:public class PartialSlideTransition extends Transition { private static final String PROPNAME_TRANSLATION_Y = "android:translationY"; @Override public void captureStartValues(TransitionValues transitionValues) { captureValues(transitionValues); } @Override public void captureEndValues(TransitionValues transitionValues) { captureValues(transitionValues); } private void captureValues(TransitionValues values) { View view = values.view; // 只捕获滑动区域的属性 if (view.getId() == R.id.sliding_container) { values.values.put(PROPNAME_TRANSLATION_Y, view.getTranslationY()); } } @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { if (startValues == null || endValues == null) { return null; } View slidingView = endValues.view; float startY = (float) startValues.values.get(PROPNAME_TRANSLATION_Y); float endY = (float) endValues.values.get(PROPNAME_TRANSLATION_Y); if (startY != endY) { slidingView.setTranslationY(startY); return ObjectAnimator.ofFloat(slidingView, View.TRANSLATION_Y, startY, endY) .setDuration(300) .setInterpolator(new AccelerateDecelerateInterpolator()); } return null; } } - 在启动Activity时设置转场:
Intent intent = new Intent(this, TargetActivity.class); ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this); startActivity(intent, options.toBundle()); - 在目标Activity的
onCreate中设置转场:
这种方式的好处是可以利用系统转场的生命周期回调,更规范,但需要确保只有滑动区域被标记为参与转场的View。getWindow().setEnterTransition(new PartialSlideTransition()); getWindow().setExitTransition(new PartialSlideTransition());
关键注意事项
- 固定区域的一致性:一定要保证所有相关Activity的固定区域完全一致,包括布局结构、控件位置、颜色尺寸,否则切换时会出现视觉断层。
- 动画同步:所有滑动动画的时长、插值器要统一,保证过渡流畅自然。
- 性能优化:如果滑动区域包含复杂控件,建议开启硬件加速,避免动画卡顿。
内容的提问来源于stack exchange,提问作者Archie G. Quiñones
相关产品推荐
相关产品推荐

