프래그먼트를 사용할 때 Android Navigation Drawer 이미지와 Up caret 간 전환


177

Navigation Drawer를 사용할 때 Android 개발자는 ActionBar에서 "Navigation Drawer에 표시되는 화면에만 실제로 Navigation Drawer 이미지가 있어야하며"다른 모든 화면에는 전통적인 업 캐럿이 있어야합니다.

자세한 내용은 여기를 참조하십시오 : http://youtu.be/F5COhlbpIbY

하나의 활동을 사용하여 여러 레벨의 조각을 제어하고 탐색 드로어 이미지를 모든 레벨에서 표시하고 작동시킬 수 있습니다.

낮은 수준의 조각을 만들 때 ActionBarDrawerToggle setDrawerIndicatorEnabled(false)탐색 창 이미지를 숨기고 위로 캐럿을 표시하도록 호출 할 수 있습니다

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, 
lowFrag, "lowerFrag").addToBackStack(null).commit();

내가 겪고있는 문제는 원래 탐색 서랍 이미지 대신 여전히 업 캐럿이 표시하는 최상위 조각으로 돌아갈 때입니다. Navigation Drawer 이미지를 다시 표시하기 위해 최상위 조각에서 ActionBar를 "새로 고침"하는 방법에 대한 제안 사항이 있습니까?


해결책

톰의 제안이 나를 위해 일했습니다. 내가 한 일은 다음과 같습니다.

주요 활동

이 활동은 앱의 모든 조각을 제어합니다.

다른 조각을 대체하기 위해 새 조각을 준비 할 때 DrawerToggle을 다음 setDrawerIndicatorEnabled(false)과 같이 설정 했습니다.

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout,   
lowFrag).addToBackStack(null).commit();

다음으로의 재정의 onBackPressed에서 DrawerToggle을 다음 setDrawerIndicatorEnabled(true)과 같이 설정하여 위의 내용을 되돌 렸습니다 .

@Override
public void onBackPressed() {
    super.onBackPressed();
    // turn on the Navigation Drawer image; 
    // this is called in the LowerLevelFragments
    setDrawerIndicatorEnabled(true)
}

LowerLevelFragments에서

내가 수정 한 조각에서 다음 onCreateonOptionsItemSelected같이 :

에서는 onCreate추가 setHasOptionsMenu(true)옵션 메뉴를 구성 할 수 있도록. 또한 작업 표시 줄 setDisplayHomeAsUpEnabled(true)에서 < 을 활성화하도록 설정하십시오 .

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // needed to indicate that the fragment would 
    // like to add items to the Options Menu        
    setHasOptionsMenu(true);    
    // update the actionbar to show the up carat/affordance 
    getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}

그런 다음 < 을 누를 onOptionsItemSelected때마다 액티비티에서를 호출 하여 계층 구조에서 한 수준 위로 이동하고 탐색 창 이미지를 표시합니다.onBackPressed()

@Override
public boolean onOptionsItemSelected(MenuItem item) {   
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            getActivity().onBackPressed();
            return true;
         
    }

2
또한 onBackPressed () 메소드에서 getFragmentManager (). getBackStackEntryCount () 메소드를 사용하여 백 스택에있는 항목 수를 확인하고 결과가 0 인 경우에만 서랍 표시기를 활성화 할 수 있습니다.이 경우 각 LowerLevelFragments에서 homeAsUpIndicator를 활성화 할 필요가 없습니다.
pvshnik

2
이것은 매우 유용합니다! 게시물의 "솔루션"부분을 이동하여 실제 "답변"으로 만들어야합니다. 당신은 upvotes에 대한 더 많은 포인트를 얻을 수 있습니다 그리고 그것은 이다 결국 답
올렉시

