Cách xóa ngăn xếp điều hướng sau khi điều hướng đến một phân đoạn khác trong Android


121

Tôi đang sử dụng Thành phần kiến ​​trúc điều hướng mới trong android và tôi gặp khó khăn trong việc xóa ngăn xếp điều hướng sau khi chuyển sang một phân đoạn mới.

Ví dụ: Tôi đang ở trong loginFragment và tôi muốn phân đoạn này bị xóa khỏi ngăn xếp khi tôi điều hướng đến phân đoạn chính để người dùng sẽ không bị quay lại loginFragment khi anh ta nhấn nút quay lại.

Tôi đang sử dụng NavHostFragment.findNavController (Fragment) .navigate (R.id.homeFragment) đơn giản để điều hướng.

Mã hiện tại:

mAuth.signInWithCredential(credential)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment);
                    } else {
                        Log.w(TAG, "signInWithCredential:failure", task.getException());
                    }
                }
            });

Tôi đã thử sử dụng NavOptions trong điều hướng () , nhưng nút quay lại vẫn đưa tôi trở lại loginFragment

NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder.setPopUpTo(R.id.homeFragment, false).build();   
             NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment, null, navOptions);

Bạn có thể sử dụng popBackStackhoặc không thêm LoginFragmentđể backstack cung cấp nullđến addToBackStack(null);và thay thế bằng mớiFragment
Yupi

Vui lòng chỉnh sửa bài đăng của bạn và thêm mã bạn hiện đang sử dụng để điều hướng
Barns

Tôi đã chỉnh sửa câu hỏi của mình và thêm mã của mình
Youssef El Behi

Tôi nghĩ @Yupi đã cung cấp một gợi ý tốt. Hoặc bạn có thể sử dụng navigate()phương pháp như navigate(int resId, Bundle args, NavOptions navOptions)và cung cấp loại NavOptionsphù hợp nhất với senario của bạn
Barns

Tôi đã thử sử dụng The NavOptions nhưng nút quay lại vẫn đưa tôi quay lại loginFragment
Youssef El Behi

Câu trả lời:


179

Đầu tiên, hãy thêm thuộc tính app:popUpTo='your_nav_graph_id'app:popUpToInclusive="true"vào thẻ hành động.

<fragment
    android:id="@+id/signInFragment"
    android:name="com.glee.incog2.android.fragment.SignInFragment"
    android:label="fragment_sign_in"
    tools:layout="@layout/fragment_sign_in" >
    <action
        android:id="@+id/action_signInFragment_to_usersFragment"
        app:destination="@id/usersFragment"
        app:launchSingleTop="true"
        app:popUpTo="@+id/main_nav_graph"
        app:popUpToInclusive="true" />
</fragment>

Thứ hai, điều hướng đến đích, sử dụng hành động trên làm tham số.

findNavController(fragment).navigate(
     SignInFragmentDirections.actionSignInFragmentToUserNameFragment())

Xem tài liệu để biết thêm thông tin.

LƯU Ý : Nếu bạn điều hướng bằng phương pháp navigate(@IdRes int resId), bạn sẽ không nhận được kết quả mong muốn. Do đó, tôi đã sử dụng phương pháp navigate(@NonNull NavDirections directions).


1
Câu trả lời hay nhất là clearTask hiện không được dùng nữa trong các phiên bản mới của Thành phần điều hướng Android!
Michał Ziobro

14
Người ta cũng có thể sử dụng id của hành động như vậyfindNavController(fragment).navigate(R.id.action_signInFragment_to_usersFragment)
Calin

1
Tuyệt vời! Tuy nhiên, bây giờ tôi có back / up glyph, nhưng nhấn nó không làm gì cả. Cách sạch sẽ để loại bỏ điều đó quá là gì?
Khan tiếng Đan Mạch

