底部表单的布局变化动画化

35

我的应用程序中使用了一个底部工具栏(来自支持库),它运行良好。现在我想要在拖动表格时动画更改布局。为此,我创建了 BottomSheetCallback 的子类(通常是 Fragment 的内部类,因此并非在此处初始化所有对象使用的类):

public class MyBehavior extends BottomSheetBehavior.BottomSheetCallback {

    Transition transition;
    float lastOffset = 0;
    Scene scene;

    public PlayerBehavior() {
        TransitionInflater inflater = TransitionInflater.from(getContext());
        transition = inflater.inflateTransition(R.transition.player);
        //transition.setDuration(300);

        scene = fullLayout;

        transition.setInterpolator(new Interpolator() {
            @Override
            public float getInterpolation(float v) {
                return lastOffset;
            }
        });
    }

    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
        if(newState == BottomSheetBehavior.STATE_DRAGGING) {
            TransitionManager.go(scene, transition);
        }
    }

    @Override
    public void onSlide(View bottomSheet, final float slideOffset) {
        scene = (slideOffset > lastOffset) ? smallLayout : fullLayout;
        lastOffset = slideOffset;
    }
}

正如您所看到的,我还从不同的布局文件中创建了两个Scene和一个自定义的Transition,并使用TransitionManager在场景之间进行动画过渡。 我的问题是,Transition 应该基于 slideOffset 参数(范围为0-1),但是 TransitionManager 在后台使用的是 Animation 类,在 Android 中通常是基于时间的。

我尝试创建自定义的Intapolator,但这并没有有效地解决我的问题。 那么,我应该如何创建一个基于外部变量而不是时间的Transition呢?


4
可以提供一个视觉化的例子吗?例如截图? - Pedro Varela
我认为这是不可能的。BottomSheet 的偏移值从 -1 到 1,并不总是在 0 和 1 或者 -1 和 0 之间反弹。在某些情况下,它会从 0.3 开始上升到 1.0f。我以前也遇到过同样的问题。我必须监听状态改变事件并基于时间进行动画处理。 - Gokhan Arik
你需要哪种类型的动画。 - Khemraj Sharma
5个回答

4
根据您的描述,我认为您正在尝试实现类似于Google地图底部表格行为的功能。当拖动bottomsheet时,布局会发生变化。
如果您要实现这一点,那么您不需要强制执行自定义动画,因为bottomsheetdialog本身在父级Coordinator Layout中具有这些动画行为。
以下是我实现相同行为的示例代码。当bottomsheet被拖到全屏大小时,它还会使FloatingActionButton不可见:
  1. Create a bottomsheetdialog that you want to use inside your main layout

    public class CustomBottomDialog extends BottomSheetDialogFragment {
    
    String mSomeName;
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // if some arguments are passed from the calling activity 
        mSomeName = getArguments().getString("some_name");
    
    
    }
    
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View bottomSheet = inflater.inflate(R.layout.bottomsheet_layout, container, false);
         // initialise your bottomsheet_layout items here 
        TextView tvName = bottomSheet.findViewById(R.id.display_name);
        tvName.setText(mSomeName); 
        tvName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               // do something here
                ((MainActivity)getActivity()).doSomething();
            }
        });
    
        return bottomSheet;
    }
    }
    
  2. bottomsheet_layout:

    <android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <android.support.design.widget.FloatingActionButton
    android:id="@+id/nav"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/navigation_tilt_grey"
    app:backgroundTint="@color/colorAccent"
    app:elevation="3dp"
    app:fabSize="normal"
    android:layout_marginEnd="@dimen/activity_horizontal_margin"
    app:layout_anchor="@+id/live_dash"
    app:layout_anchorGravity="top|right" />
    
    <!--BottomSheet-->
    
    <android.support.v4.widget.NestedScrollView
    android:id="@+id/live_dash"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#F3F3F3"
    android:clipToPadding="true"
    app:layout_behavior="android.support.design.widget.BottomSheetBe 
    havior"
    tools:layout_editor_absoluteY="150dp">
    
    <!--Include your items here, the height of all items combined
    will take the main screen layout size with animation-->
    
    </android.support.v4.widget.NestedScrollView>
    
    </android.support.design.widget.CoordinatorLayout>
    
  3. Calling this BottomSheet from your activity:

    public void notifyBottomSheet(String somename){
    
    BottomSheetDialogFragment customDialogFragment = new CustomBottomDialog();
    Bundle args = new Bundle();
    args.putString("some_name", somename);
    customDialogFragment.setArguments(args);
    customDialogFragment.show(getSupportFragmentManager(), customDialogFragment.getTag());
    customDialogFragment.setCancelable(false); // if you don't wish to hide
    }
    

    Hope this solves what you are trying to achieve.


