Thành phần kiến ​​trúc điều hướng Android - Nhận phân đoạn hiển thị hiện tại


96

Trước khi thử thành phần Điều hướng, tôi đã sử dụng để thực hiện các giao dịch phân mảnh theo cách thủ công và sử dụng thẻ phân mảnh để tìm nạp phân mảnh hiện tại.

val fragment:MyFragment = supportFragmentManager.findFragmentByTag(tag):MyFragment

Bây giờ trong bố cục hoạt động chính của tôi, tôi có một cái gì đó như:

<fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

Làm cách nào để truy xuất phân đoạn được hiển thị hiện tại bằng thành phần Điều hướng? Đang làm

supportFragmentManager.findFragmentById(R.id.nav_host)

trả về a NavHostFragmentvà tôi muốn truy xuất 'MyFragment' đã hiển thị của mình.

Cảm ơn bạn.


4
Không có cách nào để truy xuất phân mảnh hiện tại stackoverflow.com/a/50689587/1268507 Bạn đang cố gắng đạt được điều gì? có thể có một số giải pháp khác cho nó.
Alex,

Tôi cần từ mảnh vỡ của mình để bắt đầu ý định máy ảnh, chụp ảnh và sử dụng hình ảnh đã chụp. Để làm như vậy, tôi đã bắt đầu một hoạt động và chờ đợi kết quả onActivityResultcủa hoạt động chính của mình. Vì tôi không thể lấy được mảnh vỡ, nên tôi chỉ cần chuyển tất cả những thứ này vào chính mảnh vỡ đó và dường như hoạt động.
Alin

Bạn có muốn thực hiện một thao tác trên đối tượng phân mảnh đó. Hay chỉ bạn muốn đoạn nào được hiển thị?
Ranjan,

Câu trả lời:


102

Tôi đã tìm ra một cách cho bây giờ và nó như sau:

NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host);
navHostFragment.getChildFragmentManager().getFragments().get(0);

Trong trường hợp tất nhiên bạn biết nó là mảnh đầu tiên. Tôi vẫn đang điều tra một cách mà không có điều này. Tôi đồng ý rằng đó không phải là cách tốt nhất nhưng đó nên là một cái gì đó cho bây giờ.


6
Thật tuyệt khi thấy một giải pháp hiệu quả. Tôi đã từ bỏ điều này và bắt đầu sử dụng viewModel được chia sẻ giữa hoạt động và phân đoạn, Điều này có nghĩa là tôi có thể chuyển các sự kiện giữa chúng mà không cần gọi lại hoặc truy cập trực tiếp.
Alin

Như đã đề cập trong vấn đề báo cáo lỗi này Issuetracker.google.com/issues/119800853, đây không phải là phương pháp chính xác để thực hiện. Vui lòng xem xét thay đổi câu trả lời đúng!
Nicolas Pietri

2
Như nó đã được đề cập, nó không phải là giải pháp, nó là một cách giải quyết cho đến khi một giải pháp thích hợp được tìm thấy.
Andrei Toader

1
Điều này làm việc, cảm ơn rất nhiều. Tôi đã thử getPrimaryNavigationFragment () như được đề xuất trong trình theo dõi lỗi nhưng nó không hoàn thành công việc.
Jerry Hu

1
Dựa trên giải pháp này, tôi đã tạo một hàm kotlin có thể kiểm tra xem phân đoạn hiện tại có thuộc loại cụ thể hay không (thay vì id): navHostFragment!!.childFragmentManager.fragments.any { it.javaClass == fragmentClass && it.isVisible }Hoạt động khá giống nhau. Hy vọng nó sẽ giúp ích cho bất cứ ai!
P Kuijpers

49

Không có cách nào tôi có thể tìm thấy để truy xuất phiên bản phân đoạn hiện tại. Tuy nhiên, bạn có thể lấy ID của đoạn được thêm cuối cùng bằng cách sử dụng mã bên dưới.

navController.currentDestination?.getId()

Nó trả về ID trong tệp điều hướng. Hi vọng điêu nay co ich.