6
Tôi nghĩ rằng chìa khóa cho câu trả lời này nằm ở chỗ "popUpTo" (xóa tất cả các đoạn giữa đoạn hiện tại của bạn và lần xuất hiện đầu tiên của id mà bạn chuyển đến đối số này) được đặt thành id của chính biểu đồ điều hướng . Có hiệu quả hoàn toàn xóa hoàn toàn backstack. Chỉ thêm nhận xét này vì tôi chưa thấy nó giải thích chính xác như thế này, và đó là những gì tôi đang tìm kiếm. +1!
Scott O'Toole,

7
LƯU Ý : Không phải là lớp "Chỉ đường" sẽ không được tạo nếu bạn không bao gồm lớp gradle safe-args. Để biết thêm thông tin truy cập tại đây
Jaydip Kalkani

28

Trong trường hợp của tôi, tôi cần xóa mọi thứ trong Ngăn xếp phía sau trước khi mở một phân đoạn mới nên tôi đã sử dụng mã này

navController.popBackStack(R.id.fragment_apps, true);
navController.navigate(R.id.fragment_company);

dòng đầu tiên loại bỏ ngăn xếp phía sau cho đến khi nó đạt đến phân đoạn được chỉ định trong trường hợp của tôi, đó là phân đoạn chính vì vậy nó sẽ loại bỏ hoàn toàn tất cả ngăn xếp phía sau và khi người dùng nhấp lại vào mảnh_công ty, anh ta sẽ đóng ứng dụng.


1
Là gì fragment_apps??
IgorGanapolsky

1
Phân mảnh hiện tại. Nó giống như nếu bạn đang ở trêngmentOne và muốn đi tới mảnh vỡ Hai bạn cũng cần sử dụng navController.popBackStack (R.id.fragmentOne, true); navController.navigate (R.id.fragmentTwo);
Bbeni

26

Tôi nghĩ câu hỏi của bạn liên quan cụ thể đến cách sử dụng Ứng dụng Pop Behavior / Pop To /: popUpTo (trong xml)

Trong tài liệu, hãy
bật lên một điểm đến nhất định trước khi điều hướng. Điều này sẽ bật tất cả các điểm đến không phù hợp từ ngăn xếp phía sau cho đến khi tìm thấy điểm đến này.

Ví dụ (Ứng dụng tìm việc đơn giản), biểu đồ start_screen_nav
của tôi như sau:

startScreenFragment (start) -> loginFragment -> EmployerMainFragment

                            -> loginFragment -> JobSeekerMainFragment

nếu tôi muốn điều hướng đến EmployerMainFragmentvà bật tất cả bao gồm startScreenFragment thì mã sẽ là:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"
            app:popUpToInclusive="true" />

nếu tôi muốn điều hướng đến EmployerMainFragmentvà bật tất cả ngoại trừ startScreenFragment thì mã sẽ là:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"/>

nếu tôi muốn điều hướng đến EmployerMainFragmentvà bật lên loginFragmentnhưng không phải startScreenFragment thì mã sẽ là:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/loginFragment"
            app:popUpToInclusive="true"/>

HOẶC LÀ

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"/>

Làm cách nào để thực hiện điều này theo chương trình thay vì trong XML?
IgorGanapolsky

14

LƯU Ý: Tác vụ xóa không được dùng nữa, mô tả chính thức là

Phương pháp này không được dùng nữa. Sử dụng setPopUpTo (int, boolean) với id của đồ thị NavController và đặt bao gồm thành true.

Câu trả lời cũ

Nếu bạn không muốn đi qua tất cả lông tơ mà trong mã, bạn chỉ có thể kiểm tra Clear Tasktrong Launch Optionstrong tính chất của hành động.

Tùy chọn khởi chạy

Chỉnh sửa: Kể từ Android Studio 3.2 Beta 5, Clear Task không còn hiển thị trong cửa sổ Launch Options, nhưng bạn vẫn có thể sử dụng nó trong mã XML của điều hướng, trong thẻ action , bằng cách thêm

app:clearTask="true"

Thay vào đó, bạn nên sử dụng id của gốc của biểu đồ vì thông báo clearTask xml thuộc về sự ghi nhận có nội dung: "đặt popUpTo thành gốc của biểu đồ và sử dụng popUpToInclusive". Tôi đã thực hiện nó như thế này và đạt được những hành vi mong muốn cùng với findNavController () điều hướng (@IdRes resid, bó).
artnest