3
我觉得我已经盯着这个东西很久了,试图理解它是如何工作的。如果按照问题所问的想法,将布局从“窥视”转换为“非窥视”,那它是如何知道要从哪里到哪里进行动画呢? - Kane Cheshire

0

我不确定这是否符合您的要求,但也许您可以使用函数animate()而不是使用transition。因为使用这个函数,您可以更改有关动画的所有内容(时间、可见性等)。


0

为了轻松地将某物从屏幕底部滑出,您可以使用以下代码:

final int activityHeight = findViewById(android.R.id.content).getHeight();
cardContainer.animate().yBy(activityHeight - cardContainer.getY()).setDuration(SLIDE_OUT_DURATION);

其中cardContainer是您要滑出屏幕的视图。

请参阅博客文章以获取完整示例。请注意,您还可以使用translationY而不是yBy。另一种更通用的方法是使用以下代码:

public static ViewPropertyAnimator slideOutToBottom(Context ctx, View view) {
    final int screenHeight = ctx.getResources().getDisplayMetrics().heightPixels;
    int[] coords = new int[2];
    view.getLocationOnScreen(coords);
    return view.animate().translationY(screenHeight - coords[Y_INDEX]).setDuration(SLIDE_OUT_DURATION);
}

public static ViewPropertyAnimator slideInFromBottom(Context ctx, View view) {
    final int screenHeight = ctx.getResources().getDisplayMetrics().heightPixels;
    int[] coords = new int[2];
    view.getLocationOnScreen(coords);
    view.setTranslationY(screenHeight - coords[Y_INDEX]);
    return view.animate().translationY(0).setDuration(SLIDE_IN_DURATION).setInterpolator(new OvershootInterpolator(1f));
}

0
## Translation Animation ##
<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:fillAfter="true"
    >
    <translate
        android:fromYDelta="100%p"
        android:toYDelta="-30%p"
        android:duration="900" />
</set>

## 主要活动 ##

@Override
protected void onResume() {
    super.onResume();
    Animation am= AnimationUtils.loadAnimation(this,R.anim.fadeout);
    tv5.startAnimation(am);
    Animation myanim= AnimationUtils.loadAnimation(this,R.anim.translate);
    tv1.startAnimation(myanim);
    myanim.setStartOffset(500);
    Animation animation= AnimationUtils.loadAnimation(this,R.anim.translate);
    animation.setStartOffset(1000);
    tv2.startAnimation(animation);
    Animation an= AnimationUtils.loadAnimation(this,R.anim.translate);
    an.setStartOffset(1500);
    tv3.startAnimation(an);
    Animation ab= AnimationUtils.loadAnimation(this,R.anim.translate);
    ab.setStartOffset(2000);
    tv4.startAnimation(ab);
    Animation ac= AnimationUtils.loadAnimation(this,R.anim.fadein);
    ac.setStartOffset(2500);
    btn1.startAnimation(ac);
}

0
这是我尝试使用setWindowsAnimation(res int style)的结果:
mainBottomSheet.getWindow().setWindowAnimations(R.style.YourAnimation);

styles.xml中添加您喜欢的动画,如下所示:
<style name="YourAnimation" parent="Animation.AppCompat.Dialog">
      <item name="android:windowEnterAnimation">@anim/slide_in</item>
      <item name="android:windowExitAnimation">@anim/slide_out</item>
</style>

将以下文件添加到res/anime目录中:
<!-- slide_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:duration="700"
     android:interpolator="@anim/interpolator_slight_overshoot">

    <translate
        android:fromYDelta="-100%"
        android:toYDelta="0%"/>

</set>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接