Cảm ơn về câu trả lời của bạn. Tôi cần truy xuất phân mảnh để có thể tương tác với nó. Vì navController không thể trả lại nó, tôi đã kết thúc việc sử dụng mô hình xem được chia sẻ để nói chuyện giữa hoạt động và các mảnh của nó
Alin

7
tôi mong rằng id trở phù hợp với id trong xml nav, nhưng nó không, vì vậy nói cách khác, bạn có thể không kiểm tra navController.getCurrentDestination () getId () == R.id.myfrag.
LERES Aldtai

4
@LeresAldtai Nó khớp với ID của phân mảnh trong tệp điều hướng.
slhddn

1
Cảm ơn bạn @slhddn. Nhận xét của bạn đã giúp tôi và có thể lấy id từ phân đoạn và làm cho công việc này. Đã thêm một câu trả lời ở đây với mã của tôi trong trường hợp hữu ích cho người khác. Chúc mừng.
Francislainy Campos

32

Bạn nên xem thuộc tính childFragmentManager primaryNavigationFragment của phân đoạn điều hướng, đây là nơi tham chiếu đến phân đoạn hiển thị hiện tại.

val navHost = supportFragmentManager.findFragmentById(R.id.main_nav_fragment)
    navHost?.let { navFragment ->
        navFragment.childFragmentManager.primaryNavigationFragment?.let {fragment->
                //DO YOUR STUFF
        }
    }
}

2
Phương pháp này hiện đã được ghi lại tronggmentManager Issuetracker.google.com/issues/119800853
Nicolas Pietri

điều này có vẻ là một chút vấn đề. xử lý Điều hướng lên, tôi phải thiết lập thanh tác vụ để nhận lệnh gọi đến onSupportNaigateUp ()
filthy_wizard

18

Đây có vẻ như là giải pháp sạch nhất. Cập nhật: Đã thay đổi chức năng tiện ích mở rộng thành thuộc tính. Cảm ơn Igor Wojda .

1. Tạo phần mở rộng sau

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager

val FragmentManager.currentNavigationFragment: Fragment?
    get() = primaryNavigationFragment?.childFragmentManager?.fragments?.first()

2. Trong Activityvới NavHostFragmentsử dụng nó như thế này:

val currentFragment = supportFragmentManager.currentNavigationFragment

2
Tuyệt vời - một trong những điều này tôi sẽ thay đổi - tôi sẽ định nghĩa tiện ích mở rộng là thuộc tính FragmentManager.currentNavigationFragment(không phải là một hàm)
Igor Wojda

16

Sử dụng addOnDestinationChangedListenertrong hoạt động cóNavHostFragment

Đối với Java

 NavController navController= Navigation.findNavController(MainActivity.this,R.id.your_nav_host);

        navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
            @Override
            public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
                Log.e(TAG, "onDestinationChanged: "+destination.getLabel());
            }
        });

Đối với Kotlin

var navController :NavController=Navigation.findNavController(this, R.id.nav_host_fragment)
    navController.addOnDestinationChangedListener { controller, destination, arguments ->
        Log.e(TAG, "onDestinationChanged: "+destination.label);

    }

8

điều này có thể muộn nhưng đối với bất kỳ ai vẫn đang tìm kiếm

val currentFragment = NavHostFragment.findNavController(nav_host_fragment).currentDestination?.id

sau đó bạn có thể làm một cái gì đó như

if(currentFragment == R.id.myFragment){
     //do something
}

lưu ý rằng đây là đối với kotlin, mã java phải gần như giống nhau



5
navController().currentDestination?.id

sẽ cung cấp cho bạn Fragmentid hiện tại của bạn ở đâu

private fun navController() = Navigation.findNavController(this, R.id.navHostFragment)

id này là id mà bạn đã cung cấp trong Navigation Graph XMLtệp của mình dưới fragmentthẻ. Bạn cũng có thể so sánh currentDestination.labelnếu bạn muốn.


4

Đã tìm thấy giải pháp làm việc.

