Реализовано и работает так, как и ожидалось, поэтому действительно нет кода, который стоит опубликовать здесь, просто глядя, чтобы узнать, есть ли у кого-нибудь опыт ускорения времени, которое требуется, чтобы ящик открывался и закрывался? Например, приложение YouTube намного быстрее!
Ответ 2
Альтернативой ускорению анимации и ожиданием ее завершения является простое исключение анимации: просто вызовите startActivity()
без вызова closeDrawer()
. Хотя вы не видите, что ящик закрыт, анимация перехода к действию по-прежнему обеспечивает довольно приятный эффект, и это происходит немедленно, без необходимости ждать, пока анимация закрывает ящик, чтобы завершить настройку сначала, без прерывания и более короткую задержку восприятия.
Подробнее
(Вы можете пропустить это объяснение, если хотите просто увидеть код.)
Чтобы выполнить эту работу, вам понадобится способ закрыть ящик без какой-либо близкой анимации, когда вы перейдете к активности с помощью кнопки "Назад". (Не вызывая closeDrawer()
, он оставит ящик открытым в этом экземпляре активности, относительно расточительным обходным путем будет просто принудительно активировать действие recreate()
при навигации назад, но это можно решить, не делая этого.) Вам также необходимо убедитесь, что вы только закрываете ящик, если вы возвращаетесь после навигации, а не после изменения ориентации, но это легко.
Хотя вызов closeDrawer()
из onCreate()
приведет к тому, что ящик начнет закрываться без какой-либо анимации, то же самое не относится к onResume()
. Вызов closeDrawer()
из onResume()
закроет ящик с анимацией, которая мгновенно отображается пользователю. DrawerLayout
не предоставляет никакого способа закрыть ящик без этой анимации, но можно добавить его.
Как отмечает @syesilova, закрытие ящика фактически просто сдвигает его с экрана. Таким образом, вы можете эффективно пропускать анимацию, перемещая ящик непосредственно в "закрытую" позицию. Направление трансляции будет меняться в зависимости от силы тяжести (будь то левый или правый ящик), а точное положение зависит от размера выдвижного ящика, когда оно выложено всеми его детьми.
Однако просто перемещать его недостаточно, поскольку DrawerLayout
сохраняет некоторое внутреннее состояние в расширенном LayoutParams
, которое оно использует, чтобы узнать, открыт ли ящик. Если вы просто переместите ящик с экрана, он не будет знать, что он закрыт, и это вызовет другие проблемы. (Например, ящик снова появится при следующем изменении ориентации.)
Поскольку вы компилируете библиотеку поддержки в свое приложение, вы можете создать класс в пакете android.support.v4.widget
, чтобы получить доступ к его частям по умолчанию (пакетно-частный) или расширить DrawerLayout
без копирования через любой из другие классы, в которых он нуждается. Это также уменьшит нагрузку на обновление кода с будущими изменениями в библиотеке поддержки. (Всегда лучше изолировать код от деталей реализации как можно больше.) Вы можете использовать moveDrawerToOffset()
для перемещения ящика и установить LayoutParams
, чтобы он знал, что ящик закрыт.
код
Это код, который пропустит анимацию:
// move drawer directly to the closed position
moveDrawerToOffset(drawerView, 0.f);
/* EDIT: as of v23.2.1 this direct approach no longer works
because the LayoutParam fields have been made private...
// set internal state so DrawerLayout knows it closed
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
lp.onScreen = 0.f;
lp.knownOpen = false;
invalidate();
/*/
// ...however, calling closeDrawer will set those LayoutParams
// and invalidate the view.
closeDrawer(drawerView);
/**/
Примечание:, если вы просто вызываете moveDrawerToOffset()
без изменения LayoutParams
, ящик вернется в открытое положение при следующем изменении ориентации.
Вариант 1 (используйте существующий DrawerLayout)
Этот подход добавляет класс утилиты в пакет support.v4, чтобы получить доступ к частным частям пакета, которые нам нужны внутри DrawerLayout.
Поместите этот класс в /src/android/support/v 4/widget/:
package android.support.v4.widget;
import android.support.annotation.IntDef;
import android.support.v4.view.GravityCompat;
import android.view.Gravity;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class Support4Widget {
/** @hide */
@IntDef({Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END})
@Retention(RetentionPolicy.SOURCE)
private @interface EdgeGravity {}
public static void setDrawerClosed(DrawerLayout drawerLayout, @EdgeGravity int gravity) {
final View drawerView = drawerLayout.findDrawerWithGravity(gravity);
if (drawerView == null) {
throw new IllegalArgumentException("No drawer view found with gravity " +
DrawerLayout.gravityToString(gravity));
}
// move drawer directly to the closed position
drawerLayout.moveDrawerToOffset(drawerView, 0.f);
/* EDIT: as of v23.2.1 this no longer works because the
LayoutParam fields have been made private, but
calling closeDrawer will achieve the same result.
// set internal state so DrawerLayout knows it closed
final DrawerLayout.LayoutParams lp = (DrawerLayout.LayoutParams) drawerView.getLayoutParams();
lp.onScreen = 0.f;
lp.knownOpen = false;
drawerLayout.invalidate();
/*/
// Calling closeDrawer updates the internal state so DrawerLayout knows it closed
// and invalidates the view for us.
drawerLayout.closeDrawer(drawerView);
/**/
}
}
Задайте логическое значение в вашей активности, когда вы перемещаетесь, указав, что ящик должен быть закрыт:
public static final String CLOSE_NAV_DRAWER = "CLOSE_NAV_DRAWER";
private boolean mCloseNavDrawer;
@Override
public void onCreate(Bundle savedInstanceState) {
// ...
if (savedInstanceState != null) {
mCloseNavDrawer = savedInstanceState.getBoolean(CLOSE_NAV_DRAWER);
}
}
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
// ...
startActivity(intent);
mCloseNavDrawer = true;
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean(CLOSE_NAV_DRAWER, mCloseNavDrawer);
super.onSaveInstanceState(savedInstanceState);
}
... и используйте метод setDrawerClosed()
, чтобы закрыть ящик в onResume()
без анимации:
@Overrid6e
protected void onResume() {
super.onResume();
if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
Support4Widget.setDrawerClosed(mDrawerLayout, GravityCompat.START);
mCloseNavDrawer = false;
}
}
Вариант 2 (простирается от DrawerLayout)
Этот подход расширяет DrawerLayout, чтобы добавить метод setDrawerClosed().
Поместите этот класс в /src/android/support/v 4/widget/:
package android.support.v4.widget;
import android.content.Context;
import android.support.annotation.IntDef;
import android.support.v4.view.GravityCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class CustomDrawerLayout extends DrawerLayout {
/** @hide */
@IntDef({Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END})
@Retention(RetentionPolicy.SOURCE)
private @interface EdgeGravity {}
public CustomDrawerLayout(Context context) {
super(context);
}
public CustomDrawerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setDrawerClosed(View drawerView) {
if (!isDrawerView(drawerView)) {
throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
}
// move drawer directly to the closed position
moveDrawerToOffset(drawerView, 0.f);
/* EDIT: as of v23.2.1 this no longer works because the
LayoutParam fields have been made private, but
calling closeDrawer will achieve the same result.
// set internal state so DrawerLayout knows it closed
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
lp.onScreen = 0.f;
lp.knownOpen = false;
invalidate();
/*/
// Calling closeDrawer updates the internal state so DrawerLayout knows it closed
// and invalidates the view for us.
closeDrawer(drawerView);
/**/
}
public void setDrawerClosed(@EdgeGravity int gravity) {
final View drawerView = findDrawerWithGravity(gravity);
if (drawerView == null) {
throw new IllegalArgumentException("No drawer view found with gravity " +
gravityToString(gravity));
}
// move drawer directly to the closed position
moveDrawerToOffset(drawerView, 0.f);
/* EDIT: as of v23.2.1 this no longer works because the
LayoutParam fields have been made private, but
calling closeDrawer will achieve the same result.
// set internal state so DrawerLayout knows it closed
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
lp.onScreen = 0.f;
lp.knownOpen = false;
invalidate();
/*/
// Calling closeDrawer updates the internal state so DrawerLayout knows it closed
// and invalidates the view for us.
closeDrawer(drawerView);
/**/
}
}
Используйте CustomDrawerLayout
вместо DrawerLayout
в ваших макетах действий:
<android.support.v4.widget.CustomDrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
>
... и установите логическое значение в вашей деятельности, когда вы перемещаетесь, указав, что ящик должен быть закрыт:
public static final String CLOSE_NAV_DRAWER = "CLOSE_NAV_DRAWER";
private boolean mCloseNavDrawer;
@Override
public void onCreate(Bundle savedInstanceState) {
// ...
if (savedInstanceState != null) {
mCloseNavDrawer = savedInstanceState.getBoolean(CLOSE_NAV_DRAWER);
}
}
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
// ...
startActivity(intent);
mCloseNavDrawer = true;
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean(CLOSE_NAV_DRAWER, mCloseNavDrawer);
super.onSaveInstanceState(savedInstanceState);
}
... и используйте метод setDrawerClosed()
, чтобы закрыть ящик в onResume()
без анимации:
@Overrid6e
protected void onResume() {
super.onResume();
if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.setDrawerClosed(GravityCompat.START);
mCloseNavDrawer = false;
}
}
Я нашел, что это лучший способ избежать трясти без каких-либо длительных задержанных задержек.
Вы могли бы использовать подобную технику, чтобы имитировать закрытие ящика после достижения активности, передав значение в намерении сказать новой активности открыть свой ящик без анимации от onCreate()
, а затем оживить его после макет активности завершен, однако в моих экспериментах переход активности разрушил эффект моделирования, поэтому вам также необходимо отключить его.