TextViewに表示されるテキストを指定行位置へスクロールさせる方法(Android Studio Kotlin)

,

TextViewに改行した複数行のテキストを表示して、全てのテキスト行がTextViewに収まらないので、縦方向にスクロールできるようにした場合に、指定した行がTextViewの中ほどに来るようにスクロールしたい場合の対処方法を示します。

1.対処例のアプリ画面動画

行番号をラインNo入力欄に入力すると、その指定した行のテキスト行がTextViewの縦方向の真ん中辺りに来るようにスクロールするアプリの動作例です。また、このアプリでは指定した行のテキストの色を黄色に変更しています。

2.開発環境等

開発ツールバージョン: Android Studio Meerkat Feature Drop 2024.3.2 Patch1
使用言語: Kotlin

3.TextViewのスクロール位置を指定する方法

layoutのxmlファイルの記述で、TextViewの部分を<ScrollView…>と</ScrollView>で挟むことによってTextViewのスクロールが出来るようになります。

ScrollViewのidをscrollView1、TextViewのidをtextView1とした時、下に示すコードのようにTextViewの1ラインの高さ(textView.lineHeight)と、ScrollViewの高さ(scroll.height)から、対象行のScrollView上の縦方向位置を求めて、smoothScrollTo()メソッドを使用してスクロールを実効します。

下のコードでは、lineNoがスクロールして、中央付近に表示したい行の番号値です。(1~)

val textView = findViewById<TextView>(R.id.textView1)
val scroll = findViewById<ScrollView>(R.id.scrollView1)

//対象行が真ん中になるようにTextViewをスクロール
val lineHeightPixel = textView.lineHeight   //TextViewの1ラインの高さ(Pix)
val scrollViewPixel = scroll.height         //scrollViewの高さ(Pix)

val yPos = lineNo * lineHeightPixel - scrollViewPixel / 2   //縦位置計算(Pixel)
scroll.smoothScrollTo(0, yPos)          //スクロール実施

4.TextViewの対象部分の文字色を変える方法

本アプリでは指定した行の文字列の色を変更して、その行が真ん中に来るようにスクロールしています。ここで、TextViewに表示する文字列の一部分の文字色を変える方法に関して記します。

具体的方法は、色を変えたい文字列部分を<font color–>と</color>のタグで挟み、それをString型からSpanned型に変換してTextViewにセットします。

具体的にはテキストの色を変えたい文字列部分を
<font color=”色名称”>と</font>で挟みます。

(例)
val text = “ABCDEFGHI\r\n”
の”CDEF”のみ赤字としたい場合は
val text = “AB<font color=\”red\”>CDEF</font>GHI<br>”
とします。

ここで、元のテキストの”\r\n”は改行ですが、代わりに<br>タグに変更します。

このString型のtextをHtml.fromHtml()メソッドを使用して、マークアップオブジェクトが添付されているテキストであるSpanned型に変換して、対象となるTextViewにsetText()メソッドでセットします。

以下にコード例を示します。

//------部分的に文字色を変えてTextViewに表示------------
val text: String = "部分的に色を変えて表示<br>" +
       "<font color=\"red\">ABCDEFGHIJKLMNOPQRSTUVWXYZ</font><br>" +
       "<font color=\"green\">1234567890123456789012345678</font><br>" +
       "abcdefgh<font color=\"blue\">ijklmno</font>pqrstuvwxyz<br>" +
       "98<font color=\"yellow\">7654321098</font>76543210986543<br>"

val tView1 = findViewById<TextView>(R.id.textView1)
tView1.setText(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY))

5.アプリ作成例

5-1.画面レイアウト

図1のように部品を配置した場合の例を示します。(ConstraintLayout使用)

図1.部品配置図

5-2.画面レイアウトのxmlコード