Sử dụng phương pháp được khuyến nghị để đặt popUpTo và popUpToInclusive dường như không hoạt động đối với các hành động giữa các điểm đến khác với điểm đến bắt đầu: Issuetracker.google.com/issues/116831650
hsson 28/09/18

Tôi thực sự đang gặp phải một vấn đề trong đó nó không hoạt động với điểm đến bắt đầu, thật bực bội.
Mel

Bạn sẽ làm điều này như thế nào nếu bạn không biết đoạn mà bạn muốn bật lên? Ví dụ, tôi có một phân đoạn tạo ra một mục mới. Sau khi tạo, tôi muốn hiển thị chế độ xem chi tiết của mục này. Nhưng tôi không muốn người dùng quay lại phân đoạn tạo sau đó mà quay lại chế độ xem trước đó. Đó có thể là danh sách tất cả các mục hoặc một mục khác mà mục mới dựa trên đó. Vậy thì clearTask có phải là lựa chọn duy nhất không?
findusl

Không, nó thực sự không nên được sử dụng nữa. Thay vào đó, bạn nên sử dụng popUpTo hoàn toàn. Tôi sẽ cập nhật câu trả lời rất sớm với những phát hiện của tôi về việc sử dụng popUpTo.
Mel

5

Cuối cùng tôi cũng tìm ra cách làm thế nào để tắt UP trong Điều hướng cho một số phân đoạn với Thành phần Kiến trúc Điều hướng mới?

Tôi đã phải chỉ định .setClearTask (true) làm NavOption.

mAuth.signInWithCredential(credential)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "signInWithCredential:success");


                        NavOptions.Builder navBuilder = new NavOptions.Builder();
                        NavOptions navOptions = navBuilder.setClearTask(true).build();
                        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment,null,navOptions);
                    } else {
                        Log.w(TAG, "signInWithCredential:failure", task.getException());

                    }

                }
            });

2
Rất vui khi thấy bạn có thể thực hiện đề xuất của tôi về việc sử dụng navigate(int resId, Bundle args, NavOptions navOptions)NavOptions
Chuồng

Đúng. Cảm ơn @Barns
Youssef El Behi,

14
vâng "setClearTask" không được dùng nữa nhưng thay vào đó bạn có thể sử dụng: val navOptions = NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build() findNavController().navigate(R.id.my_next_frag, null, navOptions)
Pablo Reyes

1
@ c-an nó sẽ hoạt động. Có thể là bạn đang sử dụng sai mục đích phân mảnh (ví dụ:R.id.my_next_frag có thể giống như đăng nhập hoặc màn hình giật gân); còn nếu bạn muốn popBack chỉ là một, bạn có thể sử dụng: NavHostFragment.findNavController(this).popBackStack().
Pablo Reyes

2
@PabloReyes Đây là câu trả lời chính xác. Chỉ cần chỉ định phân đoạn cuối cùng cần loại bỏ: Giả sử bạn có ngăn xếp này: Fragment_1 Fragment_2 Fragment_3 Bạn hiện đang ở trên Fragment_3 và muốn điều hướng đến Fragment_4 và xóa tất cả các phân đoạn khác. Chỉ cần chỉ định trong điều hướng xml: app: popUpTo = "@ id / Fragment_1" app: popUpToInclusive = "true"
user3193413

5

Đây là cách tôi thực hiện nó.

 //here the R.id refer to the fragment one wants to pop back once pressed back from the newly  navigated fragment
 val navOption = NavOptions.Builder().setPopUpTo(R.id.startScorecardFragment, false).build()

//now how to navigate to new fragment
Navigation.findNavController(this, R.id.my_nav_host_fragment)
                    .navigate(R.id.instoredBestPractice, null, navOption)

Tại sao lại là cờ bao hàm của bạn false?
IgorGanapolsky

2
Theo tài liệu inclusive boolean: true to also pop the given destination from the back stack Vì vậy, tôi đặt bao gồm thành sai vì tôi không muốn bật phân đoạn hiện tại khỏi ngăn xếp.
Abdul Rahman Shamair