여기에서 조각을 대체하는 이유는 다음과 같습니다 .replace(R.id.frag_layout.. 이것이 하나 이상의 계층 구조 수준이라면 .add백 스택에 기대할 것입니다 .
JJD

5
Bro, theDrawerToggle.setDrawerIndicatorEnabled(false);조각 내부를 어떻게 참조 합니까? 메인 활동 클래스 파일에 선언되어 있다고 생각합니다. 이것을 참조하는 방법을 찾을 수 없습니다. 힌트가 있습니까?
Skynet

1
툴바를 사용할 때 그동안 홈을 사용하지 않도록 표시 옵션을 전환해야했습니다. 그렇지 않으면 (내 android.support.v7.internal.widget 패키지의) setDisplayOptions()메소드 ToolbarWidgetWrapper가 동일한 조각을 두 번 입력 할 때 아이콘을 다시 만들지 않습니다. 다른 사람들 도이 문제에 걸려 넘어 질 때 여기에 남겨 두십시오.
Wolfram Rittmeyer

답변:


29

하위 레벨 단편을 구현하기 위해 새 활동에서 하위 레벨 단편을 구현하는 것과는 반대로 기존 단편을 대체하고 있습니다.

그런 다음 뒤로 기능을 수동으로 구현해야한다고 생각합니다. 사용자가 뒤로 밀면 스택을 팝업하는 코드가 있습니다 (예 : Activity::onBackPressed재정의). 그래서, 당신이 그것을하는 곳마다, 당신은를 바꿀 수 있습니다 setDrawerIndicatorEnabled.


고마워 Tom, 효과가 있었다! 내가 사용한 솔루션으로 원본 게시물을 업데이트했습니다.
EvilAsh 2016 년

6
하위 조각에서 TheDrawerToggle을 참조하는 방법은 무엇입니까? 그것은 주요 활동에서 정의되었으며, 이것에 대해 내 머리를 잡을 수 없습니다!
Skynet

1
조각에서 활동을 얻을 수 있습니다. 따라서 메인 액티비티에 게터를 추가하여 서랍 토글에 액세스하십시오.
Raphael Royer-Rivard

83

1-2-3처럼 쉽습니다.

달성하려는 경우 :

1) 드로어 표시기 -백 스택에 조각이 없거나 드로어가 열린 경우

2) 화살표 -일부 조각이 백 스택에있을 때

private FragmentManager.OnBackStackChangedListener
        mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        syncActionBarArrowState();
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    getSupportActionBar().setDisplayShowHomeEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    mDrawerToggle = new ActionBarDrawerToggle(
            this,             
            mDrawerLayout,  
            R.drawable.ic_navigation_drawer, 
            0, 
            0  
    ) {

        public void onDrawerClosed(View view) {
            syncActionBarArrowState();
        }

        public void onDrawerOpened(View drawerView) {
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    };

    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}

@Override
protected void onDestroy() {
    getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
    super.onDestroy();
}

private void syncActionBarArrowState() {
    int backStackEntryCount = 
        getSupportFragmentManager().getBackStackEntryCount();
    mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

3) 모양에 따라 작동하는 두 지표

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (mDrawerToggle.isDrawerIndicatorEnabled() && 
        mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    } else if (item.getItemId() == android.R.id.home && 
               getSupportFragmentManager().popBackStackImmediate()) {
        return true;
    } else {
        return super.onOptionsItemSelected(item);
    }
}

PS 3 줄 표시기 동작에 대한 다른 팁은 Android 개발자 에서 탐색 창 만들기를 참조하십시오 .


3
나는 너와 비슷한 것을 끝내었다. 이 솔루션 (BackStackChangedListener를 사용하여 표시된 내용 사이를 전환)이 가장 우아하다고 생각합니다. 그러나 두 가지 변경 사항이 있습니다 .1) onDrawerClosed / onDrawerOpened 호출에서 서랍 표시기를 호출 / 변경하지 않습니다-필요하지 않습니다 .2) Fragments가 AbstractFragment를 모두 상속하여 Up 탐색을 처리하도록합니다. onOptionsItemSelected (..)를 구현하며 항상 setHasOptionsMenu (true)를 호출합니다.
Espen Riskedal

2
화살표 모드에서 탐색 드로어의 스 와이프 제스처를 잠그어야합니다. mDrawerLayout.setDrawerLockMode (DrawerLayout.LOCK_MODE_LOCKED_CLOSED); 서랍 표시기 모드에서 다시 활성화
artkoenig

5
내 NavigationDrawer 조각 에서이 모든 작업을 마쳤습니다. 고마워요 매력처럼 작동합니다.
frostymarvelous 2016 년

1
setActionBarArrowDependingOnFragmentsBackStack()...이 얼마나 긴 이름 : P
S.Thiongane

2
조각 A에서 B로 이동하고 A를 백 스택에 추가 할 때 위 버튼이 표시되지 않습니다. :(
aandis

14

나는 다음을 사용했다 :

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if(getSupportFragmentManager().getBackStackEntryCount() > 0){
                    mDrawerToggle.setDrawerIndicatorEnabled(false);
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
                }
                else {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    mDrawerToggle.setDrawerIndicatorEnabled(true);
                }
            }
        });

