#はじめに
スクロールするとヘッダーに収納されるマーキーのタイトルを作りたくて始めた結果、力技で実装することにしました。
やりたいこと
スクロールすると左画像の[ハチワレ女王]が右画像のようにヘッダーに収納される
__力技に至った経緯__ 1. CollapsingToolbarLayout + Toolbarで実装しよう! > Toolbarにマーキーがない 2. マーキーするToolbarをカスタムビューで作ろう! > リフレクションでTextView引っこ抜いてマーキーする設定にしたけどマーキーしない
↓ とりあえず実装させたい! 力技でやろう!
3. collapseModeをpinにしたTextViewで作ろう!
> スクロール中の動作が気に入らない
4. AppBarLayout.OnOffsetChangedListenerを使ってpaddingの高さを調整することで再現しよう!
>できた!!!
この記事では3と4について記述します。
#CollapsingToolbarLayout + TextView
CollapsingToolbarLayoutの中にTextViewを配置し、collapseModeをpinにして再現させる。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/clear">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="50dp"
android:background="@color/clear"
app:collapsedTitleGravity="center"
app:collapsedTitleTextAppearance="@style/collapsedTitle"
app:expandedTitleGravity="top|start"
app:expandedTitleMarginTop="65dp"
app:expandedTitleTextAppearance="@style/expandedTitle"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:layout_marginTop="70dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/cat"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="320dp"
android:layout_marginHorizontal="30dp"
android:singleLine="false"
android:gravity="center"
android:textSize="15sp"
android:text="@string/expo"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/white"
app:layout_collapseMode="pin">
<com.google.android.material.button.MaterialButton
android:id="@+id/back_button"
style="@style/Widget.MaterialComponents.Button.TextButton.Icon"
android:layout_width="30dp"
android:layout_height="match_parent"
app:iconGravity="start"
app:icon="@drawable/ic_back"
app:iconTint="@color/black" />
</FrameLayout>
<com.example.MarqueeTextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
android:layout_marginTop="270dp"
android:paddingHorizontal="50dp"
android:gravity="center"
android:textSize="20sp"
android:text="ハチワレ女王"
app:layout_collapseMode="pin"/>
<TextView
android:id="@+id/screen_title"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="50dp"
android:gravity="center"
android:text="お猫様と配下一覧"
app:layout_collapseMode="parallax"/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
CollapsingToolbarLayout内にタイトルより下の位置にもtextが配置されているのでスクロール中文字が被ってしまった。
また、このデザインでは問題なかったが、左右の移動もできないためデフォルト位置とヘッダーの時の位置がずれる場合に使用できなかった。
#AppBarLayout.OnOffsetChangedListener + padding制御
AppBarLayoutの高さの変化を検知してくれるAppBarLayout.OnOffsetChangedListenerを使用し高さに応じてpaddingを調整する。
レイアウトは変更部分のみ記載します。
<!-- old: android:layout_marginTop="270dp"
new: android:paddingTop="280dp" -->
<com.example.MarqueeTextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
android:paddingTop="280dp"
android:paddingHorizontal="50dp"
android:gravity="center"
android:textSize="20sp"
android:text="ハチワレ女王"
app:layout_collapseMode="pin"/>
class MainFragment : Fragment() {
private lateinit var listener: AppBarLayout.OnOffsetChangedListener
private lateinit var binding: FragmentFirstBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return FragmentFirstBinding.inflate(inflater, container, false).apply {
val strList = mutableListOf("ヒト1: お気に入り", "ヒト2: ご飯係", "ヒト3: おやつ係", "ヒト4: ブラッシング係",
"ヒト5: 爪を切る不届きもの", "ヒト6: お気に入りその2", "ヒト7: ご飯係その2", "ヒト8: おやつ係その2")
list.adapter = ListAdapter(strList)
list.layoutManager = LinearLayoutManager(context)
//paddingの制御
val paddingHorizontal = resources.getDimensionPixelSize(R.dimen.padding_horizontal) //50dp
val defaultPaddingTop = resources.getDimensionPixelSize(R.dimen.padding_top) //280dp
listener = AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
val paddingTop = if (defaultPaddingTop + verticalOffset > 0) defaultPaddingTop + verticalOffset
else 0
name.setPadding(paddingHorizontal, paddingTop, paddingHorizontal, 0)
}
appbarLayout.addOnOffsetChangedListener(listener)
}.root
}
override fun onPause() {
super.onPause()
binding.appbarLayout.removeOnOffsetChangedListener(listener)
}
}
AppBarLayout.OnOffsetChangedListenerのverticalOffsetはデフォルトの状態が0で上方向にスクロールするとマイナス値になる。
あらかじめ設定したデフォルトの値にverticalOffsetを加算することでスクロールと同じようにタイトルが動くようになる。
マーキーするようにしたTextViewのCustomViewを使っているのでもちろんマーキーもする。
#最後に
今回は動きを力技で実装することができましたが、マーキーするToolbarも含めもっと上手く実装できる方法があったのではないかと思います。
自分の知識の少なさを再認識しもっと勉強しようと思えた良い機会になりました。
マーキーするToolbarについてはしっかり動作するものが作れるまで頑張ろうと思います。