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

Скрыть навигационный ящик при нажатии кнопки "Назад"

Я сделал следующие учебники для разработчиков Google здесь, чтобы создать навигационный ящик.

В настоящий момент все работает нормально, за исключением случаев, когда пользователь использует встроенную кнопку обратной связи, которую Android поддерживает в нижней части экрана (наряду с домашними и недавними кнопками приложения). Если пользователь перейдет обратно с помощью этой кнопки "назад", навигационный ящик по-прежнему будет открыт. Если пользователь вместо этого перемещается назад с помощью ActionBar, ящик навигации будет закрыт, как я хочу.

Мой код почти идентичен официальным учебным пособиям, за исключением того, как я обрабатываю пользователя, выбрав элемент в ящике:

   mDrawerList.setOnItemClickListener(new ListView.OnItemClickListener()
    {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id)
        {
            switch(position)
            {
                case 0:
                {
                    Intent intent = new Intent(MainActivity.this, NextActivity.class);
                    startActivity(intent);
                }
            }
        }
    });

Как я могу закрыть ящик навигации, когда пользователь перейдет обратно с помощью кнопки "назад"? Любые советы приветствуются. Спасибо!

4b9b3361

Ответ 1

Вам нужно переопределить onBackPressed(). Из документов:

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

Итак, у вас может быть такой код:

@Override
public void onBackPressed() {
    if (this.drawerLayout.isDrawerOpen(GravityCompat.START)) {
        this.drawerLayout.closeDrawer(GravityCompat.START);
    } else {
        super.onBackPressed();
    }
}

Если это открыто, этот метод закрывает его, иначе возвращается к поведению по умолчанию.

Ответ 2

Вам нужно переопределить onBackPressed() в своей деятельности и проверить состояние, в котором открыт ящик навигации. Если он открыт, закройте его, иначе выполните обычный метод назад. Ниже приведен код, смешанный с некоторым псевдокодом, который поможет вам:

@Override
public void onBackPressed(){
  if(drawer.isDrawerOpen()){ //replace this with actual function which returns if the drawer is open
   drawer.close();     // replace this with actual function which closes drawer
  }
  else{
   super.onBackPressed();
  }
}

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

Ответ 3

UPDATE:

Как и в библиотеке поддержки 24.0.0, это возможно без каких-либо обходных решений. Два новых openDrawer и closeDrawer методы до DrawerLayout, которые позволяют открывать или закрывать ящик без анимации.

Теперь вы можете использовать openDrawer(drawerView, false) и closeDrawer(drawerView, false), чтобы открывать и закрывать ящик без задержки.


Если вы вызываете startActivity() без вызова closeDrawer(), ящик будет оставлен открытым в этом экземпляре активности, когда вы перейдете к нему с помощью кнопки "Назад". Вызов closeDrawer(), когда вы вызываете startActivity(), имеет несколько проблем, начиная от прерывистой анимации и заканчивая длительной задержкой восприятия, в зависимости от того, какой метод обхода вы используете. Поэтому я согласен, что лучший подход - просто позвонить startActivity(), а затем закрыть ящик после возврата.

Чтобы сделать эту работу красивой, вам необходимо закрыть ящик без близкой анимации, когда вы переходите к активности с помощью кнопки "Назад". (Относительно расточительным обходным путем было бы просто заставить активность recreate() при навигации назад, но это можно решить, не делая этого.)

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


Подробнее

(Вы можете пропустить это объяснение, если хотите просто увидеть код.)

Хотя вызов closeDrawer() из onCreate() приведет к тому, что ящик начнет закрываться без какой-либо анимации, то же самое не относится к onResume(). Вызов closeDrawer() из onResume() закроет ящик с анимацией, которая мгновенно отображается пользователю. DrawerLayout не предоставляет никакого способа закрыть ящик без этой анимации, но его можно расширить, чтобы добавить его.

Закрытие ящика фактически просто сдвигает его с экрана, поэтому вы можете эффективно пропускать анимацию, перемещая ящик непосредственно в "закрытое" положение. Направление трансляции будет меняться в зависимости от силы тяжести (будь то левый или правый ящик), а точное положение зависит от размера выдвижного ящика, когда оно выложено всеми его детьми.

Однако просто перемещать его недостаточно, поскольку DrawerLayout сохраняет некоторое внутреннее состояние в расширенном LayoutParams, которое оно использует, чтобы узнать, открыт ли ящик. Если вы просто переместите ящик с экрана, он не будет знать, что он закрыт, и это вызовет другие проблемы. (Например, ящик снова появится при следующем изменении ориентации.)