private fun getCurrentFragment(): Fragment? {
    val currentNavHost = supportFragmentManager.findFragmentById(R.id.nav_host_id)
    val currentFragmentClassName = ((this as NavHost).navController.currentDestination as FragmentNavigator.Destination).className
    return currentNavHost?.childFragmentManager?.fragments?.filterNotNull()?.find {
        it.javaClass.name == currentFragmentClassName
    }
}

3

Từ một Hoạt động sử dụng NavHostFragment, bạn có thể sử dụng mã bên dưới để truy xuất phiên bản của Active Fragment.

val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment

3

Yêu cầu của tôi là lấy phân đoạn hiển thị hiện tại từ Hoạt động chính, giải pháp của tôi là

 private LoginFragment getCurrentVisibleFragment() {
        NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().getPrimaryNavigationFragment();
        FragmentManager fragmentManager = navHostFragment.getChildFragmentManager();
        Fragment loginFragment = fragmentManager.getPrimaryNavigationFragment();
        if(loginFragment instanceof LoginFragment){
            return (LoginFragment)loginFragment;
        }
        return null;
    }

Điều này có thể giúp một số người có cùng vấn đề.


có Kotlin lang?
IgorGanapolsky

1
Điều này hoạt động hoàn hảo đối với tôi, không chắc chắn có gì với các câu trả lời khác? Có thể điều này không hiệu quả trước đây nhưng nó hoạt động như một sự quyến rũ bây giờ.
Wess

2

Theo @slhddn câu trả lời và nhận xét, đây là cách tôi sử dụng nó và truy xuất id phân đoạn từ tệp nav_graph.xml :

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    setSupportActionBar(findViewById(R.id.toolbar))

    val navController = findNavController(this, R.id.nav_host_fragment)

    ivSettingsCog.setOnClickListener {

        if (navController.currentDestination?.id == R.id.updatesFragment) // Id as per set up on nav_graph.xml file
        {
            navController.navigate(R.id.action_updatesFragment_to_settingsFragment)
        }

    }

}

}

1

bạn có thể vui lòng kiểm tra bằng getChildFragmentManager()cuộc gọi với

NavHostFragment fragment = supportFragmentManager.findFragmentById(R.id.nav_host);
MyFragment frag = (MyFragment) fragment.getChildFragmentManager().findFragmentByTag(tag);

1

Làm :

• trong một số nút trong phân đoạn của bạn:

val navController = findNavController(this)
  navController.popBackStack()

• Trong Hoạt động của bạn onBackPressed ()

override fun onBackPressed() {
        val navController = findNavController(R.id.nav_host_fragment)
        val id = navController.currentDestination?.getId()
        if (id == R.id.detailMovieFragment){
            navController.popBackStack()
            return
        }

        super.onBackPressed()
    } 

1

Nếu bạn cần phiên bản của phân đoạn của mình, bạn có thể sử dụng:

(Hoạt động) => supportFragmentManager.fragments.first().childFragmentManager.fragments.first()

rất xấu nhưng từ đó bạn có thể kiểm tra xem đó có phải là bản sao của đoạn bạn muốn xử lý không


1

Trên Androidx, tôi đã thử nhiều phương pháp để tìm ra phân mảnh bắt buộc từ NavHostFragment. Cuối cùng tôi có thể bẻ khóa nó bằng phương pháp dưới đây

public Fragment getFunctionalFragment (String tag_name)
{
    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
    FragmentManager navHostManager = Objects.requireNonNull(navHostFragment).getChildFragmentManager();

    Fragment fragment=null;
    List fragment_list = navHostManager.getFragments();

    for (int i=0; i < fragment_list.size() ; i ++ )
    {
        if ( fragment_list.get(i) instanceof HomeFragment && tag_name.equals("frg_home")) {
            fragment = (HomeFragment) fragment_list.get(i);
            break;
        }
    }

    return fragment;
}

1
Chào mừng đến với SO! Xin vui lòng, trước khi bạn đăng câu trả lời của bạn, hãy kiểm tra nó ... Mã không rõ ràng. Một vấn đề khác: nếu bạn chỉ mang mã, hãy giải thích giải pháp của bạn một chút, tối đa khi có thêm 16 câu trả lời, vì vậy bạn nên giải thích Ưu điểm của câu trả lời của bạn.
David García Bodego,

