Подтвердить что ты не робот

Ускорить скорость анимации "Навигационный ящик" при закрытии?

Реализовано и работает так, как и ожидалось, поэтому действительно нет кода, который стоит опубликовать здесь, просто глядя, чтобы узнать, есть ли у кого-нибудь опыт ускорения времени, которое требуется, чтобы ящик открывался и закрывался? Например, приложение YouTube намного быстрее!

4b9b3361

Ответ 1

Вы можете точно настроить продолжительность анимации, но вам потребуется скопировать классы из библиотеки поддержки, а затем отредактировать их соответствующим образом.

ViewDragHelper

Продолжительность определяется здесь в ViewDragHelper

Затем применяется к DrawerLayout, когда ViewDragHelper.smoothSlideViewTo вызывается

Вам понадобится создать измененную версию ViewDragHelper.forceSettleCapturedViewAt, которая будет проходить с параметром длительности.

forceSettleCapturedViewAt(... int duration)

Затем создайте свою версию ViewDragHelper.smoothSlideViewTo.

public boolean smoothSlideViewTo(... int duration) {
        ...
        return forceSettleCapturedViewAt(... int duration);
    }

DrawerLayout

Затем вам нужно будет изменить DrawerLayout.closeDrawer и DrawerLayout.closeDrawers в соответствии с вашим новым ViewDragHelper.

ActionBarDrawerToggle

Вам также придется копировать ActionBarDrawerToggle и ActionBarDrawerToggleHoneycomb. Однако эти файлы не требуют редактирования.

Ответ 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(), а затем оживить его после макет активности завершен, однако в моих экспериментах переход активности разрушил эффект моделирования, поэтому вам также необходимо отключить его.

Ответ 3

Сначала снизу ссылки загружают файлы sourcode

DrawerLayout.java

И

ViewDrawerHelper.java

и вставить вышеуказанные два файла в ваших приложениях используйте пакет (или где вы хотите) и ссылайтесь на этот макет ящика в своей деятельности, а не на android.support.v4.widget.DrawerLayout измените свой указатель макета ящика в файле макета работы,

Теперь отрегулируйте

private static final int MAX_SETTLE_DURATION = 600; // ms

of ViewDrawerHelper, для ускорения просто увеличьте значение, а вниз уменьшите значение.

Если вы хотите добавить действие на кнопку переключения панели действий, то ниже ссылки загружают исходные файлы

ActionBarDrawerToggle.java

ActionBarDrawerToggleJellybeanMR2.java

ActionBarDrawerToggleHoneycomb.java

и вставьте вышеуказанные файлы в свои приложения, используя пакет (или где вы хотите). Примечание. - Убедитесь, что импортированные пакеты каждого вновь добавленного файла относятся к файлу, который находится в вашем проекте приложения, а не к android.support.v4.widget *;.

Если приведенные выше ссылки не работают, добавьте http://

Ответ 4

Если вы хотите принудительно немедленно удалить левую панель без какой-либо анимации, вы можете просто установить ее значение x. Когда макет ящика открывается, его значение x левой левой панели становится 0, а при закрытии становится -1 * (его ширина). Поэтому, если вы устанавливаете x значение -2 * width при его открытии, левая панель сразу же исчезает. И, конечно же, не забудьте установить x на -1 * ширину после ее закрытия. Например:

DisplayMetrics metrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(metrics);
//obtain left panel width in px
private float mToggleStartX=-260*metrics.density; //260 is the width of left panel in dpi

//while drawer layout is opened, to disappear left panel
ll_drawerLayoutMenuPanel.setX(mToggleStartX*2); //ll_drawerLayoutMenuPanel is the left panel layout
mDrawerLayout.closeDrawers();

//don't forget reset x value in the onDrawerClosed method.
mDrawerToggle = new ActionBarDrawerToggle(this,mDrawerLayout,mainToolBar,R.string.drawer_open,R.string.drawer_close) {
    public void onDrawerClosed(View view) {
        super.onDrawerClosed(view);

        ll_drawerLayoutMenuPanel.setX(mToggleStartX);
    }
    ......
};

Ответ 5

Это не позволяет вам изменять скорость анимации, но если вы хотите только мгновенно закрыть ящик, вы можете использовать новые методы DrawerLayout.closeDrawer(int/View, bool) в v24 библиотеки поддержки:

drawerLayout.closeDrawer(Gravity.LEFT, false);

Ответ 6

Я считаю, что реальный смысл вашего вопроса в том, как я могу сделать анимацию более плавным после того, как я нажму меню в drawerlayout, которое запустит новую активность.
Если это значение вашего вопроса, вот как я это делаю.

mLeftDrawer.ItemClick += delegate (object sender, Android.Widget.AdapterView.ItemClickEventArgs e)
        {
            // Mark that item is selected and ask redraw
            e.View.Selected = true;
            adapter.NotifyDataSetChanged();

            var handler = new Handler();
            handler.PostDelayed(new Java.Lang.Runnable(() =>
            {
                _mLeftDrawerLayout.CloseDrawers();

                // Here you should call your activity
            }), 100);

        };