Поскольку вы компилируете библиотеку поддержки в свое приложение, вы можете создать класс в пакете android.support.v4.widget, чтобы получить доступ к его частям по умолчанию (пакетно-частный) или расширить DrawerLayout без копирования через любой из другие классы, в которых он нуждается. Это также уменьшит нагрузку на обновление кода с будущими изменениями в библиотеке поддержки. (Всегда лучше всего изолировать свой код от деталей реализации.) Вы можете использовать moveDrawerToOffset() для перемещения ящика и установить LayoutParams, чтобы он знал, что ящик закрыт.


код

Это код, который пропустит анимацию:

        // move drawer directly to the closed position
        moveDrawerToOffset(drawerView, 0.f); 

        // set internal state so DrawerLayout knows it closed
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        lp.onScreen = 0.f;
        lp.knownOpen = false;

        invalidate();

Примечание:, если вы просто вызываете 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); 

        // 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();
    }
}

Задайте логическое значение в вашей активности, когда вы перемещаетесь, указав, что ящик должен быть закрыт:

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); 

        // set internal state so DrawerLayout knows it closed
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        lp.onScreen = 0.f;
        lp.knownOpen = false;

        invalidate();
    }

    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); 

        // set internal state so DrawerLayout knows it closed
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        lp.onScreen = 0.f;
        lp.knownOpen = false;

        invalidate();
    }
}

Используйте 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;
    }
}

Ответ 4

Используя реализацию ответа, предоставленного @James Cross, но анимация, чтобы закрыть ящик, была нежелательной и нефиксируемой без особых проблем, пример.

@Override
public void onResume()
{
    super.onResume();
    mDrawerLayout.closeDrawers();
}

Обход - это перезапуск активности при нажатии кнопки возврата устройства. Для меня это не кажется идеальным, но оно работает. Переопределение onBackPressed(), как это было предложено @mt0s и @Qazi Ahmed, и передать дополнительную информацию для определения вызывающей активности:

    mDrawerList.setOnItemClickListener(new ListView.OnItemClickListener()
    {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id)
        {
            switch(position)
            {
                case 0:
                {
                    Intent intent = new Intent(MainActivity.this, NextActivity.class);
                    //pass int extra to determine calling activity
                    intent.putExtra(EXTRA_CALLING_ACTIVITY, CallingActivityInterface.MAIN_ACTIVITY);
                    startActivity(intent);
                }
            }
        }
    });

В NextActivity.class проверьте активность вызова:

@Override
public void onBackPressed()
{
    int callingActivity = getIntent().getIntExtra(EXTRA_CALLING_ACTIVITY, CallingActivityInterface.MAIN_ACTIVITY);
    switch(callingActivity)
    {
        case CallingActivityInterface.MAIN_ACTIVITY:
        {
            Intent intent = new Intent(this, MainActivity.class);
            startActivity(intent);
            finish();
        }
        ...
    }
}

Таким образом, ящик закрывается без анимации, когда я возвращаюсь к MainActivity, независимо от того, использую ли я кнопку "вверх" или кнопку "Назад". Вероятно, есть лучшие способы сделать это. Мое приложение относительно просто в данный момент, и это работает, но я жду более эффективного метода, если у кого-то есть.

Ответ 5

Вот альтернативное решение вашей проблемы.

@Override    
public void onBackPressed(){    
    if(drawerLayout.isDrawerOpen(navigationView)){    
        drawerLayout.closeDrawer(navigationView);    
    }else {    
        finish();    
    }    
}    

Ответ 6

Вероятно, вы захотите убедиться, что навигационная ничья всегда закрыта, когда активность открывается. Используйте это для этого:

@Override
public void onResume(){
    mDrawerList.closeDrawer(Gravity.LEFT);
}

Ответ 7

Почему хлопот? Просто закройте ящик при щелчке по элементу ящика. Как это делается в официальном приложении Google Play.

private class DrawerItemClickListener implements ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         drawerLayout.closeDrawer(GravityCompat.START, false);
         selectItem(position); 
    }
}

Ответ 8

простой образец:

Drawer resultDrawer;

public void onBackPressed(){
    if (this.resultDrawer.isDrawerOpen()) {    
        this.resultDrawer.closeDrawer();    
    } else {    
        super.onBackPressed();    
    }
}