Chuyển đổi giữa hình ảnh ngăn kéo điều hướng Android và dấu mũ khi sử dụng các đoạn


177

Khi sử dụng Ngăn kéo Điều hướng, các nhà phát triển Android khuyến cáo rằng trong ActionBar "chỉ những màn hình được thể hiện trong Ngăn kéo Điều hướng mới thực sự có hình ảnh Ngăn kéo Điều hướng" và "tất cả các màn hình khác đều có carat truyền thống."

Xem tại đây để biết chi tiết: http://youtu.be/F5COhlbpIbY

Tôi đang sử dụng một hoạt động để kiểm soát nhiều cấp độ mảnh vỡ và có thể khiến hình ảnh Ngăn kéo Điều hướng hiển thị và hoạt động ở tất cả các cấp độ.

Khi tạo các đoạn ở mức thấp hơn, tôi có thể gọi ActionBarDrawerToggle setDrawerIndicatorEnabled(false)để ẩn hình ảnh Ngăn điều hướng và hiển thị dấu mũ lên

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

Vấn đề tôi gặp phải là khi tôi điều hướng trở lại các đoạn cấp cao nhất, Up carat vẫn hiển thị thay vì hình ảnh Ngăn kéo Điều hướng ban đầu. Bạn có đề xuất nào về cách "làm mới" ActionBar trên các đoạn cấp cao nhất để hiển thị lại hình ảnh Ngăn điều hướng không?


Giải pháp

Đề nghị của Tom làm việc cho tôi. Đây là những gì tôi đã làm:

Hoạt động chủ yêu

Hoạt động này kiểm soát tất cả các mảnh trong ứng dụng.

Khi chuẩn bị các đoạn mới để thay thế các đoạn khác, tôi đặt Ngăn kéo setDrawerIndicatorEnabled(false)như thế này:

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

Tiếp theo, trong phần ghi đè onBackPressed, tôi đã hoàn nguyên phần trên bằng cách đặt ngăn kéo cho phép setDrawerIndicatorEnabled(true)như thế này:

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

Trong phần LowerLevelFragments

Trong các đoạn tôi đã sửa đổi onCreateonOptionsItemSelectednhư thế này:

Trong onCreatebổ sung setHasOptionsMenu(true)để cho phép cấu hình trình đơn tùy chọn. Cũng được đặt setDisplayHomeAsUpEnabled(true)để bật < trong thanh hành động:

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

Sau đó, onOptionsItemSelectedbất cứ khi nào < được nhấn, nó sẽ gọi onBackPressed()từ hoạt động để di chuyển lên một cấp trong cấu trúc phân cấp và hiển thị Hình ảnh ngăn kéo điều hướng:

@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
Ngoài ra, trong phương thức onBackPression () của bạn, bạn có thể kiểm tra có bao nhiêu mục trong ngăn xếp trở lại với phương thức getFragmentManager (). GetBackStackEntryCount () và chỉ bật chỉ báo ngăn kéo nếu kết quả là 0. Trong trường hợp đó, không cần thiết phải bật homeAsUpTheicator trong mỗi LowerLevelFragments.
pvshnik

2
Điều này rất hữu ích! Bạn nên di chuyển phần "giải pháp" của bài đăng của mình và biến nó thành một "câu trả lời" thực sự. Bạn sẽ nhận được nhiều điểm hơn cho upvote và đó câu trả lời sau tất cả
Oleksiy

Tại sao bạn thay thế đoạn ở đây : .replace(R.id.frag_layout. Nếu đây là một cấp bậc phân cấp nữa, tôi sẽ mong bạn đưa .addnó vào backstack.
JJD

5
Bro, làm thế nào để bạn tham khảo theDrawerToggle.setDrawerIndicatorEnabled(false);các mảnh bên trong? Tôi nghĩ rằng nó được khai báo trong tệp lớp Hoạt động chính. Tôi không thể tìm thấy một cách để tham khảo này. Có gợi ý nào không?
Skynet

1
Khi sử dụng thanh công cụ, tôi phải chuyển các tùy chọn hiển thị sang không sử dụng nhà trong lúc này. Mặt khác, setDisplayOptions()phương thức trong ToolbarWidgetWrapper(của gói android.support.v7.iternal.widget nội bộ) sẽ không tạo lại biểu tượng khi nhập cùng một đoạn lần thứ hai. Chỉ để lại ở đây khi người khác cũng vấp phải vấn đề này.
Wolfram Rittmeyer 3/03/2015

Câu trả lời:


29

Bạn đã viết rằng, để thực hiện các phân đoạn cấp thấp hơn, bạn đang thay thế các phân đoạn hiện có, trái ngược với việc thực hiện phân đoạn cấp thấp hơn trong một hoạt động mới.

Tôi nghĩ rằng sau đó bạn sẽ phải thực hiện chức năng quay lại theo cách thủ công: khi người dùng nhấn lại, bạn có mã bật ra ngăn xếp (ví dụ như Activity::onBackPressedghi đè). Vì vậy, bất cứ nơi nào bạn làm điều đó, bạn có thể đảo ngược setDrawerIndicatorEnabled.


Cảm ơn Tom, đã làm việc! Tôi đã cập nhật bài viết gốc với giải pháp tôi đã sử dụng.
EvilAsh

6
Làm cách nào để tham chiếu theDrawerToggle trong một đoạn cấp thấp hơn? Nó đã được xác định trong hoạt động chính, không thể hiểu được điều này!
Skynet

1
Bạn có thể có được các hoạt động từ các mảnh. Vì vậy, chỉ cần thêm một getter trong hoạt động chính của bạn để truy cập vào ngăn kéo chuyển đổi.
Raphael Royer-Rivard

83

Thật dễ dàng như 1-2-3.

Nếu bạn muốn đạt được:

1) Chỉ báo ngăn kéo - khi không có mảnh vỡ nào trong Ngăn xếp phía sau hoặc Ngăn kéo được mở

