관리 메뉴

나만을 위한 블로그

[Android Compose] Swipe Refresh 구현하기 본문

Android/Compose

[Android Compose] Swipe Refresh 구현하기

참깨빵위에참깨빵 2023. 8. 20. 01:45
728x90
반응형

여러 아이템들이 표시되는 화면의 맨 위에서 계속 스크롤을 시도하면 위에서 재활용 아이콘이 나오고, 이 때 손을 떼면 화면에 표시되는 데이터들을 새로고침하는 기능이 있다. 안드로이드에선 이를 Swipe Refresh라고 부른다. 이 포스팅에선 컴포즈에서 이걸 구현한 예제 코드를 확인한다.

먼저 이 예제에서 사용한 라이브러리는 현재 deprecated 되었음에 주의한다. 그래서 실제로 사용한다면 이 라이브러리 대신 다른 걸 사용하는 게 맘 편할 것이다. 여기선 예제 포스팅이기 때문에 그냥 사용했다.

 

implementation("com.google.accompanist:accompanist-swiperefresh:0.27.0")

 

그리고 뷰모델을 만든다.

 

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

class RefreshViewModel: ViewModel() {

    private val _isLoading = MutableStateFlow(false)
    val isLoading = _isLoading.asStateFlow()

    init {
        loadSomething()
    }

    fun loadSomething() = viewModelScope.launch {
        _isLoading.value = true
        delay(1000L)
        _isLoading.value = false
    }

}

 

뷰모델의 인스턴스가 처음 만들어질 때 수행할 코드를 init 블록에 작성한다. init 블록에서 수행할 것은 로딩 중임을 표시하기 위해 사용할 MutableStateFlow 변수에 true를 대입하고, 1초 뒤 false 값으로 덮어씌우는 간단한 함수다. 실제라면 delay() 대신 API를 통해 서버에서 값을 받아오고, 필요하다면 filter, map 등으로 데이터를 조작하거나 합치는 등의 컬렉션 함수를 사용할 수 있을 것이다.

액티비티에서 사용은 아래와 같이 한다.

 

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.composeprac.swipe_refresh.ui.theme.ComposePracTheme
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.SwipeRefreshIndicator
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState

class SwipeRefreshTestActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposePracTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    val viewModel = viewModel<RefreshViewModel>()
                    val isLoading by viewModel.isLoading.collectAsState()
                    val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isLoading)

                    SwipeRefresh(
                        state = swipeRefreshState,
                        onRefresh = viewModel::loadSomething,
                        indicator = { state, refreshTrigger ->
                            SwipeRefreshIndicator(
                                state = state,
                                refreshTriggerDistance = refreshTrigger,
                                backgroundColor = Color.Blue,
                                contentColor = Color.White
                            )
                        }
                    ) {
                        LazyColumn(
                            modifier = Modifier.fillMaxSize()
                        ) {
                            items(100) {
                                Text(
                                    text = "테스트",
                                    modifier = Modifier
                                        .fillMaxWidth()
                                        .padding(32.dp)
                                )
                            }
                        }
                    }
                }
            }
        }
    }
}

 

뷰모델의 참조변수를 선언한다. 이 과정에서 init 블록에 작성했던 loadSomething()이 호출되고, 이 액티비티가 실행되자마자 리프레시 처리가 이뤄진다.

SwipeRefresh 컴포저블 함수의 state로는 초기화된 rememberSwipeRefreshState 변수를 넣고, onRefresh에는 뷰모델의 함수를 집어넣는다. 특이한 게 있다면 뷰모델 변수 뒤에 콜론을 2개 사용한 것이다. 이것을 함수 참조(Function Reference)라고 부른다.

간단히 말하면 함수를 값처럼 다룰 수 있게 해주는 기능으로 만약 람다가 오직 하나의 함수 or 프로퍼티만 호출한다면 위 코드처럼 이중 콜론을 써서 함수 참조를 사용할 수 있다. 함수 참조를 쓰지 않았다면 onRefresh에 넘기는 함수는 아래와 같은 형태가 된다.

 

onRefresh = { viewModel.loadSomething() }

 

더 알아보고 싶은 사람은 블로그나 공식 문서에 설명이 잘 되어 있으니 그것들을 찾아보자.

본론으로 돌아와서, indicator 매개변수를 쓰면 리프레시 처리가 이뤄지는 동안 표시할 아이콘의 배경색, 화살표 아이콘의 색을 설정할 수 있다.

이 예제 코드를 그대로 실행해서 리프레시하면 아래와 같은 로딩 아이콘이 표시된다. 취향껏 바꿔주면 된다.

 

 

반응형
Comments