如何在NavigationView的项目上添加跑马灯文本?

4
我的项目名称很长,因此我想确保它们的名称可以水平滚动。 我在几个SO帖子上搜索过,但并没有找到解决我的问题的方法。 但是我尝试了这个:
我的 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>

我的 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>

我的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);


}

但是没有改变,有人有什么想法吗?请帮忙。

编辑:

我尝试覆盖这个:

<?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>

我有一些更改:

覆盖之后:

输入图片说明

覆盖之前:

输入图片说明

3个回答

2

我认为没有官方方法来在NavigationView的项上添加跑马灯文本,最好的方法是实现自己的自定义RecyclerView作为NavigationView,但如果您想保持默认的NavigationView,则下面将描述一种解决方法:

简短说明:

关键点是找到NavigationMenuView,它是包含所有导航菜单项的RecyclerView,然后找到所有类型为NavigationMenuItemView的可见子项的RecyclerView.ViewHolder,以便能够访问每个菜单项的CheckedTextView以更改其属性为跑马灯文本。

实现

下面我实现了两个帮助函数:setNavigationViewItemsMarquee(NavigationView navigationView)负责从NavigationView中找到NavigationMenuView,第二个函数setNavigationMenuViewItemsMarquee(NavigationMenuView menuRecyclerView, boolean startMarquee)负责找到所有可见的CheckedTextView项以将其属性更改为跑马灯文本。

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

用法:

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

从上面的代码中,我使用了DrawerLayout.DrawerListener来仅在抽屉打开时启动跑马灯效果,并在抽屉关闭时停止跑马灯效果。
如果您还有当前屏幕上看不见的菜单项,并且需要在RecyclerView滚动期间添加跑马灯效果,可以监听RecyclerView.OnScrollListener(),例如:mMenuRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(),并在onScrolled()回调中使用上面的辅助程序将新可见项目启动跑马灯效果,例如:setNavigationMenuViewItemsMarquee(mMenuRecyclerView, true); 当然,您可以根据自己的需求进一步修改代码。 结果:

drawer


1
在上面的例子中,我只有在抽屉将要打开并对用户可见时才启动跑马灯效果,并且当抽屉将要关闭时停止跑马灯效果,因为现在抽屉已经完全隐藏,不再需要跑马灯效果。@LouisChabert - MariosP
@LouisChabert 在这里添加为评论。 - MariosP
https://github.com/LoulouChabChab/DebugApp.git - Louis Chabert
同样地,在你的MainActivity中,如果你添加我在答案中提到的代码,它也会起作用。 - MariosP
让我们在聊天中继续这个讨论。点击此链接 - MariosP
显示剩余11条评论

1
这个技巧就是使用样式。属性适用于直接的项目,例如当您有一个 TextView 希望使用跑马灯效果时,您需要将这些更改直接添加到该项目中。
在一些示例中,如果您的视图实际上是自定义的(例如,您可能知道自定义视图可以是多个不同基本视图的集合),则必须在样式中进行更改,以便这些属性传播到相应的项,例如您的 TextView。
因此,更改应该在样式中进行,类似于下面的内容。在 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>

将新样式设置为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" />

另一个非常简单的方法是使用导航视图的自定义条目。你可以在这里找到更多关于如何构建它的信息:这里


"<com.google.android.material.navigation.NavigationView>"和"<android.support.design.widget.NavigationView>"有什么不同? 我尝试修改了我的代码,但它仍然无法工作:( - Louis Chabert
1
它们都代表相同的视图。当您看到以com.google.android开头的内容时,这意味着这个视图的最新版本(适用于新的Android设备)。当您看到android.support.design时,这意味着适用于所有设备(新的和旧版Android的后移版本)。 - GensaGames

1

我已经在activity_main.xml中以某种方式使用了导航视图,我只是在我的帖子开头添加了代码。 - Louis Chabert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接