APP包含的功能:图片列表,图片检查,图片共享,图片下载,设置壁纸,设置

图片列表

Compose写一个简单的图片APP
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
fun <T> SwipeStaggeredGridRefresh(
    limitRefresh: Boolean = true,
    limitLoadMore: Boolean = true,
    refreshingUI: @Composable () -> Unit = {},
    loadMoreUI: @Composable () -> Unit,
    emptyLayout: @Composable () -> Unit,
    contentUI: @Composable () -> Unit = {},
    items: List<T>?,
    refreshing: Boolean,
    onRefresh: () -> Unit,
    loading: Boolean,
    onLoad: () -> Unit,
    modifier: Modifier = Modifier.background(color = MaterialTheme.colorScheme.background),
    listState: LazyStaggeredGridState = rememberLazyStaggeredGridState(),
    contentPadding: PaddingValues = PaddingValues(
        start = 10.dp,
        top = 0.dp,
        end = 10.dp,
        bottom = 0.dp
    ),
    verticalItemSpacing: Dp = 10.dp,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(10.dp),
    key: ((index: Int, item: T) -> Any)? = null,
    contentType: (index: Int, item: T) -> Any? = { _, _ -> null },
    itemContent: @Composable LazyStaggeredGridItemScope.(index: Int, item: T) -> Unit
) {
    val pullRefreshState = rememberPullRefreshState(refreshing, { onRefresh() })
    if (items.isNullOrEmpty()) {
        if (!refreshing) {
            emptyLayout()
        }
    } else {
        Box(Modifier.pullRefresh(pullRefreshState)) {
            LazyVerticalStaggeredGrid(
                modifier = modifier.navigationBarsPadding(),
                state = listState,
                columns = StaggeredGridCells.Fixed(2),
                contentPadding = contentPadding,
                verticalItemSpacing = verticalItemSpacing,
                horizontalArrangement = horizontalArrangement,
            ) {
                item {
                    contentUI()
                }
                itemsIndexed(items, key = key, contentType = contentType) { index, item ->
                    itemContent(index, item)
                    LaunchedEffect(items.size) {
                        if (!refreshing && limitLoadMore && items.size - index < 2) {
                            onLoad()
                        }
                    }
                }
                if (!refreshing && loading && limitLoadMore) {
                    item(span = FullLine) {
                        loadMoreUI()
                    }
                }
            }
            if (limitRefresh && !loading) {
                PullRefreshIndicator(
                    refreshing = refreshing,
                    state = pullRefreshState,
                    backgroundColor = MaterialTheme.colorScheme.background,
                    contentColor = MaterialTheme.colorScheme.primary,
                    modifier = Modifier.align(Alignment.TopCenter),
                )
            }
        }
    }
}

图片加载

@Composable
fun <T> PhotoItem(item: T, imgUrl: String, click: (T) -> Unit) {
    val configuration = LocalConfiguration.current
    AsyncImage(
        modifier = Modifier
            .fillMaxWidth()
            .clickable { click(item) }
            .clip(RoundedCornerShape(10.dp))
            .heightIn(max = configuration.screenHeightDp.dp * 0.5f),
        model = ImageRequest.Builder(LocalContext.current)
            .data(imgUrl)
            .crossfade(true)
            .build(),
        contentDescription = null,
        contentScale = ContentScale.FillWidth,
    )
}

图片概况

Compose写一个简单的图片APP

顶部APPBar

TopAppBar(
    title = {
        Text(
            text = title,
            maxLines = 1,
            softWrap = false,
            textAlign = TextAlign.Start,
            overflow = TextOverflow.Ellipsis,
            color = Color(0xFFFFFFFF),
            style = MaterialTheme.typography.titleMedium,
            modifier = Modifier.padding(start = 5.dp, end = 10.dp)
        )
    },
    navigationIcon = {
        IconButton(onClick = onBack) {
            Icon(
                imageVector = Icons.Filled.ArrowBack,
                contentDescription = null,
                tint = Color(0xFFFFFFFF)
            )
        }
    },
    actions = {
        DownLoadButton(
            tint = Color(0xFFFFFFFF),
            onClick = downLoad
        )
        WallpaperButton(
            tint = Color(0xFFFFFFFF),
            onClick = wallpaper
        )
        ShareButton(
            tint = Color(0xFFFFFFFF),
            onClick = share
        )
    },
    colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
        containerColor = Color(0x00FFFFFF),
    )
)

图片扩大缩小