1

Phân đoạn đầu tiên trong phân đoạn navhost là phân mảnh hiện tại

Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
if (navHostFragment != null) 
{Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);}

Bạn có thể giải thích thêm về câu trả lời của mình một chút không? Có thể bằng cách giải thích mã của bạn.
Hai mươi

Câu trả lời chỉ có mã không được khuyến khích. Vui lòng cung cấp lời giải thích tại sao và cách câu trả lời của bạn giải quyết vấn đề.
Igor F.

1

Đầu tiên, bạn lấy phân đoạn hiện tại theo id, sau đó bạn có thể tìm thấy phân mảnh, tùy thuộc vào id đó.

int id = navController.getCurrentDestination().getId();
Fragment fragment = getSupportFragmentManager().findFragmentById(id);

0

Tôi kết hợp câu trả lời của Andrei Toaderscâu trả lời của Mukul Bhardwajs với một OnBackStackChangedListener.

Như một thuộc tính:

private FooFragment fragment;

trong tôi onCreate

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host);
FragmentManager navHostManager = Objects.requireNonNull(navHostFragment).getChildFragmentManager();
FragmentManager.OnBackStackChangedListener listener = () -> {
        List<Fragment> frags = navHostManager.getFragments();
        if(!frags.isEmpty()) {
            fragment = (FooFragment) frags.get(0);
        }
};
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
        if(destination.getId()==R.id.FooFragment){
            navHostManager.addOnBackStackChangedListener(listener);
        } else {
            navHostManager.removeOnBackStackChangedListener(listener);
           fragment = null;
        }
});

Bằng cách này, tôi có thể lấy một phân đoạn, sẽ được hiển thị sau này. Một thậm chí có thể lặp lại mặc dù fragsvà kiểm tra nhiều đoạn với instanceof.


0

Tôi cần làm điều này để thiết lập chế độ xem điều hướng dưới cùng và tôi đã làm theo cách này:

val navController = Navigation.findNavController(requireActivity(), R.id.nav_tabs_home)
        val bottomNavBar: BottomNavigationView = nav_content_view
        NavigationUI.setupWithNavController(bottomNavBar, navController)

0

Cách tốt nhất để đạt được điều này là đăng ký một cuộc gọi lại vòng đời phân mảnh .

Theo câu hỏi ban đầu, nếu của bạn NavHostFragmentđược định nghĩa là ...

<fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

Sau đó, trong hoạt động chính của bạn onCreate(..)...

nav_host.childFragmentManager.registerFragmentLifecycleCallbacks(
    object:FragmentManager.FragmentLifecycleCallbacks() {
        override fun onFragmentViewCreated(
            fm: FragmentManager, f: Fragment, v: View,
            savedInstanceState: Bundle?
        ) {
            // callback method always called whenever a
            // fragment is added, or, a back-press returns
            // to the start-destination
        }
    }
)

Các phương thức gọi lại khác có thể được ghi đè để phù hợp với yêu cầu của bạn.


0

Giải pháp này sẽ hoạt động trong hầu hết các tình huống Hãy thử cách này:

val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment?.parentFragment as MainFragment

hoặc cái này:

val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment as MainFragment

0

Mã này hoạt động từ tôi

NavDestination current = NavHostFragment.findNavController(getSupportFragmentManager().getPrimaryNavigationFragment().getFragmentManager().getFragments().get(0)).getCurrentDestination();

switch (current.getId()) {
    case R.id.navigation_saved:
        // WRITE CODE
        break;
}

0

Điều này phù hợp với tôi

(navController.currentDestination as FragmentNavigator.Destination).className 

Nó sẽ trả lại canonicalNamemảnh vỡ của bạn


Điều này trông giống như Sự phản chiếu
IgorGanapolsky

0

điều này có thể muộn nhưng có thể giúp ích cho ai đó sau này.

nếu bạn đang sử dụng điều hướng lồng nhau, bạn có thể nhận được id như thế này trong Kotlin

val nav = Navigation.findNavController(requireActivity(), R.id.fragment_nav_host).currentDestination

