Retrofit

grandle import以下內容

1
2
3
4
5
6
def retrofit = "2.9.0"
implementation "com.squareup.retrofit2:retrofit:$retrofit"
implementation "com.squareup.retrofit2:converter-gson:$retrofit"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit"
def moshi_version = "1.13.0"
implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
object Network {
    //文件位置:https://docs.apipost.cn/preview/1a28e17fa3c8f473/16838456ae6dc5c7
    private const val baseUrl =
        "https://mock.apipost.cn/app/mock/project/ced69cf2-9206-4a42-895e-dd7442a888df/"
    //創建一個Retrofit builder
    private val retrofit = Retrofit
        .Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(
						//建立Moshi工廠
            MoshiConverterFactory.create(
                Moshi.Builder()
                    .add(KotlinJsonAdapterFactory())
                    .build()
            )
        ).build()
    //傳進來什麼類型,返回就是什麼類型
    fun <T> createService(clazz: Class<T>):T {
        return retrofit.create(clazz)
    }
}

圖中先建立ArticleService
先建一個interface 在Service的目錄下
img

生成伴生物件
img
img

進到https://docs.apipost.cn/preview/1a28e17fa3c8f473/16838456ae6dc5c7 點進文章列表
使用/article/list
參數有pageOffset跟pageSize,如下圖
img

1
2
3
4
5
@GET("article/list")
suspend fun list(
    @Query("pageOffset") pageOffset: Int,
    @Query("pageSize") pageSize: Int
)

img

建立基礎的Response

1
2
3
4
5
package com.example.project1.model.entity
open class BaseResponse {
    var code:Int = -1
    var message:String = ""
}

img

建立繼承的Response img

1
2
3
4
5
6
7
8
9
10
11
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class ArticleEntity(
    val title: String,
    var source: String,
    @Json(name = "time")
    var timestamp: String
)
//因為data有可能為空,所以類型設空值List<ArticleEntity>?
data class ArticleListResponse(val data: List<ArticleEntity>?):BaseResponse()

img

回到Article Service

繼承:ArticleListResponse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import com.example.project1.model.entity.ArticleListResponse
import retrofit2.http.GET
import retrofit2.http.Query
interface ArticleService {
    @GET("article/list")
    suspend fun list(
        @Query("pageOffset") pageOffset: Int,
        @Query("pageSize") pageSize: Int
    ): ArticleListResponse

    companion object {
        //創建Home Service的實例
        fun instance(): ArticleService {
            return Network.createService(ArticleService::class.java)
        }
    }
}

img
在viewmodel中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class ArticleViewModel : ViewModel() {
    private val articleService = ArticleService.instance()
    val pageSize = 10 //每頁10筆
    private var pageOffset = 1 //預設從第1頁讀取
    var list by mutableStateOf(
        listOf(
   
        ArticleEntity(
            title = "test3",
            source = "source3",
            timestamp = "2023-01-01"
        ),
        ArticleEntity(
            title = "test4",
            source = "source4",
            timestamp = "2023-01-01"
        )
    )
    )
        private  set //不讓人修改

    var listLoaded by mutableStateOf(false)
    private set

    suspend fun fetchArticleList(){
       val res = articleService.list(pageOffset = pageOffset, pageSize = pageSize)
        if(res.code == 0 && res.data != null) {
            list = res.data
            listLoaded = true //是否載入完畢
        }
    }
}

在screen畫面使用

在LaunchedEffect使用(fetchArticleList) img 因為有”是否載入完畢”,要做placeholder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
fun ArticleItem(article: ArticleEntity, loaded: Boolean, modifier: Modifier = Modifier) {
    Column(modifier = modifier.padding(8.dp)) {
        Text(
            article.title,
            color = Color(0xFF333333),
            fontSize = 16.sp,
            maxLines = 2,
            overflow = TextOverflow.Ellipsis,
            modifier = Modifier
                .padding(bottom = 8.dp)
                .placeholder(
                    visible = !loaded,
                    color = Color.Gray,
                    highlight = PlaceholderHighlight.shimmer(highlightColor = Color.Gray)
                )
        )
        Row(horizontalArrangement = Arrangement.SpaceBetween,
            modifier = Modifier
                .fillMaxWidth()
                .placeholder(
                    visible = !loaded,
                    color = Color.Gray,
                    highlight = PlaceholderHighlight.shimmer(highlightColor = Color.Gray)
                )) {
            Text(
                text = "來源:${article.source}",
                color = Color(0xFF999999),
                fontSize = 10.sp,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
            Text(text = article.timestamp)
        }
        Spacer(Modifier.height(8.dp))
        Divider()
    }
}

在呼叫ArticleItem時添加loaded

1
2
3
4
5
6
7
8
9
10
11
if (vm.showArticleList) {
  //列表
  items(articleViewModel.list) { article ->
      ArticleItem(article,
          articleViewModel.listLoaded,
          modifier = Modifier.clickable {
              //Log.d("======","here")
              onNavigateToArticle.invoke()
          })
  }
}

img

results matching ""

    No results matching ""