@Composable
private fun Image(
    imgUrl: String,
    onLoading: () -> Unit,
    onSuccess: () -> Unit,
    drawable: (Drawable) -> Unit
) {
    /**
     * 缩放份额
     */
    var scale by remember { mutableStateOf(1f) }
    /**
     * 监听手势状况变换
     */
    val state = rememberTransformableState(onTransformation = { zoomChange, panChange, _ ->
        scale = (zoomChange * scale).coerceAtLeast(1f)
        scale = if (scale > 5f) {
            5f
        } else {
            scale
        }
    })
    val configuration = LocalConfiguration.current
    AsyncImage(
        //placeholder = rememberAsyncImagePainter(imgUrl),
        model = ImageRequest.Builder(LocalContext.current)
            .data(imgUrl)
            .crossfade(true)
            .build(),
        contentDescription = null,
        modifier = Modifier
            .transformable(state = state)
            .graphicsLayer(
                scaleX = scale,
                scaleY = scale,
            )
            .pointerInput(Unit) {
                detectTapGestures(
                    onDoubleTap = {
                        scale = if (scale <= 1f) {
                            2f
                        } else {
                            1f
                        }
                    }
                )
            }
            .fillMaxSize(),
        contentScale = ContentScale.Fit,
        onLoading = {
            onLoading()
        },
        onSuccess = {
            onSuccess()
            drawable(it.result.drawable)
        }
    )
}

壁纸设置我是经过跳转到系统壁纸设置页面来完成,这样设置作用最好。

图片下载,我偷个懒,直接经过bitmap来保存。

图片共享

Compose写一个简单的图片APP

Compose写一个简单的图片APP

我们需求把compose UI转换为Android view再去截图共享

@Composable
fun ComposeUIToView(
    content: @Composable () -> Unit,
    onLoading: () -> Unit,
    onSuccess: (ShareCardView) -> Unit,
) {
    onLoading()
    AndroidView(modifier = Modifier.wrapContentSize(),
        factory = {
            ShareCardView(
                it,
                content = {
                    content()
                }
            )
        },
        update = {
            onSuccess(it)
        }
    )
}
@SuppressLint("ViewConstructor")
class ShareCardView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    val content: @Composable () -> Unit
) : AbstractComposeView(context, attrs, defStyleAttr) {
    @Composable
    override fun Content() {
        content()
    }
}

设置

Compose写一个简单的图片APP

主要用来装备一下APP需求的功能

暗黑形式几行代码就搞定啦

val systemUiController = rememberSystemUiController()
val isSystemInDarkTheme: Boolean = isSystemInDarkTheme()
appViewModel.initUserData(appContainer.appRepository, isSystemInDarkTheme)
val darkTheme = appUiState.userData?.darkTheme ?: false
NetworkStatus(context, appViewModel, appUiState.networkConnected, appUiState.dailyData)
// Update the dark content of the system bars to match the theme
DisposableEffect(systemUiController, darkTheme) {
    systemUiController.systemBarsDarkContentEnabled = !darkTheme
    onDispose {}
}

Compose写一个简单的图片APP

APP依靠库

dependencies {
    implementation 'androidx.core:core-ktx:1.10.1'
    implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
    implementation 'androidx.activity:activity-compose:1.7.2'
    implementation platform('androidx.compose:compose-bom:2022.10.00')
    implementation 'androidx.compose.ui:ui'
    implementation 'androidx.compose.ui:ui-graphics'
    implementation 'androidx.compose.ui:ui-tooling-preview'
    implementation 'androidx.compose.material3:material3'
    implementation "androidx.compose.material:material:1.5.0"
    implementation "androidx.compose.material:material-icons-extended:1.5.0"
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.10.0'
    //AsyncImage
    implementation "io.coil-kt:coil-compose:2.2.2"
    implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.1"
    implementation "androidx.core:core-splashscreen:1.0.1"
    implementation "com.google.accompanist:accompanist-systemuicontroller:0.30.1"
    implementation "androidx.compose.material3:material3-window-size-class:1.1.1"
    implementation 'androidx.compose.ui:ui-util'
    implementation "androidx.navigation:navigation-compose:2.7.0"
    implementation files('src/main/libs/jsoup-1.9.2.jar')
    //local-preference
    implementation "androidx.datastore:datastore-preferences:1.0.0"
    implementation "com.google.accompanist:accompanist-permissions:0.30.1"
    implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
}

整个APP跳转经过Navigation

/**
 * Models the navigation actions in the app.
 */
class NavigationActions(navController: NavHostController) {
    val navigateToPexels: () -> Unit = {
        navController.navigate(PhotoDestinations.PEXELS_ROUTE) {
            // Pop up to the start destination of the graph to
            // avoid building up a large stack of destinations
            // on the back stack as users select items
            popUpTo(navController.graph.findStartDestination().id) {
                saveState = true
            }
            // Avoid multiple copies of the same destination when
            // reselecting the same item
            launchSingleTop = true
            // Restore state when reselecting a previously selected item
            restoreState = true
        }
    }
    val navigateToDetail: (String) -> Unit = {
        navController.navigate("detail/$it") {
            launchSingleTop = true
            restoreState = true
        }
    }
    val navigateToShare: () -> Unit = {
        navController.navigate(PhotoDestinations.SHARE_ROUTE) {
            launchSingleTop = true
            restoreState = true
        }
    }
    val navigateToDaily: () -> Unit = {
        navController.navigate(PhotoDestinations.DAILY_ROUTE) {
            launchSingleTop = true
            restoreState = true
        }
    }
}

总结:个人能力有限,经过有限的学习、项目练习,觉得做展示类型的APP非常适宜。

APP体验

下载

GitHub地址

传送门