2) Mũi tên - khi một số Mảnh vỡ nằm trong Ngăn xếp Quay lại

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) Cả hai chỉ số để hành động theo hình dạng của họ

@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 Xem Tạo ngăn kéo điều hướng trên Nhà phát triển Android về các mẹo khác về hành vi chỉ báo 3 dòng.


3
Tôi đã kết thúc với một cái gì đó tương tự như của bạn. Tôi nghĩ giải pháp này (sử dụng BackStackChangedListener để lật giữa những gì được hiển thị) là thanh lịch nhất. Tuy nhiên, tôi có hai thay đổi: 1) Tôi không gọi / thay đổi chỉ báo ngăn kéo trong các cuộc gọi onDrawerCloses / onDrawerOpened - không cần thiết và 2) Tôi để các mảnh vỡ tự xử lý điều hướng Up bằng cách tạo một AbstractFragment mà tất cả chúng đều thừa hưởng thực hiện onOptionsItemSelected (..) và luôn gọi setHasOptionsMothy (true);
Espen Riskedal

2
Yout cũng nên khóa cử chỉ vuốt cho ngăn kéo điều hướng ở chế độ mũi tên: mDrawerLayout.setDrawerLockMode (DrawerLayout.LOCK_MODE_LOCKED_CLposed); và kích hoạt lại trong chế độ chỉ báo ngăn kéo
artkoenig

5
Tôi cuối cùng đã làm tất cả điều này trong đoạn NavigationDrawer của tôi. Hoạt động như một btw quyến rũ, cảm ơn.
sương giá

1
setActionBarArrowDependingOnFragmentsBackStack()... thật là một cái tên dài: P
S.Thiongane 22/12/14

2
Điều này không hiển thị nút lên cho tôi khi tôi đi từ đoạn A đến B và thêm A vào backstack. :(
aandis

14

Tôi đã sử dụng điều tiếp theo:

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
CẢM ƠN BẠN RẤT NHIỀU đây là người duy nhất thực sự làm việc. Khi tôi thêm phần này, drawerToggle.setToolbarNavigationClickListener(trình nghe này được gọi khi nhấp vào mũi tên
EpicPandaForce

Cảm ơn @Yuriy, điều này đã giúp tôi giải quyết vấn đề của mình
Muhammad Shoaib Murtaza

12

Nếu nút thanh hành động lên của bạn không hoạt động, đừng quên thêm người nghe:

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

Tôi đã gặp một số khó khăn khi thực hiện điều hướng ngăn kéo bằng nút home, mọi thứ đều hoạt động trừ buton hành động.


10

Hãy thử xử lý lựa chọn mục Home trong MainActivity tùy thuộc vào trạng thái của DrawerToggle. Bằng cách này, bạn không phải thêm cùng một mã vào mỗi phân đoạn.

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

Giải pháp gọn gàng +1. Để câu trả lời của bạn hoàn toàn có thể sử dụng được, bạn nên thêm một kiểm tra vào backstack. Khi nó trống thì tự động đặt chỉ báo ngăn kéo thành true.
HpTerm

@HpTerm Tôi xử lý backstack onBackPressed()vì tôi muốn hành vi tương tự cho cả hai.
dzeikei

6

THEO SÁT

Giải pháp được đưa ra bởi @dzeikei rất gọn gàng, nhưng nó có thể được mở rộng, khi sử dụng các đoạn, để tự động xử lý cài đặt lại chỉ báo ngăn kéo khi backstack trống.

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

BIÊN TẬP

Đối với câu hỏi của @JJD.

Các mảnh được tổ chức / quản lý trong một hoạt động. Đoạn mã trên được viết một lần trong hoạt động đó, nhưng chỉ xử lý dấu mũ cho onOptionsItemSelected.

Trong một trong những ứng dụng của mình, tôi cũng cần xử lý hành vi của dấu mũ khi nhấn nút quay lại. Điều này có thể được xử lý bằng cách ghi đè 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);
        }
    }
};

