Tech Notes

scrollView.fullScroll(View.FOCUS_DOWN)で最後までスクロールしない問題

起こる条件とか詳しくはよく分かんないけど、なんか発生してなんか解決したので備忘録。

チャットっぽいアプリを作ろうとしているのですが、そのUI部分を以下のような感じにしたところ、問題が起こりました。

まあまず大雑把にこんな感じのレイアウトを組んだわけですよ。(細かい部分は省略)

<RelativeLayout
    android:id="@+id/activity_main">

    <ScrollView
        android:id="@+id/scrollView">

        <LinearLayout
            android:id="@+id/talk">
        </LinearLayout>

    </ScrollView>

    <Button
        android:id="@+id/button"
        android:text="update"/>

</RelativeLayout>

ScrollViewの中にLinearLayoutが入ってて、そこにTextViewを動的に追加していくことで、ハングアウトとかTwitterのDMとかみたいなトークを表現するっていう感じです。それで下部のボタンをタップでリロード、みたいな。

とりあえずデモとして、ボタンを押したらランダムな数字のテキストが3つ追加されて最下部までスクロールっていうのを作りました。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(OnClickBtn);
    }

    private View.OnClickListener OnClickBtn = new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            AddText(Integer.toString(new Random().nextInt()));
            AddText(Integer.toString(new Random().nextInt()));
            AddText(Integer.toString(new Random().nextInt()));

            scrollView.fullScroll(View.FOCUS_DOWN);
        }
    };

    void AddText(String text)
    {
        LinearLayout tl = (LinearLayout)findViewById(R.id.talk);
        TextView tv = new TextView(this);
        tv.setText(text);
        tl.addView(tv);
    }

}

これまた細かいところは省いてますがだいたいこんな感じです。

これを実行するじゃありませんか。するとどうなるでしょう。

テキスト追加→最下部までスクロールとやっているのでそうなるべき筈なのに、「テキストを追加する前の状態における最下部」までしかスクロールされないのです。なにがfullScrollだよダンカンバカヤロウ!

さてそんなfullScroll()な挙動ですが、StackOverFlowに似たような現象に関する内容の記事があったため、それに従った結果どうにかちゃんと動くようになりました。どうやらこう書き換えれば意図したとおりに動作するようです。

scrollView.fullScroll(View.FOCUS_DOWN);

この部分を

scrollView.post(new Runnable() 
{
    public void run() 
    {
        scrollView.fullScroll(View.FOCUS_DOWN);
    }
});

こう

なんと別スレッドからやるようにするだけのようです。Androidのカーネルの中身とかまるで分らないのでどういう理由でこうしないといけないのか謎なんですが、とにかくこれで動作しました。同様の現象に悩まされている方はご査収ください。