Reputation: 439
The names of my items are quite long, so I would like to make sure that their names scroll horizontally. I have searched on several SO posts, but I have not found a solution to my problem But I can't, I tried this:
my activity_main.xml :
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
app:headerLayout="@layout/nav_header"
app:menu="@menu/menuDrawer">
</com.google.android.material.navigation.NavigationView>
My XML "menuDrawer":
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_welcome"
android:icon="@drawable/ic_folder_black_24dp"
android:title="@string/menu_welcome" />
<item
android:id="@+id/nav_dataset1"
android:icon="@drawable/ic_folder_black_24dp"
android:title="@string/menu_dataset1"/>
<item
android:id="@+id/nav_dataset2"
android:icon="@drawable/ic_folder_black_24dp"
android:title="@string/menu_dataset2" />
<item
android:id="@+id/nav_dataset3"
android:icon="@drawable/ic_folder_black_24dp"
android:title="@string/menu_dataset3" />
</group>
My java :
private AppBarConfiguration mAppBarConfiguration;
private TextView tvDataset1;
private TextView tvDataset2;
private TextView tvDataset3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
tvDataset1 = this.findViewById(R.id.nav_dataset1);
tvDataset1.setSelected(true);
setSupportActionBar(toolbar);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_dataset1, R.id.nav_dataset2, R.id.nav_dataset3)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
But no change, does anyone have an idea ? Please help
EDIT :
I tried to override this :
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<CheckedTextView
android:id="@+id/design_menu_item_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawablePadding="@dimen/design_navigation_icon_padding"
android:gravity="center_vertical|start"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:focusable="true"
android:focusableInTouchMode="true"/>
<ViewStub
android:id="@+id/design_menu_item_action_area_stub"
android:inflatedId="@+id/design_menu_item_action_area"
android:layout="@layout/design_menu_item_action_area"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</merge>
I have some changes : AFTER the override:
BEFORE the override :
Upvotes: 4
Views: 619
Reputation: 9113
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:
Upvotes: 2
Reputation: 5788
The trick is using styles. Attribute works for direct item, for ex, when you have a TextView
where you want to marquee, you add these changes directly onto the item.
In examples, where your view is actually custom (for ex. as you may know custom view might be set of mutliple different base views) you have to change it in styles in order for these attributes to propagate to corresponding items, in your example TextView.
So the changes should be in styles, something like next. Add a new style to the styles.xml:
res / values / styles.xml
<style name="TextAppearance">
<item name="android:ellipsize">marquee</item>
<item name="android:focusable">true</item>
<item name="android:focusableInTouchMode>true</item>
<item name="android:marqueeRepeatLimit">marquee_forever</item>
</style>
Set the new style to the NavigationView
:
res / layout / activity_main.xml
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer"
app:theme="@style/TextAppearance" />
PS. Another very simple approach it's by using custom entries for Navigation View. You can find more information on how to build it from here.
Upvotes: 1
Reputation: 76809
Styling Widget.MaterialComponents.NavigationView
will not cause it to scroll.
One would have to replace the whole NavigationView
with a ListView
or RecyclerView
.
And OnNavigationItemSelectedListener(MenuItem item)
would need to be implemented.
Upvotes: 1