Lưu ý sự trùng lặp mã giữa onOptionsItemSelectedonBackPressedcó thể tránh được bằng cách tạo một phương thức và gọi phương thức đó ở cả hai nơi.

Cũng lưu ý rằng tôi thêm hai lần nữa executePendingTransactionstrong trường hợp của tôi là bắt buộc nếu không tôi đôi khi có những hành vi kỳ lạ của sự chăm sóc.


Đây có phải là mã đầy đủ tôi cần thêm để thực hiện hành vi Up caret hay bạn đang tham khảo một trong những bài đăng khác? Hãy làm rõ điều này.
JJD

Cảm ơn đã chỉnh sửa. Thật ra tôi duy trì mDrawerToggletrong một NavigationDrawerFragmentlớp học. Để làm cho nó hoạt động, tôi cũng cần phải chuyển trạng thái của nút home / chỉ báo - xem : NavigationDrawerFragment#toggleDrawerIndicator. Hơn nữa, tôi không chắc bạn cần kiểm tra ban đầu onOptionsItemSelected: Tôi không ghi chú nó. - Ví dụ đơn giản
JJD

Sửa đổi thực hiện : Bạn đã đúng về việc kiểm tra ban đầu onOptionsItemSelected. Điều này đảm bảo rằng Ngăn kéo Điều hướng vẫn mở trong hệ thống phân cấp cấp cao nhất. Tuy nhiên, tôi đã chuyển mã vào NavigationDrawerFragment#onOptionsItemSelected. Điều này giúp tôi không tiếp xúc mDrawerTogglevới MainActivity.
JJD

@JJD Tôi nhớ đã đấu tranh một chút để mọi thứ hoạt động cho sự chăm sóc lên cho từng cấp của hệ thống phân cấp. Miễn là nó hoạt động với bạn cũng vậy. Tất nhiên, như bạn nói, bạn có thể di chuyển mã ở nơi khác để không lộ nó.
HpTerm

2

Tôi đã tạo một giao diện cho hoạt động lưu trữ để cập nhật trạng thái xem của menu hamburger. Đối với các phân đoạn cấp cao nhất, tôi đặt chuyển đổi thành truevà cho các phân đoạn mà tôi muốn hiển thị lên <mũi tên tôi đặt chuyển đổi thành 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);
    }
}

Sau đó, trong Hoạt động của tôi ...

public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {

    private ActionBarDrawerToggle mDrawerToggle;

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

}

2

Đây câu trả lời đã được làm việc nhưng có một vấn đề nhỏ với nó. Điều getSupportActionBar().setDisplayHomeAsUpEnabled(false)này không được gọi một cách rõ ràng và nó đã khiến biểu tượng ngăn kéo bị ẩn ngay cả khi không có vật phẩm nào ở phía sau để thay đổi setActionBarArrowDependingOnFragmentsBackStack()phương thức làm việc cho tôi.

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

    }

trong trường hợp của tôi, giải pháp cứng nhắc chỉ sử dụng actionBarDrawerToggle.setDrawerTheicatorEnables (getSupportFragmentManager (). getBackStackEntryCount () <0);
Gorets 7/12/2016

1

Logic là rõ ràng. Hiển thị nút quay lại nếu phân mảnh trở lại ngăn xếp rõ ràng. Hiển thị hình ảnh động vật liệu hamburger trở lại nếu ngăn xếp mảnh không rõ ràng.

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

Nếu bạn xem ứng dụng GMAIL và đến đây để tìm kiếm biểu tượng carret / khả năng chi trả ..

Tôi sẽ yêu cầu bạn làm điều này, không có câu trả lời nào ở trên là rõ ràng. tôi đã có thể sửa đổi câu trả lời được chấp nhận.

  • NavigationDrawer -> Listview chứa các tập tin con.


  • subfragments sẽ được liệt kê như thế này

  • FirstFragment == vị trí 0 ---> cái này sẽ có các phần con -> đoạn

  • thứ hai
  • thirdFragment và vv ....

Trong FirstFragment bạn có đoạn khác.

Gọi cái này trên DrawerActivity

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

và trong mảnh

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

Trên phương thức hoạt động Ngăn kéo OnBackPression, đặt ngăn kéo chuyển thành đúng để bật lại biểu tượng danh sách điều hướng.

Cảm ơn, Pusp



0

IMO, sử dụng onNavigateUp () (như được hiển thị ở đây ) trong giải pháp của riwnodennyk hoặc Tom sạch hơn và dường như hoạt động tốt hơn. Chỉ cần thay thế mã onOptionsItemSelected bằng mã này:

@Override
public boolean onSupportNavigateUp() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        // handle up navigation
        return true;
    } else {
        return super.onSupportNavigateUp();
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.