2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Android】PullToRefreshBoxを使って引っ張って更新機能を実装する

Posted at

はじめに

アプリでよく見る引っ張って更新する機能ですが、Material3のPullToRefreshBox使って引っ張って更新機能を作ってみたいと思います。

PullToRefreshBoxの詳しい内容はこちらを参照ください。

PullToRefreshBoxを使って引っ張って更新機能を実装

PullToRefreshBoxでUIを作成

以下は単純なリストを表示するComposableです。
リストを下に引っ張ると更新機能が走るように、PullToRefreshBoxを使っています。

sample.kt
class MainActivity : ComponentActivity() {
    private val viewModel: ListScreenViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            PullToRefreshBoxSampleTheme {
                val uiState by viewModel.uiState.collectAsState()
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                        ListScreen(uiState, viewModel::refresh, innerPadding)
                }
            }
        }
    }
}


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ListScreen(
    uiState: ListScreenViewModel.UiState,
    refreshAction: () -> Unit,
    padding: PaddingValues
) {
    val list = (uiState as? ListScreenViewModel.UiState.Success)?.data ?: emptyList()
    val isRefreshing = (uiState as? ListScreenViewModel.UiState.Success)?.isRefreshing ?: false

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = {
            refreshAction()
        }
    ) {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize()
                .padding(padding)
        ) {
            items(list.size) {
                Text("Item $it")
            }
        }
    }
}

PullToRefreshBoxの引数で設定している2つについて解説します。

  • isRefreshing:リフレッシュが行われているかどうかを設定します。trueが設定されている場合、リストの上にローディングインジケーターが表示されます。
  • onRefresh:引っ張るアクションを実行した時に動く処理を設定します。

ViewModelを用意

UiStateを用意し、表示するデータとリフレッシュの実行中かどうかを設定するisRefreshingを持たせています。

sample.kt
class ListScreenViewModel : ViewModel() {
    sealed interface UiState {
        data class Success(val data: List<String>, val isRefreshing: Boolean) : UiState
    }

    private val _uiState = MutableStateFlow<UiState>(UiState.Success(listOf("1", "2", "3"), false))
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()

    // 引っ張って更新時に動かす処理
    fun refresh() {
        val currentUiState = _uiState.value
        if (currentUiState is UiState.Success) {
            _uiState.value = currentUiState.copy(isRefreshing = true)
        }

        viewModelScope.launch {
            delay(2000)
            val currentData = (currentUiState as UiState.Success).data
            val newData = currentData + (currentData.size + 1).toString()
            _uiState.value = UiState.Success(newData, false)
        }
    }
}

refresh関数の中身について解説します。
画面で引っ張って更新が実行されたら、表示データは保持しつつ、isRefreshingのフラグをtrueに設定します。
こうすることで、画面にはローディングインジケーターが表示されるようになります。

isRefreshingをtrueに設定した後、coroutineを立ち上げてリストの更新処理を実行しています。
今回はサンプル処理として、2秒遅延後にリストのデータを一つ増やすような更新処理をしています。
更新処理の最後に、uiStateに更新後のデータとisRefreshingのフラグをtrueに設定してリフレッシュ処理を終了します。

実際に動かしてみた時の動き

前述のコードで実際に動かしてみた時の動きになります。
画面を下に引っ張るとローディングが表示され、リストが更新・ローディング非表示となっています。
サンプル.gif

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?