使用compose+paging3+jsoup實(shí)現(xiàn)雙色球數(shù)據(jù)分頁(yè)加載,并且添加了手勢(shì)動(dòng)作,既可拖動(dòng)也可縮放


雙色球走勢(shì)圖是比較大的,圖表可拖動(dòng)可縮放是很硬性的要求,而我在實(shí)現(xiàn)這個(gè)功能時(shí)又遇到了很大的困難,不過(guò)最終還是完成了,所以在此重新記錄一下。
1,首先在google官網(wǎng)中找到在compose中實(shí)施拖動(dòng)和縮放動(dòng)作的代碼
2,套用在這篇文章中:Compose+Paging3+Retrofit實(shí)現(xiàn)列表分頁(yè)加載
下面重新貼上主要的相關(guān)代碼

    implementation "dev.chrisbanes.accompanist:accompanist-coil:0.6.0"http://coil
    implementation 'org.jsoup:jsoup:1.13.1' //jsoup
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha02"
    implementation "androidx.paging:paging-runtime:3.0.0-beta02"
    implementation "androidx.paging:paging-compose:1.0.0-alpha08"

LotteryActivity

class LotteryActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
           LeftModeScreen()
        }
    }
}

LotteryViewModel

class LotteryViewModel: ViewModel() {
    val lotterys = Pager(PagingConfig(pageSize = 50)){
        LotteryPagingSource(LotteryRepository)
    }.flow.cachedIn(viewModelScope)
}

LotteryData

data class Lottery(
    val code :String,   //開(kāi)獎(jiǎng)號(hào)碼
    val issue :String,//期號(hào)
) : Serializable

LotteryRepository

object LotteryRepository {

    private const val TARGET_URL = "https://datachart.500.com/ssq/history/newinc/history.php"

    suspend fun getLotterys(page: Int): MutableStateFlow<List<Lottery>> {

        val lotterys = MutableStateFlow<List<Lottery>>(listOf())
        val lotteryList: MutableList<Lottery> = mutableListOf()

        withContext(Dispatchers.IO) {
            val doc =
                //"https://datachart.500.com/ssq/history/newinc/history.php?start=21001&end=21200"
                Jsoup.connect(TARGET_URL + "?start=${page}001&end=${page}200").get()
            val element: Element = doc.getElementById("tdata")
            val elements: Elements = element.select("tr.t_tr1")
            elements.forEach { item ->
                val aaa: Elements = item.select("td")
                if (!aaa.isNullOrEmpty()) {
                    val bbb = Lottery(
                        code = aaa[1].text() + "," +
                                aaa[2].text() + "," +
                                aaa[3].text() + "," +
                                aaa[4].text() + "," +
                                aaa[5].text() + "," +
                                aaa[6].text() + "  " +
                                aaa[7].text(),
                        issue = aaa[0].text()
                    )
                    lotteryList.add(bbb)
                }
            }
            lotterys.value = lotteryList
        }
        return lotterys
    }
}

LotteryPagingSource

