1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Kotlin]スクロールするとヘッダーに収納されるTextViewを力技で作成してみた。[Android]

Last updated at Posted at 2022-02-15

#はじめに
スクロールするとヘッダーに収納されるマーキーのタイトルを作りたくて始めた結果、力技で実装することにしました。

やりたいこと
無題94.png
スクロールすると左画像の[ハチワレ女王]が右画像のようにヘッダーに収納される


__力技に至った経緯__ 1. CollapsingToolbarLayout + Toolbarで実装しよう!  > Toolbarにマーキーがない 2. マーキーするToolbarをカスタムビューで作ろう!  > リフレクションでTextView引っこ抜いてマーキーする設定にしたけどマーキーしない

↓ とりあえず実装させたい! 力技でやろう!

3. collapseModeをpinにしたTextViewで作ろう!
> スクロール中の動作が気に入らない
4. AppBarLayout.OnOffsetChangedListenerを使ってpaddingの高さを調整することで再現しよう!
>できた!!!

この記事では3と4について記述します。

#CollapsingToolbarLayout + TextView
CollapsingToolbarLayoutの中にTextViewを配置し、collapseModeをpinにして再現させる。

layout_main.xml
<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が配置されているのでスクロール中文字が被ってしまった。
また、このデザインでは問題なかったが、左右の移動もできないためデフォルト位置とヘッダーの時の位置がずれる場合に使用できなかった。

qiita1.gif

#AppBarLayout.OnOffsetChangedListener + padding制御
AppBarLayoutの高さの変化を検知してくれるAppBarLayout.OnOffsetChangedListenerを使用し高さに応じてpaddingを調整する。

レイアウトは変更部分のみ記載します。

layout_main.xml
             <!--   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"/>
MainFragment.kt
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)
    }
}

qiita2.gif

AppBarLayout.OnOffsetChangedListenerのverticalOffsetはデフォルトの状態が0で上方向にスクロールするとマイナス値になる。
あらかじめ設定したデフォルトの値にverticalOffsetを加算することでスクロールと同じようにタイトルが動くようになる。

qiita3.gif
マーキーするようにしたTextViewのCustomViewを使っているのでもちろんマーキーもする。

#最後に
今回は動きを力技で実装することができましたが、マーキーするToolbarも含めもっと上手く実装できる方法があったのではないかと思います。
自分の知識の少なさを再認識しもっと勉強しようと思えた良い機会になりました。
マーキーするToolbarについてはしっかり動作するものが作れるまで頑張ろうと思います。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?