và đây là một trong những phân đoạn trong mảnh_nav_host.xml

<fragment
    android:id="@+id/sampleFragment"
    android:name="com.app.sample.SampleFragment"
    android:label="SampleFragment"
    tools:layout="@layout/fragment_sample" />

và bạn có thể kiểm tra tất cả những gì bạn cần như thế này

if (nav.id == R.id.sampleFragment || nav.label == "SampleFragment"){}

0

Cuối cùng, giải pháp làm việc cho những bạn vẫn đang tìm kiếm Thực ra nó rất đơn giản và bạn có thể đạt được điều này bằng cách sử dụng callback.

làm theo các bước sau:

  1. Tạo trình nghe bên trong phân đoạn bạn muốn lấy phiên bản từ hoạt động onCreate ()

    public class HomeFragment extends Fragment {
    
    private OnCreateFragment createLIstener;
    
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        createLIstener.onFragmentCreated(this);
        View root = inflater.inflate(R.layout.fragment_home, container, false);
        //findViews(root);
        return root;
    }
    
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.menu_main, menu);
    
    }
    
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        if (item.getItemId()==R.id.action_show_filter){
            //do something
        }
        return super.onOptionsItemSelected(item);
    }
    
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            createLIstener = (OnCreateFragment) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
            }
        }
    
        public interface OnCreateFragment{
            void onFragmentCreated(Fragment f);
        }
    }
    
  2. Triển khai trình lắng nghe của bạn trong Phân đoạn / Hoạt động máy chủ

    public class MainActivity extends AppCompatActivity implements HomeFragment.OnCreateFragment {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            BottomNavigationView navView = findViewById(R.id.nav_view);
           // Passing each menu ID as a set of Ids because each
           // menu should be considered as top level destinations.
            AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                    R.id.navigation_home,
                    R. ----)
            .build();
            NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
            NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
            NavigationUI.setupWithNavController(navView, navController);
        }
    
        @Override
        public void onFragmentCreated(Fragment f) {
            if (f!=null){
                H.debug("fragment created listener: "+f.getId());
                f.setHasOptionsMenu(true);
            }else {
                H.debug("fragment created listener: NULL");
            }
        }
    }
    

Và đó là mọi thứ bạn cần để thực hiện công việc. Hy vọng điều này giúp ai đó


0

Cách tốt nhất tôi có thể tìm ra với cách sử dụng 1 mảnh, Kotlin, Điều hướng:

Trên tệp bố cục của bạn, hãy thêm thẻ: "android: tag =" main_fragment_tag ""

<fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:tag="main_fragment_tag"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

Về hoạt động của bạn:

val navHostFragment = supportFragmentManager.findFragmentByTag("main_fragment_tag") as NavHostFragment
val mainFragment = navHostFragment.childFragmentManager.fragments[0] as MainFragment
mainFragment.mainFragmentMethod()

0
val fragment: YourFragment? = yourNavHost.findFragment()

Lưu ý: findFragmentchức năng mở rộng nằm trong androidx.fragment.appgói và đó là chức năng chung


-2

Trong hoạt động của bạn, xác định một đối tượng phân mảnh. Và từ mỗi phân mảnh gọi phương thức này bằng cách truyền đối tượng phân mảnh, giống như vậy, đối tượng phân mảnh tĩnh sẽ bị ghi đè bởi phân mảnh hiển thị hiện tại mỗi lần.

//method in MainActivity
private static Fragment fragment;

public void setFragment(Fragment fragment){
this.fragment = fragment;
}

//And from your fragment class, you can call
((<yourActivity in which fragment present>)getActivity()).setFragment(<your fragment object>);

//if you want to set it directly;
 ((<yourActivity in which fragment present>)getActivity()).fragment=<your fragment object>;

Bạn cũng có thể thiết lập gọi lại từ phân mảnh thành hoạt động, để có cách tiếp cận tốt hơn và chuyển đối tượng phân mảnh hiện tại của bạn.


2
Cảm ơn bạn đã dành thời gian. Tôi đang tìm kiếm thứ gì đó để sử dụng từ chính thành phần Điều hướng.
Alin
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.