class LotteryPagingSource(private val repository: LotteryRepository) :
    PagingSource<Int, Lottery>() {

    private val year = TimeUtils.nowTime.substring(2, 4).toInt()//取年份最后兩位 (2021 => 21)

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Lottery> {
        return try {
            val page = params.key ?: year//一個(gè)年份的開(kāi)獎(jiǎng)數(shù)據(jù)為一頁(yè)
            val response = repository.getLotterys(page)
            LoadResult.Page(
                data = response.value,
                prevKey = null,
                nextKey = if (page > 3) page - 1 else null//"3"為2003年,雙色球從2003年開(kāi)始的,
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, Lottery>): Int? {
        return null
    }
}

TimeUitls

object TimeUtils {

    val nowTime: String
        get() {
            val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.CHINESE)
            val date = Date(System.currentTimeMillis())
            return simpleDateFormat.format(date)
        }
}

下面是一些與compose相關(guān)的頁(yè)面代碼,由于添加了手勢(shì)動(dòng)作和數(shù)據(jù)加載錯(cuò)誤處理,代碼比較多,這里就不一一貼出來(lái)了,只貼主要的,部分缺少的可以在文章開(kāi)頭的那篇文章中找到,還有一些找不到的就自己實(shí)現(xiàn)吧。這是一個(gè)進(jìn)行中的項(xiàng)目,代碼根據(jù)需要隨時(shí)在變化。
Compose+Paging3+Retrofit實(shí)現(xiàn)列表分頁(yè)加載

@Composable
fun LeftModeScreen() {
    val showPop = remember { mutableStateOf(false) }
    Row(modifier = Modifier.fillMaxSize()) {
        LeftMenuColumn(showPop)//左側(cè)菜單欄
        SaleTrasformable{ LotteryList() }//右側(cè)走勢(shì)圖,可拖動(dòng),可縮放
    }
    if (showPop.value) {
        HandModePop(showPop)//切換左右手模式
    }
}

//使組件可拖動(dòng)、縮放和旋轉(zhuǎn)
@Composable
fun SaleTrasformable(
    content: @Composable () -> Unit
) {
    val offsetX = remember { mutableStateOf(0f) }
    val offsetY = remember { mutableStateOf(0f) }
    var width by remember { mutableStateOf(0f) }
    val scale = remember { mutableStateOf(1f) }
    val rotation = remember { mutableStateOf(0f) }
    val offset = remember { mutableStateOf(Offset.Zero) }
    val state = rememberTransformableState { zoomChange, offsetChange, rotationChange ->
        scale.value *= zoomChange//縮放
        offset.value += offsetChange//拖動(dòng)
        // rotation.value += rotationChange//旋轉(zhuǎn)
    }
    Column(
        modifier = Modifier
            .wrapContentWidth()
            // .background(Color(34, 43, 44, 50))
            .clipToBounds() //放大縮小時(shí),內(nèi)容限定在這個(gè)Column內(nèi)
            .graphicsLayer(
                scaleX = scale.value,
                scaleY = scale.value,
                // rotationZ = rotation.value,
                translationX = offset.value.x,
                translationY = offset.value.y
            )
            .transformable(state = state)
            .onSizeChanged { width = it.width.toFloat() }
            .offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
            .pointerInput(Unit) {
                forEachGesture {
                    awaitPointerEventScope {
                        val down = awaitFirstDown()
                        val change =
                            awaitHorizontalTouchSlopOrCancellation(down.id) { change, over ->
                                val originalX = offsetX.value
                                val newValue =
                                    (originalX + over).coerceIn(0f, width - 50.dp.toPx())
                                change.consumePositionChange()
                                offsetX.value = newValue
                            }
                        if (change != null) {
                            horizontalDrag(change.id) {
                                val originalX = offsetX.value
                                val newValue = (originalX + it.positionChange().x)
                                    .coerceIn(0f, width - 50.dp.toPx())
                                it.consumePositionChange()
                                offsetX.value = newValue
                            }
                        }
                    }
                }
            }
    ) {
        content()//內(nèi)容過(guò)多,用這種方式可另起一個(gè)Composable
    }
}

//妥善處理數(shù)據(jù)加載過(guò)程中的各種異常
@Composable
fun LotteryList(viewModel: LotteryViewModel = viewModel()) {

    val lotterys = viewModel.lotterys.collectAsLazyPagingItems()

    when (lotterys.loadState.refresh) {
        is LoadState.NotLoading -> //正常加載數(shù)據(jù)
            LazyColumn(
                Modifier.fillMaxSize(),
                reverseLayout = true//反轉(zhuǎn)數(shù)據(jù)
            ) {
                itemsIndexed(lotterys) { _, lottery ->
                    LotteryItem(lottery = lottery!!)
                }
                when (lotterys.loadState.append) {//劃到了數(shù)據(jù)邊界
                    is LoadState.Error -> item {//沒(méi)取不到新數(shù)據(jù),重試
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(8.dp),
                            verticalAlignment = Alignment.CenterVertically,
                            horizontalArrangement = Arrangement.Center,
                        ) {
                            Button(onClick = {
                                lotterys.retry()
                            }) {
                                Text(text = "重試")
                            }
                        }
                    }
                    is LoadState.Loading -> item {//加載中
                        LoadingPage()
                    }
                }
            }
        is LoadState.Error -> ErrorPage { lotterys.refresh() }//網(wǎng)絡(luò)錯(cuò)誤
        is LoadState.Loading -> LoadingPage()//加載中
    }
}

//將數(shù)據(jù)的各個(gè)字段填入到相應(yīng)的組件中
@Composable
fun LotteryItem(lottery: Lottery) {
   // DrawCanvas()
    Box(
        modifier = Modifier
            .padding(start = 16.dp, top = 8.dp, bottom = 8.dp)
        //.clickable(onClick = {})
    ) {
        Row(modifier = Modifier.fillMaxWidth()) {
            Column(
                modifier = Modifier.padding(start = 8.dp)
            ) {
                Text(
                    lottery.code,//開(kāi)獎(jiǎng)號(hào)碼
                    style = TextStyle(color = Color.Black, fontSize = 16.sp),
                    modifier = Modifier.padding(end = 8.dp),
                    maxLines = 1
                )
            }
            Column(
                modifier = Modifier.padding(start = 8.dp)
            ) {
                Text(
                    lottery.issue,//期號(hào)
                    style = TextStyle(color = Color.Black, fontSize = 16.sp),
                    modifier = Modifier.padding(end = 8.dp),
                    maxLines = 1
                )
            }
        }
    }
}

最后,再一次特別的感謝這篇文章的作者,《Compose+Paging3+Retrofit實(shí)現(xiàn)列表分頁(yè)加載》,正是因?yàn)樗募皶r(shí)出現(xiàn),我又可以愉快地進(jìn)行下一步的代碼編寫(xiě)了,感覺(jué)離我的設(shè)想越來(lái)越近了,也越來(lái)越相信那個(gè)設(shè)想是我力所能及的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容