Анимация изменения макета нижнего листа
В моем приложении я использую нижний лист (из библиотеки поддержки), который отлично работает. Теперь я хотел бы оживить изменение макета, пока лист перетаскивается. Для этого я создал подкласс BottomSheetCallback
(это нормальный внутренний класс фрагмента, поэтому здесь не все объекты, используемые в этих calss):
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
, который основан на внешней переменной, а не во времени?
Ответы
Ответ 1
Основываясь на вашем описании, я думаю, что вы пытаетесь достичь чего-то вроде поведения нижнего листа карты Google. Макет изменяется при перетаскивании нижнего листа.
Если это то, чего вы пытаетесь достичь, вам не нужно принудительно выполнять пользовательские анимации, так как сам нижний индекс сам по себе имеет такое поведение анимации, когда он встроен в родительский Макет координатора.
Вот пример кода того, как я реализую одно и то же поведение. Это также делает невидимым FloatingActionButton, когда нижний лист перетаскивается до полного размера экрана:
-
Создайте нижестоящий указатель, который вы хотите использовать внутри основного макета
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;
}
}
-
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>
-
Вызов этого нижнего листа из вашей активности:
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
}
Надеюсь, это решит то, чего вы пытаетесь достичь.
Ответ 2
Я не уверен, что это то, что вы хотите, но, возможно, вместо использования перехода, вы можете использовать функцию animate()
, поскольку с помощью этой функции вы можете изменить все о своей анимации (время, видимость и т.д.).
Ответ 3
## 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);
}
Ответ 4
Чтобы легко сдвинуть что-то в нижней части экрана, вы можете использовать код, например:
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));
}