1
정말 감사합니다 이것은 실제로 작동 한 유일한 것입니다. 이것을 추가 drawerToggle.setToolbarNavigationClickListener(하면 화살표를 클릭 할 때이 리스너가 호출됩니다
EpicPandaForce

@Yuriy에게 감사드립니다. 이것은 나의 문제를 해결하는 데 도움이되었습니다
Muhammad Shoaib Murtaza

12

업 액션 바 버튼이 작동하지 않으면 리스너를 추가하는 것을 잊지 마십시오.

// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
});

홈 버튼으로 서랍 탐색을 구현하는 데 문제가 있습니다. 작업 버튼을 제외한 모든 것이 작동했습니다.


10

DrawerToggle의 상태에 따라 MainActivity에서 홈 항목 선택을 처리하십시오. 이렇게하면 모든 조각에 동일한 코드를 추가 할 필요가 없습니다.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            onBackPressed();
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

깔끔한 솔루션 +1 답변을 완전히 사용하려면 백 스택에 확인 표시를 추가해야합니다. 비어 있으면 드로어 표시기를 자동으로 true로 설정하십시오.
HpTerm

@HpTerm 나는 onBackPressed()두 가지에 대해 동일한 동작을 원했기 때문에 백 스택을 처리합니다 .
dzeikei

6

팔로우

@dzeikei가 제공 한 솔루션은 깔끔하지만 조각을 사용할 때 확장되어 백 스택이 비어있을 때 서랍 표시기 설정을 자동으로 처리 할 수 ​​있습니다.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            // Use getSupportFragmentManager() to support older devices
            FragmentManager fragmentManager = getFragmentManager();
            fragmentManager.popBackStack();
            // Make sure transactions are finished before reading backstack count
            fragmentManager.executePendingTransactions();
            if (fragmentManager.getBackStackEntryCount() < 1){
                mDrawerToggle.setDrawerIndicatorEnabled(true);  
            }
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

편집하다

@JJD의 질문에.

조각은 활동에서 유지 / 관리됩니다. 위의 코드는 해당 활동에서 한 번 작성되었지만의 업 캐럿 만 처리합니다 onOptionsItemSelected.

내 앱 중 하나에서 뒤로 버튼을 눌렀을 때 업 캐럿의 동작을 처리해야했습니다. 이를 재정 의하여 처리 할 수 ​​있습니다 onBackPressed.

@Override
public void onBackPressed() {
    // Use getSupportFragmentManager() to support older devices
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.executePendingTransactions();
    if (fragmentManager.getBackStackEntryCount() < 1){
        super.onBackPressed();
    } else {
        fragmentManager.executePendingTransactions();
        fragmentManager.popBackStack();
        fragmentManager.executePendingTransactions();
        if (fragmentManager.getBackStackEntryCount() < 1){
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    }
};

사이의 코드 중복을 참고 onOptionsItemSelected하고 onBackPressed있는이 방법을 생성하고 두 곳에서 해당 메소드를 호출하여 피할 수 있습니다.

또한 executePendingTransactions필자의 경우 필요하거나 때로는 업 캐럿의 이상한 행동을했던 두 번 더 추가 합니다.


Up 캐럿 동작 을 구현하기 위해 추가해야하는 전체 코드 입니까? 아니면 다른 게시물 중 하나를 참조하고 있습니까? 이것을 명확히하십시오.
JJD

편집 해 주셔서 감사합니다. 실제로 나는 수업 mDrawerToggle내에서 유지 합니다 NavigationDrawerFragment. 작동하려면 홈 버튼 / 표시기의 상태를 전환해야합니다 NavigationDrawerFragment#toggleDrawerIndicator. 또한 초기 체크인이 필요한지 확실하지 않습니다 onOptionsItemSelected. 주석 처리를 제거했습니다. - 단순화 된 예
JJD

수정 된 구현 : 초기 체크인에 대한 귀하의 권리입니다 onOptionsItemSelected. 이렇게하면 탐색 드로어가 여전히 최상위 계층 구조로 열립니다. 그러나 코드를로 이동했습니다 NavigationDrawerFragment#onOptionsItemSelected. 이 날은 노출하지 않는 데 도움 mDrawerToggle받는 MainActivity.
JJD

@ JJD 나는 계층의 각 레벨에 대해 위로 캐럿을 위해 모든 것이 작동하는 데 약간의 어려움을 겪은 것을 기억했습니다. 그것이 당신을 위해 일하는 한 괜찮습니다. 물론, 말했듯이 코드를 노출시키지 않는 다른 위치로 코드를 이동할 수 있습니다.
HpTerm

2

햄버거 메뉴의보기 상태를 업데이트하기 위해 호스팅 활동에 대한 인터페이스를 만들었습니다. 최상위 조각의 경우 토글을 설정 true하고 위쪽 <화살표를 표시하려는 조각의 토글을로 설정했습니다 false.

public class SomeFragment extends Fragment {

    public interface OnFragmentInteractionListener {
        public void showDrawerToggle(boolean showDrawerToggle);
    }

    private OnFragmentInteractionListener mListener;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            this.mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        mListener.showDrawerToggle(false);
    }
}