4
    NavController navController 
    =Navigation.findNavController(requireActivity(),          
    R.id.nav_host_fragment);// initialize navcontroller

    if (navController.getCurrentDestination().getId() == 
     R.id.my_current_frag) //for avoid crash
  {
    NavDirections action = 
    DailyInfoSelectorFragmentDirections.actionGoToDestionationFragment();

    //for clear current fragment from stack
    NavOptions options = new 
    NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build();
    navController.navigate(action, options);
    }

1

Bạn có thể ghi đè mặt sau của hoạt động cơ sở như sau:

override fun onBackPressed() {

  val navigationController = nav_host_fragment.findNavController()
  if (navigationController.currentDestination?.id == R.id.firstFragment) {
    finish()
  } else if (navigationController.currentDestination?.id == R.id.secondFragment) {
    // do nothing
  } else {
    super.onBackPressed()
  }

}

1

Lưu ý: Điều này có thể không liên quan đến câu hỏi thực tế.

Cách tiếp cận này sẽ hữu ích nếu cần duy trì một trường hợp phân mảnh duy nhất trong Điều hướng .

// imports
import androidx.navigation.navOptions

// navigate to fragment
navController.navigate(R.id.nav_single_instance_fragment, null,
                            navOptions {
                                popUpTo = R.id.nav_single_instance_fragment
                                launchSingleTop = true
                            })

Đây, navOptionslà DSL và cần được nhập. Tại thời điểm câu trả lời này, tôi đang sử dụng các phiên bản gradle sau đây của Giao diện người dùng điều hướng .

    def nav_version = "2.2.0"
    implementation "androidx.navigation:navigation-ui:$nav_version"
    implementation "androidx.navigation:navigation-fragment:$nav_version"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

0

Tôi đã vật lộn một lúc để ngăn nút quay lại quay lại đoạn bắt đầu của mình, trong trường hợp của tôi là thông báo giới thiệu chỉ xuất hiện một lần.

Giải pháp dễ dàng là tạo một hành động toàn cục trỏ đến đích mà người dùng nên ở lại. Bạn phải đặt app:popUpTo="..."chính xác - đặt nó đến đích bạn muốn xuất hiện. Trong trường hợp của tôi, đó là tin nhắn giới thiệu của tôi. Cũng được thiết lậpapp:popUpToInclusive="true"


0

Tôi có thể giải thích điều này thông qua ví dụ nhỏ. Tôi có hai mảnh danh sách nhân viên và xóa nhân viên. Trong xóa nhân viên, tôi có hai sự kiện nhấp chuột là hủy và xóa. Nếu tôi nhấp vào hủy, sau đó nó chỉ trở lại màn hình danh sách nhân viên và nếu tôi nhấp vào xóa thì tôi muốn xóa tất cả các ngăn cản trước đó và chuyển đến màn hình danh sách nhân viên.

Để chuyển đến màn hình trước:

<action
        android:id="@+id/action_deleteEmployeeFragment_to_employeesListFragment2"
        app:destination="@id/employeesListFragment"/>


    btn_cancel.setOnClickListener {
                it.findNavController().popBackStack()
            }

Để xóa tất cả backstack trước đó và chuyển đến màn hình mới:

<action
        android:id="@+id/action_deleteEmployeeFragment_to_employeesListFragment2"
        app:destination="@id/employeesListFragment"
        app:popUpTo="@id/employeesListFragment"
        app:popUpToInclusive="true"
        app:launchSingleTop="true" />


 btn_submit.setOnClickListener {
                   it.findNavController().navigate(DeleteEmployeeFragmentDirections.actionDeleteEmployeeFragmentToEmployeesListFragment2())
        }

Để tham khảo thêm: Ví dụ về điều hướng Jetpack


0

Bạn có thể làm đơn giản như:

getFragmentManager().popBackStack();

Nếu bạn muốn kiểm tra số lượng, bạn có thể làm như sau:

getFragmentManager().getBackStackEntryCount()
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.