雙色球走勢圖是比較大的,圖表可拖動可縮放是很硬性的要求,而我在實現這個功能時又遇到了很大的困難,不過最終還是完成了,所以在此重新記錄一下。
1,首先在google官網中找到在compose中實施拖動和縮放動作的代碼
2,套用在這篇文章中:Compose+Paging3+Retrofit實現列表分頁加載
下面重新貼上主要的相關代碼
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, //開獎號碼
val issue :String,//期號
) : 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//一個年份的開獎數據為一頁
val response = repository.getLotterys(page)
LoadResult.Page(
data = response.value,
prevKey = null,
nextKey = if (page > 3) page - 1 else null//"3"為2003年,雙色球從2003年開始的,
)
} 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相關的頁面代碼,由于添加了手勢動作和數據加載錯誤處理,代碼比較多,這里就不一一貼出來了,只貼主要的,部分缺少的可以在文章開頭的那篇文章中找到,還有一些找不到的就自己實現吧。這是一個進行中的項目,代碼根據需要隨時在變化。
Compose+Paging3+Retrofit實現列表分頁加載
@Composable
fun LeftModeScreen() {
val showPop = remember { mutableStateOf(false) }
Row(modifier = Modifier.fillMaxSize()) {
LeftMenuColumn(showPop)//左側菜單欄
SaleTrasformable{ LotteryList() }//右側走勢圖,可拖動,可縮放
}
if (showPop.value) {
HandModePop(showPop)//切換左右手模式
}
}
//使組件可拖動、縮放和旋轉
@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//拖動
// rotation.value += rotationChange//旋轉
}
Column(
modifier = Modifier
.wrapContentWidth()
// .background(Color(34, 43, 44, 50))
.clipToBounds() //放大縮小時,內容限定在這個Column內
.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()//內容過多,用這種方式可另起一個Composable
}
}
//妥善處理數據加載過程中的各種異常
@Composable
fun LotteryList(viewModel: LotteryViewModel = viewModel()) {
val lotterys = viewModel.lotterys.collectAsLazyPagingItems()
when (lotterys.loadState.refresh) {
is LoadState.NotLoading -> //正常加載數據
LazyColumn(
Modifier.fillMaxSize(),
reverseLayout = true//反轉數據
) {
itemsIndexed(lotterys) { _, lottery ->
LotteryItem(lottery = lottery!!)
}
when (lotterys.loadState.append) {//劃到了數據邊界
is LoadState.Error -> item {//沒取不到新數據,重試
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() }//網絡錯誤
is LoadState.Loading -> LoadingPage()//加載中
}
}
//將數據的各個字段填入到相應的組件中
@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,//開獎號碼
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,//期號
style = TextStyle(color = Color.Black, fontSize = 16.sp),
modifier = Modifier.padding(end = 8.dp),
maxLines = 1
)
}
}
}
}
最后,再一次特別的感謝這篇文章的作者,《Compose+Paging3+Retrofit實現列表分頁加載》,正是因為它的及時出現,我又可以愉快地進行下一步的代碼編寫了,感覺離我的設想越來越近了,也越來越相信那個設想是我力所能及的。