レイアウト(app→res→layout→activity_main.xml)のXMLコードを以下に示します。11行目から29行目がスクロール可能なTextViewの部分です。
TextViewの部分を<ScrollView…>と</ScrollView>で挟むことによって、TextViewのスクロールが可能になります。
<ScrpllView….>タグ内のandroid:scrollbars=”vertical”は縦方向のスクロールバーを表示します。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/constraintLayout1"
    tools:context=".MainActivity">


    <ScrollView
        android:id="@+id/scrollView1"
        android:layout_width="340dp"
        android:layout_height="150dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="40dp"
        android:fillViewport="true"
        android:scrollbars="vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/textView1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#00BCD4"
            android:textColor="#000000" />

    </ScrollView>

    <EditText
        android:id="@+id/editText1"
        android:layout_width="132dp"
        android:layout_height="43dp"
        android:layout_marginStart="44dp"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:inputType="text"
        android:text="1"
        app:layout_constraintStart_toEndOf="@+id/textView2"
        app:layout_constraintTop_toBottomOf="@+id/scrollView1" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="88dp"
        android:layout_marginTop="16dp"
        android:text="ラインNo."
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/scrollView1" />
</androidx.constraintlayout.widget.ConstraintLayout>

5-3.Kotlinプログラムコード

今回作成したアプリのKotlin (MainActivity.kt)のプログラムコードを以下に示します。
アプリの動作はTextViewに改行した複数行のテキストを表示します。ラインNo.のEditTextに数字を入力すると、その数字に対応した行がTextViewの縦方向真ん中付近に表示され、その行の文字列の色が黄色に変わります。

class MainActivity : AppCompatActivity() {
    private lateinit var dispText: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //------表示テキスト---------------------
        dispText = "No1.ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>" +
                "No2. 1234567890123456789012345678<br>" +
                "No3. あいうえおかきくけこさしすせそたち<br>" +
                "No4. 春夏秋冬、春夏秋冬、春夏秋冬<br>" +
                "No5. 1234567890123456789012345678<br>" +
                "No6. ABCDEFGHIJKLMNOPQRSTUVWXWZ<br>" +
                "No7. 98765432109876543210986543<br>" +
                "No8. 1234567890123456789012345678<br>" +
                "No9. abcdefghijklmnopqrstuvwxyz<br>" +
                "No10. 98765432109876543210986543<br>" +
                "No11. 1234567890123456789012345678<br>" +
                "No12. ABCDEFGHIJKLMNOPQRSTUVWZYZ<br>" +
                "No13. あいうえおかきくけこさしすせそたち<br>" +
                "No14. 1234567890123456789012345678<br>" +
                "No15. abcdefghijklmnopqrstuvwxyz<br>" +
                "No16. 98765432109876543210986543<br>" +
                "No17. 98765432109876543210986543<br>" +
                "No18. 1234567890123456789012345678<br>" +
                "No19. あかさたなはまやらわ<br>" +
                "No20. 98765432109876543210986543<br>" +
                "No21. 1234567890123456789012345678<br>" +
                "No22. abcdefghijklmnopqrstuvwxyz<br>" +
                "No23. 98765432109876543210986543<br>" +
                "No24. abcdefghijklmnopqrstuvwxyz<br>" +
                "No25. 98765432109876543210986543<br>"


        val tView = findViewById<TextView>(R.id.textView1)
        //文字列をテキストボックスに表示
        tView.setText(Html.fromHtml(dispText, Html.FROM_HTML_MODE_LEGACY))

