やりたいこと
こういうの。
ツールバーのtitle部分の文字列が横に流れ続けている。
カスタムビューの作成
Toolbar
を継承した次のようなカスタムビューを作成する。
MarqueeToolbar.kt
package com.suihan74.utilities
import android.content.Context
import android.text.TextUtils
import android.util.AttributeSet
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
class MarqueeToolbar : Toolbar {
constructor(context: Context, attributeSet: AttributeSet? = null) :
super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) :
super(context, attributeSet, defStyleAttr)
/** タイトルの設定が完了しているか否か */
private var reflected: Boolean = false
/** タイトル部分のTextView */
private var titleTextView: TextView? = null
override fun setTitle(resId: Int) {
if (!reflected) {
reflected = reflectTitle()
}
super.setTitle(resId)
selectTitle()
}
override fun setTitle(title: CharSequence?) {
if (!reflected) {
reflected = reflectTitle()
}
super.setTitle(title)
selectTitle()
}
/** Viewが生成されたときに呼ばれる */
override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
super.onWindowFocusChanged(hasWindowFocus)
if (hasWindowFocus && !reflected) {
reflected = reflectTitle()
selectTitle()
}
}
/** タイトル部分のTextViewを設定 */
private fun reflectTitle() =
try {
val field = Toolbar::class.java.getDeclaredField("mTitleTextView").apply {
isAccessible = true
}
titleTextView = (field.get(this) as? TextView)?.apply {
ellipsize = TextUtils.TruncateAt.MARQUEE
marqueeRepeatLimit = -1 // forever
}
titleTextView != null
}
catch (e: Throwable) {
e.printStackTrace()
false
}
/** タイトル部分を選択 */
private fun selectTitle() {
titleTextView?.isSelected = true
}
}
参考:
A Marquee-able Android Toolbar. | InsanityOnABun / MarqueeToolbar.java | GitHub Gist
説明
要するに、Toolbar
のprivateフィールドであるタイトル部分のTextView
をリフレクションで強引にぶっこ抜いてきてmarquee用の設定を追加しているというわけ。(reflectTitle()
部分)
なお、onWindowFocusChanged()
をオーバーライドしているのは何らかの方法1でViewが生成完了して画面に表示されるまでの間にreflectTitle()
が実行されないとsetTitle()
が呼ばれるまでアニメーションが始まらないため。
(Activity.onCreate()
内でtoolbar.title = "長い文字列"
を記述してもView
のライフサイクル的な問題でreflectTitle()
内のfield.get(this)
に失敗する)
使用方法
activity_hoge.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.suihan74.utilities.MarqueeToolbar
android:id="@+id/toolbar"
app:layout_scrollFlags="enterAlways|scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.google.android.material.appbar.AppBarLayout>
...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
余談
「サブタイトルもmarqueeさせたいぜ」という欲張りさんは同じようにしてMarqueeToolbar
でmSubtitleTextView
をぶっこ抜いて設定すればいいんだと思う。
何らかの方法→ ViewのonStart/onPause - Qiita ↩︎