在导航图中定义的示例导航操作中:
<action
android:id="@+id/action_fragment1_to_fragment2"
app:destination="@id/fragment2"
app:enterAnim="@anim/right_slide_in"
app:popExitAnim="@anim/left_slide_out"/>
当Fragment2
打开并开始从右侧滑入视图时,Fragment1
立即消失(可悲)。当Fragment2
关闭并开始向右滑动时,Fragment1
在其下方非常清晰可见,从而提供了不错的堆栈弹出效果(与iOS相比)。
当Fragment1
滑入视图时,如何保持Fragment2
可见?
答案 0 :(得分:8)
编辑:
这不是最优雅的解决方案,它实际上是一个技巧,但是在NavigationComponent
包含更好的方法之前,它似乎是解决这种情况的最佳方法。
因此,我们可以通过translationZ
的{{1}}方法增加Fragement2
(从API 21开始),使其显示在onViewCreated
上方。
示例:
Fragment1
如@xinaiz所建议的那样,我们可以使用@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ViewCompat.setTranslationZ(getView(), 100f);
}
代替100f
或任何其他随机值来为片段分配比上一个更高的海拔。
@JFrite在此线程上提出了解决方案
FragmentTransaction animation to slide in over top
可以在此处找到更多详细信息。
答案 1 :(得分:0)
为什么不使用ViewPager? 它将照顾动画并保持片段的正确生命周期。当片段在onResume()中更改时,您将能够对其进行更新。
设置好ViewPager后,您可以通过滑动来更改片段,或自动跳转到所需的片段,而不必担心手工编码转换,翻译等:viewPager.setCurrentItem(1);
示例和更深入的描述: https://developer.android.com/training/animation/screen-slide
在活动布局XML中:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:fillViewport="true">
<include
layout="@layout/toolbar"
android:id="@+id/main_toolbar"
android:layout_width="fill_parent"
android:layout_height="?android:attr/actionBarSize">
</include>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:minHeight="?android:attr/actionBarSize"/>
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
在Activity类的onCreate()中:
ViewPager viewPager = null;
TabLayout tabLayout = null;
@Override
public void onCreate() {
...
tabLayout = findViewById(R.id.tab_layout);
viewPager = findViewById(R.id.pager);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
String[] tabs = new String[]{"Tab 1", "Tab 2"};
for (String tab : tabs) {
tabLayout.addTab(tabLayout.newTab().setText(tab));
}
PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager(), tabLayout);
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
...
}
您的PagerAdapter类,它可以驻留在您的Activity类中:
public class PagerAdapter extends FragmentStatePagerAdapter {
TabLayout tabLayout;
PagerAdapter(FragmentManager fm, TabLayout tabLayout) {
super(fm);
this.tabLayout = tabLayout;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new your_fragment1();
case 1:
return new your_fragment2();
default:
return null;
}
return null;
}
@Override
public int getCount() {
return tabLayout.getTabCount();
}
}
确保使用适当的导入:
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
答案 2 :(得分:0)
似乎您错误地使用了popExitAnim
而不是exitAnim
。
一般规则是:
打开(推动)新屏幕时,enterAnim
和exitAnim
发生
当您弹出屏幕时,popEnterAnim
和popExitAnim
发生
因此,您应该为每个过渡指定所有4种动画。
例如,我使用这些:
<action
android:id="@+id/mainToSearch"
app:destination="@id/searchFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
答案 3 :(得分:0)
我认为使用R.anim.hold
动画将创建您想要的效果:
int holdingAnimation = R.anim.hold;
int inAnimation = R.anim.right_slide_in;
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(inAnimation, holdingAnimation, inAnimation, holdingAnimation);
/*
... Add in your fragments and other navigation calls
*/
transaction.commit();
getSupportFragmentManager().executePendingTransactions();
或者只是将其标记为操作中的内容。
这是上面提到的R.anim.hold
动画:
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_longAnimTime"
android:fromYDelta="0.0%p"
android:toYDelta="0.0%p"/>
</set>
答案 4 :(得分:0)
在我自己的情况下,最简单的解决方案是使用具有适当动画和样式的DialogFragment
。
样式:
<style name="MyDialogAnimation" parent="Animation.AppCompat.Dialog">
<item name="android:windowEnterAnimation">@anim/slide_in</item>
<item name="android:windowExitAnimation">@anim/slide_out</item>
</style>
<style name="MyDialog" parent="ThemeOverlay.MaterialComponents.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">@color/transparent</item>
<item name="android:windowAnimationStyle">@style/MyDialogAnimation</item>
</style>
布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:background="@color/colorWhite"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:layout_gravity="bottom"
android:orientation="vertical"
android:scrollbars="none"
android:transitionGroup="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
// Your Ui here
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
Java:
public class MyFragmentDialog extends DialogFragment {
@Nullable
@Override
public View onCreateView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_dialog, container, false);
}
@Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
int width = ViewGroup.LayoutParams.MATCH_PARENT;
int height = ViewGroup.LayoutParams.MATCH_PARENT;
Objects.requireNonNull(dialog.getWindow())
.setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
Objects.requireNonNull(dialog.getWindow()).setLayout(width, height);
dialog.getWindow().setWindowAnimations(R.style.MyDialogAnimation);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, R.style.MyDialog);
}
}
答案 5 :(得分:0)
为了防止旧片段在新片段的滑动动画中消失,首先制作一个仅包含滑动动画持续时间的空动画。我称它为@anim/stationary
:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@slidingAnimationDuration" />
然后在导航图中,将动作的退出动画设置为新创建的空动画:
<fragment android:id="@+id/oldFragment"
android:name="OldFragment">
<action android:id="@+id/action_oldFragment_to_newFragment"
app:destination="@id/newFragment"
app:enterAnim="@anim/sliding"
app:exitAnim="@anim/stationary"
</fragment>
退出动画将应用于旧片段,因此在整个动画过程中可见旧片段。
我对为什么旧片段消失的猜测是,如果您不指定退出动画,则默认情况下,在输入动画开始时,旧片段将立即被删除。
答案 6 :(得分:0)
使用新的材质运动库,添加幻灯片动画非常容易。确保使用实体主题版本1.2.0
或更高版本。
例如,如果要使用幻灯片动画从FragmentA导航到FragmentB,请按照以下步骤操作。
在onCreate()
的{{1}}中,添加一个FragmentA
,如下所示。
exitTransition
在override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exitTransition = MaterialFadeThrough().apply {
secondaryAnimatorProvider = null
}
}
的{{1}}中,添加一个onCreate()
,如下所示。
FragmentB
上面的代码将创建一个动画,使动画淡出FragmentA,并在FragmentB中滑动。
答案 7 :(得分:0)
假设您的后排栈当前包含:
A-> B-> C
现在从片段C开始,您要导航到片段D。
所以您的动画: enterAnim->应用于D片段 exitAnim->应用于C片段
更新后的堆栈为:
A-> B-> C-> D
现在您按下“后退”或“向上”按钮
popEnterAnim->应用于C片段 popExitAnim->应用于D片段
现在您的后备箱将再次出现:
A-> B-> C
TL; DR:enterAnim,exitAnim用于推送,而popEnterAnim,popExitAnim用于弹出操作。