        //EditTextにライン番号を入力した時のリスナ登録
        val editText = findViewById<EditText>(R.id.editText1)
        editText.setOnEditorActionListener(editorAction)
    }

    //EditTextに入力した終了時のリスナ
    private val editorAction: TextView.OnEditorActionListener
        = object: TextView.OnEditorActionListener {
        override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                val editText = findViewById<EditText>(R.id.editText1)
                val no = (editText.text).toString()   //ラインNo入力値読込
                var lineNo: Int = 0
                try {
                    lineNo = no.toInt()
                } catch (e: Exception) {
                    return true
                }

                if (lineNo <= 0) {   //error
                    return true
                }

                val textLen = dispText.length   //表示テキストの長さ

                var termPosLast: Int = 0    //前回検索の"<br>"位置
                var termPos: Int = 0        //今回検索の"<br>"位置
                //"<br>"の位置検索
                for (i in 0..(lineNo-1)) {
                    if (termPos >= textLen - 4 ) {  //error
                        return true
                    }
                    termPosLast = termPos       //前回検索の"<br>”の位置記憶
                    //今回検索の"<br>"位置記憶
                    termPos = dispText.indexOf("<br>", termPos + 4)
                }

                //-----全テキストを3個に分割-----
                //前のテキスト
                val preLine = dispText.substring(0, termPosLast + 4)//前のテキスト

                //対象テキストライン
                val thisLine = if (termPosLast == 0) {  //先頭行の時
                    dispText.substring(0, termPos)
                } else {    //先頭行以外の時
                    dispText.substring(termPosLast + 4, termPos)
                }

                //後のテキスト
                val laterLine = dispText.substring(termPos + 4) //後のテキスト

                //対象テキストラインのみ色を変えて構成
                val textNew: String = if (termPosLast == 0) {  //先頭行の時
                    "<font color=\"yellow\">" + thisLine + "</font>" + "<br>" + laterLine //対象行の色を変える
                } else {    //先頭行以外の時
                    preLine + "<font color=\"yellow\">" + thisLine + "</font>" + "<br>" + laterLine //対象行の色を変える
                }

                val textView = findViewById<TextView>(R.id.textView1)
                //TextViewに表示(Html形式使用)
                textView.setText(Html.fromHtml(textNew, Html.FROM_HTML_MODE_LEGACY))

                //対象行が真ん中になるようにTextViewをスクロール
                val scroll = findViewById<ScrollView>(R.id.scrollView1)
                val lineHeightPixel = textView.lineHeight   //TextViewの1ラインの高さ(Pix)
                val scrollViewPixel = scroll.height         //scrollViewの高さ(Pix)
                val yPos = lineNo * lineHeightPixel - scrollViewPixel / 2   //対象行が真ん中に表示
                scroll.smoothScrollTo(0, yPos)          //スクロール実施
            }
            return true
        }
    }
}

【プログラム説明】
9行目~33行目:
dispText = “No1.ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>” +…….
は、TextViewに表示する文字列の定義です。後ほどマークアップオブジェクトが添付されているテキストであるSpanned型に変換するため、改行は<br>タグを使用しています。

41行目~42行目:
val editText = findViewById<EditText>(R.id.editText1)
editText.setOnEditorActionListener(editorAction)
は、EditTextにソフトウェアキーボードを使用してライン番号を入力しアクションキーを押した時に呼び出されるリスナの登録です。

48行目~49行目:
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
は、EditTextに操作が加えられた時に呼び出されるメソッドです。

49行目の if (actionId == EditorInfo.IME_ACTION_DON)で、ソフトウェアキーボートのアクションキーを押した時かを判断しています。アクションキーを押した場合は、それ以降でEditTextからスクロールする行番号を読み出して、その行の色を変えてから、その行がTextViewの縦方向位置の真ん中付近に来るようにスクロールする処理を行っています。

92行目~96行目:
val textNew: String = if (termPosLast == 0) { //先頭行の時
“<font color=\”yellow\”>” + thisLine + “</font>” + “<br>” + laterLine
} else { //先頭行以外の時
preLine + “<font color=\”yellow\”>” + thisLine + “</font>” + “<br>” + laterLine
}

は、EditTextの入力で指定されたライン番号の行の文字列のフォントの色を黄色に変える処理です。色を変えたい文字列部分を<font color–>と</color>のタグで挟んでいます。

103行目~107行目:
val scroll = findViewById<ScrollView>(R.id.scrollView1)
val lineHeightPixel = textView.lineHeight
val scrollViewPixel = scroll.height
val yPos = lineNo * lineHeightPixel – scrollViewPixel / 2
scroll.smoothScrollTo(0, yPos)

は、指定した行がTextViewの縦方向の真ん中辺りに表示されるように、スクロールする部分です。
TextViewの1ラインの高さと、ScrollViewの高さから、対象行のScrollView上の縦方向位置を求めて、smoothScrollTo()メソッドを使用してスクロールを実効します。


PAGE TOP