java – How to add marquee text on items of NavigationView?

There is no official way to add marquee text on items of NavigationView i think the best approach is to implement your own custom RecyclerView to act as the NavigationView but if you want to stay with the default NavigationView below i will describe a workaround:

Short Description:

The key point is to find the NavigationMenuView which is a RecyclerView containing all the Navigation Menu items and from there to find all RecyclerView.ViewHolder visible children of type of NavigationMenuItemView to be able to get access to each menu item CheckedTextView to change its properties to marquee text.

Implementation

Below i have implemented two helper functions the setNavigationViewItemsMarquee(NavigationView navigationView) which is responsible to find the NavigationMenuView from the NavigationView and the second one the setNavigationMenuViewItemsMarquee(NavigationMenuView menuRecyclerView, boolean startMarquee) which is responsible to find all CheckedTextView visible items to change their properties to marquee text.

private void setNavigationViewItemsMarquee(NavigationView navigationView){

    //find the NavigationMenuView RecyclerView id
    int designNavigationViewId = getResources().getIdentifier("design_navigation_view", "id", getPackageName());
    if(designNavigationViewId!=0) {
        NavigationMenuView menuRecyclerView = navigationView.findViewById(designNavigationViewId);
        if(menuRecyclerView!=null) {
            //register ViewTreeObserver.OnGlobalLayoutListener to be informed for changes in the global layout state or the visibility of views within the view tree
            menuRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
                @Override
                public void onGlobalLayout() {
                    //remove the ViewTreeObserver.OnGlobalLayoutListener() to be called only once
                    menuRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    //set the initial Marquee state to false for all visible items
                    setNavigationMenuViewItemsMarquee(menuRecyclerView, false);
                }
            });
            this.mMenuRecyclerView = menuRecyclerView;
        }
    }
}

private void setNavigationMenuViewItemsMarquee(NavigationMenuView menuRecyclerView, boolean startMarquee){

    if(menuRecyclerView!=null){
        //for every visible child in RecyclerView get its ViewHolder as NavigationMenuItemView and from there find the CheckedTextView using the design_menu_item_text id
        for (int i = 0; i < menuRecyclerView.getChildCount(); i++) {
            View child = menuRecyclerView.getChildAt(i);
            if (child!=null) {
                RecyclerView.ViewHolder holder = menuRecyclerView.getChildViewHolder(child);
                if(holder!=null && holder.itemView instanceof NavigationMenuItemView){
                    NavigationMenuItemView navigationMenuItemView = (NavigationMenuItemView)holder.itemView;
                    int designMenuItemTextId = getResources().getIdentifier("design_menu_item_text", "id", getPackageName());
                    if(designMenuItemTextId!=0){
                        //CheckedTextView found change it to MARQUEE
                        CheckedTextView textView = navigationMenuItemView.findViewById(designMenuItemTextId);
                        if(textView!=null) {
                            textView.setSingleLine(true);
                            textView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
                            textView.setHorizontalFadingEdgeEnabled(true);
                            textView.setMarqueeRepeatLimit(startMarquee ? -1 : 0);
                            textView.setSelected(startMarquee);
                        }
                    }
                }
            }
        }
    }
}

Usage:

private NavigationMenuView mMenuRecyclerView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    setSupportActionBar(findViewById(R.id.toolbar));

    //get DrawerLayout and NavigationView
    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    NavigationView navigationView = findViewById(R.id.nav_view);

    //set the NavController with AppBarConfiguration
    mAppBarConfiguration = new AppBarConfiguration.Builder(
            R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
            .setOpenableLayout(drawer)
            .build();
    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
    NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
    NavigationUI.setupWithNavController(navigationView, navController);

    //set NavigationView Items Marquee
    setNavigationViewItemsMarquee(navigationView);

    //add also the DrawerLayout.DrawerListener to start/stop the Marquee based on onDrawerOpened/onDrawerClosed callbacks
    drawer.addDrawerListener(new DrawerLayout.DrawerListener() {
        @Override
        public void onDrawerSlide(@NonNull View view, float v) {}

        @Override
        public void onDrawerStateChanged(int i) {}

        @Override
        public void onDrawerOpened(@NonNull View view) {
            //start Marquee effect for all visible items
            setNavigationMenuViewItemsMarquee(mMenuRecyclerView, true);
        }

        @Override
        public void onDrawerClosed(@NonNull View view) {
            //stop Marquee effect for all visible items
            setNavigationMenuViewItemsMarquee(mMenuRecyclerView, false);
        }
    });
}

From the above code i have used the DrawerLayout.DrawerListener to start the marquee effect only when the drawer is opened and to stop the marquee effect when the drawer is closed.

In case also you have menu items which are not visible currently in the screen and you need to add the marquee effect during a RecyclerView scroll you can listen to RecyclerView.OnScrollListener() like: mMenuRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() and in onScrolled() callback you can start the marque effect to the new visible items using the above helper like setNavigationMenuViewItemsMarquee(mMenuRecyclerView, true); Of Course you can modify further the code based on your needs.

Result:

Leave a Comment