그런 다음 내 활동에서 ...

public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {

    private ActionBarDrawerToggle mDrawerToggle;

    public void showDrawerToggle(boolean showDrawerIndicator) {
        mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
    }

}

2

답변 은 효과가 있었지만 약간의 문제가있었습니다. 은 getSupportActionBar().setDisplayHomeAsUpEnabled(false)명시 적으로 호출되지 않았으며 백 스택에 항목이 없어도 서랍 아이콘이 숨겨져 setActionBarArrowDependingOnFragmentsBackStack()메소드 변경이 효과적 이었습니다.

private void setActionBarArrowDependingOnFragmentsBackStack() {
        int backStackEntryCount = getSupportFragmentManager()
                .getBackStackEntryCount();
        // If there are no items in the back stack
        if (backStackEntryCount == 0) {
            // Please make sure that UP CARAT is Hidden otherwise Drawer icon
            // wont display
            getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            // Display the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        } else {
            // Show the Up carat
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            // Hide the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(false);
        }

    }

필자의 경우 rigth 솔루션은 actionBarDrawerToggle.setDrawerIndicatorEnabled (getSupportFragmentManager (). getBackStackEntryCount () <0) 만 사용했습니다.
Gorets

1

논리가 분명하다. 프래그먼트 백 스택이 지워지면 뒤로 버튼을 표시하십시오. 프래그먼트 스택이 명확하지 않은 경우 머티리얼 햄버거 백 애니메이션을 표시합니다.

getSupportFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            syncActionBarArrowState();
        }
    }
);


private void syncActionBarArrowState() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

//add these in Your NavigationDrawer fragment class

public void setDrawerIndicatorEnabled(boolean flag){
    ActionBar actionBar = getActionBar();
    if (!flag) {
        mDrawerToggle.setDrawerIndicatorEnabled(false);
        actionBar.setDisplayHomeAsUpEnabled(true);
        mDrawerToggle.setHomeAsUpIndicator(getColoredArrow());
    } else {
        mDrawerToggle.setDrawerIndicatorEnabled(true);
    }
    mDrawerToggle.syncState();
    getActivity().supportInvalidateOptionsMenu();
}

//download back button from this(https://www.google.com/design/icons/) website and add to your project

private Drawable getColoredArrow() {
    Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp);
    Drawable wrapped = DrawableCompat.wrap(arrowDrawable);

    if (arrowDrawable != null && wrapped != null) {
        // This should avoid tinting all the arrows
        arrowDrawable.mutate();
        DrawableCompat.setTint(wrapped, Color.GRAY);
    }
    return wrapped;
}

1

GMAIL 앱을보고 여기로 와서 캐럿 / 충격 아이콘을 검색하십시오 ..

나는 당신에게 이것을하도록 요청할 것입니다. 위의 대답 중 어느 것도 분명하지 않았습니다. 수락 된 답변을 수정할 수있었습니다.

  • NavigationDrawer-> Listview에는 하위 조각이 포함되어 있습니다.


  • 하위 조각은 다음과 같이 나열됩니다

  • firstFragment == 위치 0 ---> 하위 조각이 있습니다-> 조각

  • secondFragment
  • thirdFragment 등 ....

firstFragment에는 다른 조각이 있습니다.

DrawerActivity에서 이것을 호출하십시오.

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager().getBackStackEntryCount() > 0) {
                mDrawerToggle.setDrawerIndicatorEnabled(false);
            } else {
                mDrawerToggle.setDrawerIndicatorEnabled(true);
            }
        }
    });

그리고 단편

    setHasOptionsMenu(true);    

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            activity.onBackPressed();
            return true;
    }
    return false;
}

OnBackPressed Drawer 활동 방법에서 탐색 목록 아이콘을 다시 사용하려면 드로어 토글을 true로 설정하십시오.

고마워, Pusp



0

riwnodennyk 또는 Tom의 솔루션에서 onNavigateUp () ( 여기에 표시된)을 사용하는 IMO 는 더 깨끗하고 더 잘 작동하는 것 같습니다. onOptionsItemSelected 코드를 다음과 같이 바꾸십시오.

@Override
public boolean onSupportNavigateUp() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        // handle up navigation
        return true;
    } else {
        return super.onSupportNavigateUp();
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.