【问题标题】:Marquee title in Toolbar / ActionBar in Android with Lollipop SDK?带有 Lollipop SDK 的 Android 工具栏/操作栏中的字幕标题?
【发布时间】:2015-01-21 17:02:30
【问题描述】:

我尝试了几种不同的方法,包括找到here 的方法(这反过来又让我尝试了this 问题的两个最佳答案),以及使用反射来访问 TextView 和设置相关方法。两次尝试都失败了,前者导致根本没有将文本设置为标题(我将文本设置为正确的 textview 元素),后者设置了文本并删除了椭圆,但根本没有选框。下面是我的反思尝试。

import android.content.Context;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.reflect.Field;

public class MarqueeToolbar extends Toolbar {

    public MarqueeToolbar(Context context) {
        super(context);
    }

    public MarqueeToolbar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MarqueeToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setTitle(CharSequence title) {
        if (!reflected) {
            reflected = reflectTitle();
        }
        super.setTitle(title);
    }

    @Override
    public void setTitle(int resId) {
        if (!reflected) {
            reflected = reflectTitle();
        }
        super.setTitle(resId);
    }

    boolean reflected = false;
    private boolean reflectTitle() {
        try {
            Field field = Toolbar.class.getDeclaredField("mTitleTextView");
            field.setAccessible(true);
            TextView titleView = (TextView) field.get(this);
            titleView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
            titleView.setMarqueeRepeatLimit(-1);
            return true;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            return false;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return false;
        } catch (NullPointerException e) {
            e.printStackTrace();
            return false;
        }
    }
}

【问题讨论】:

    标签: java android android-actionbar android-5.0-lollipop android-toolbar


    【解决方案1】:

    Toolbar 类中声明的TextView 字段名称和工具栏的选取框标题中获取标题TextView 对象。

        TextView titleTextView = null;
    
        try {
            Field f = toolbar.getClass().getDeclaredField("mTitleTextView");
            f.setAccessible(true);
            titleTextView = (TextView) f.get(toolbar);
    
            titleTextView.setEllipsize(TruncateAt.MARQUEE);
            titleTextView.setFocusable(true);
            titleTextView.setFocusableInTouchMode(true);
            titleTextView.requestFocus();
            titleTextView.setSingleLine(true);
            titleTextView.setSelected(true);
            titleTextView.setMarqueeRepeatLimit(-1);
    
        } catch (NoSuchFieldException e) {
        } catch (IllegalAccessException e) {
        }
    

    【讨论】:

    • 得到这个:尝试在空对象引用上调用虚拟方法 'void android.widget.TextView.setEllipsize(android.text.TextUtils$TruncateAt)'
    • 在 android 7.0 上为我工作。
    • 在没有选项菜单但没有选项菜单的情况下工作
    【解决方案2】:

    尝试在 Toolbar 中放置一个 TextView:

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize" >
    
        <TextView
            android:id="@+id/toolbar_title"
            android:text="This will run the marquee animation forever"
            android:textSize="@dimen/abc_text_size_title_material_toolbar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:marqueeRepeatLimit="marquee_forever"
            android:scrollHorizontally="true"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:singleLine="true" />
    
    </android.support.v7.widget.Toolbar>
    

    然后,将 Toolbar 用作 ActionBar 并清除/禁用其标题:

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setTitle(null); // or, setDisplayShowTitleEnabled(false)
    

    【讨论】:

    • 不错的代码,但在 kitkat 及更高版本中不起作用。还有什么我可以做的吗?
    【解决方案3】:

    为 Title 和 Subtitle TextViews 设置 MARQUEE 的 Kotlin 解决方案(它只是在 Toolbar 中找到所有 TextViews):

    findViewById<Toolbar>(R.id.action_bar)?.let {
        setToolbarTextViewsMarquee(it)
    }
    
    fun setToolbarTextViewsMarquee(toolbar: Toolbar) {
        for (child in toolbar.children) {
            if (child is TextView) {
                setMarquee(child)
            }
        }
    }
    
    fun setMarquee(textView: TextView) {
        textView.ellipsize = TextUtils.TruncateAt.MARQUEE
        textView.isSelected = true
        textView.marqueeRepeatLimit = -1
    }
    

    因此没有必要将工具栏视图(android.support.v7.widget.Toolbarandroidx.appcompat.widget.Toolbar)添加到 xml 布局中

    您可以使用 AppCompat 主题自动添加的默认工具栏:

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    

    【讨论】:

      【解决方案4】:

      最终想通了,这是因为,据我了解,设置字幕的 TextView 需要先被选中,然后才能真正开始字幕。我更新了我在问题中发布的 MarqueeToolbar 类,可以在此 Gist 中找到:https://gist.github.com/InsanityOnABun/95c0757f2f527cc50e39

      【讨论】:

        【解决方案5】:

        创建自定义工具栏并应用标题和副标题选框效果:

        public class MarqueeToolbar extends Toolbar {
        
        TextView title, subTitle;
        
        public MarqueeToolbar(Context context) {
            super(context);
        }
        
        public MarqueeToolbar(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
        
        public MarqueeToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
        
        @Override
        public void setTitle(CharSequence title) {
        
                reflected = reflectTitle();
        
            super.setTitle(title);
            selectTitle();
        }
        
        @Override
        public void setTitle(int resId) {
            if (!reflected) {
                reflected = reflectTitle();
            }
            super.setTitle(resId);
            selectTitle();
        }
        
        boolean reflected = false;
        private boolean reflectTitle() {
            try {
                Field field = Toolbar.class.getDeclaredField("mTitleTextView");
                field.setAccessible(true);
                title = (TextView) field.get(this);
                title.setEllipsize(TextUtils.TruncateAt.MARQUEE);
                title.setMarqueeRepeatLimit(-1);
                return true;
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
                return false;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return false;
            } catch (NullPointerException e) {
                e.printStackTrace();
                return false;
            }
        }
        
        public void selectTitle() {
            if (title != null)
                title.setSelected(true);
        }
        
        // ------------ for Subtitle ----------
        
        @Override
        
        public void setSubtitle(CharSequence subTitle) {
            if (!reflectedSub) {
                reflectedSub = reflectSubTitle();
            }
            super.setSubtitle(subTitle);
            selectSubtitle();
        }
        
        
        @Override
        public void setSubtitle(int resId) {
            if (!reflected) {
                reflectedSub = reflectSubTitle();
            }
            super.setSubtitle(resId);
            selectSubtitle();
        }
        
        boolean reflectedSub = false;
        private boolean reflectSubTitle() {
            try {
                Field field = Toolbar.class.getDeclaredField("mSubtitleTextView");
                field.setAccessible(true);
                subTitle = (TextView) field.get(this);
                subTitle.setEllipsize(TextUtils.TruncateAt.MARQUEE);
                subTitle.setMarqueeRepeatLimit(-1);
                return true;
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
                return false;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return false;
            } catch (NullPointerException e) {
                e.printStackTrace();
                return false;
            }
        }
        
        public void selectSubtitle() {
            if (subTitle != null)
                subTitle.setSelected(true);
        }
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-13
          • 2012-08-30
          • 2014-12-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多