前语

回顾下之前的内容,咱们已经完成了以下相关页内容,它们分别是:

Jetpack Compose 实战之仿微信UI -完成登陆页(一)

Jetpack Compose 实战之仿微信UI -完成首页(二)

Jetpack Compose 实战之仿微信UI -完成朋友圈(三)

在这一篇文章中,我将运用 Jetpack Compose 去完成微信朋友圈图片的预览。

功用拆分

微信朋友圈图片预览的功用首要包括:

(1)图片左右滑动

(2)页面指示器

(3)缩放

(4)双击扩大(然后再双击康复,反之)

(5)单击返回

功用完成

图片左右滑动

在完成首页的时分,咱们运用了 HorizontalPager 进行页面切换,所以咱们在这儿直接运用HorizontalPager作为图片左右切换的组件即可,完成的代码为:

HorizontalPager(
    count = images.size,
    state = pageState,
    contentPadding = PaddingValues(horizontal = 0.dp),
    modifier = Modifier.fillMaxSize()
) { page ->
    ImagePageItem()
}

页面指示器

HorizontalPager给咱们封装了一个指示器组件 HorizontalPagerIndicator,咱们直接运用就能完成咱们的作用,完成代码为:

HorizontalPagerIndicator(
    pagerState = pageState,
    activeColor = Color.White,              /*** 指示器选中的色彩 */
    inactiveColor = Color(0xff888888), /*** 指示器未选中的色彩 */
    indicatorHeight = 6.dp,                  /*** 指示器的高度 */
    indicatorWidth = 6.dp,                   /*** 指示器的宽度 */
    modifier = Modifier
        .align(Alignment.BottomCenter)
        .padding(60.dp)
)

到这儿,咱们就完成了图片的切换以及指示器部分了,看下作用

Jetpack Compose 实战之仿微信UI -完成朋友圈图片预览(四)

缩放

缩放完成的中心是监听到手势的动作,然后拿到缩放份额值,平移偏移量等数据,经过查找材料,我发现官方给咱们供给了Modifier的拓宽函数 transformable,咱们看下transformable这个拓宽函数的结构:

Modifier.transformable(
    state: TransformableState,
    lockRotationOnZoomPan: Boolean = false,
    enabled: Boolean = true
)

各特点的含义:

state: 监听手势改变

lockRotationOnZoomPan: 假如为true,则仅当在平移或缩放运动之前检测到旋转的接触倾斜时才答应旋转。否则,将检测到平移和缩放手势,但不会检测到旋转手势。假如为false,一旦达到接触斜率,将检测所有三个手势。

enabled: 是否启用手势缩放

所以,在这儿咱们经过state的监听就能够拿到缩放份额值,平移偏移量等数据,具体的代码为:

/*** 缩放份额 */
var scale by remember { mutableStateOf(1f) }
/*** 偏移量 */
var offset  by remember { mutableStateOf(Offset.Zero) }
/*** 监听手势改变 */
val state = rememberTransformableState(onTransformation = { zoomChange, offsetChange, rotationChange ->
    scale = (zoomChange * scale).coerceAtLeast(1f)
    scale = if (scale > 4f) {
        4f
    } else {
        scale
    }
    offset += offsetChange
})

其间 rotationChange 是旋转的改变,这儿咱们不需求,就不处理。4f 是咱们答应扩大的最大倍数,根据自己的需求自定义即可。

到这儿,咱们将 state 赋值到 Image 组件的 Modifier,就能够监听到图片的手势的缩放,平移等,具体代码为:

Image(
    painter = rememberCoilPainter(
        request = image
    ),
    contentDescription = null,
    contentScale = ContentScale.Fit,
    modifier = Modifier.transformable(state = state) 
)

但是,咱们拿到了手势改变的数据,怎样操控图片随着这些数据进行动画呢?官方供给了动画特点 Modifier.graphicsLayer,经过 GraphicsLayerScope 来操控缩放,平移,旋转,透明度,渐变等常用动画,具体的完成代码为:

Image(
    painter = rememberCoilPainter(
        request = image
    ),
    contentDescription = null,
    contentScale = ContentScale.Fit,
    modifier = Modifier
        .transformable(state = state)         /** 检测手势元素的平移、缩放、旋转 */
        .graphicsLayer{  /**缩放、旋转、移动变换 */
            scaleX = scale                    /** 等比缩放 */
            scaleY = scale                    /** 等比缩放 */
            translationX = offset.x           /** X轴位移量*/
            translationY = offset.y           /** Y轴位移量*/
            /** 核算页面的当前偏移 */
            val pageOffset = pagerScope.calculateCurrentOffsetForPage(page = page).absoluteValue
            if (pageOffset == 1.0f) {
                scale = 1.0f
            }
)

这样,咱们监听手势的改变和运用动画就达到了图片缩放,平移的作用了。

双击,单击时事情的处理

咱们运用 pointerInput 来监听图片的单双击事情,具体的代码为:

pointerInput(Unit) {
    detectTapGestures(
        onDoubleTap = {
            scale = if (scale <= 1f) {
                2f
            } else {
                1f
            }
            offset = Offset.Zero
        },
        onTap = {
            context.finish()
        }
    )
}

(1)单击的处理:比较简单,就是直接finish当前页面,假如是运用 navigation 导航的,运用navController.popBackStack()即可,我这儿是运用Activity。

(2)双击的处理:这儿的只有扩大和正常两种状况,当前状况为正常时,双击就扩大2倍(自定义,当然也能够扩大3倍),当前状况为扩大时,双击就将缩放倍数改为1倍,这样那就达到了咱们的作用。

完成的悉数代码为:

@OptIn(ExperimentalPagerApi::class)
@Composable
fun ImagePreviewScreen(images: ArrayList<String>, currentIndex: Int) {
    rememberSystemUiController().setStatusBarColor(Color.Black, darkIcons = true)
    /*** 界面状况改变 */
    val pageState = rememberPagerState(initialPage = currentIndex)
    Box {
        /*** 图片部分 */
        HorizontalPager(
            count = images.size,
            state = pageState,
            contentPadding = PaddingValues(horizontal = 0.dp),
            modifier = Modifier.fillMaxSize()
        ) { page ->
            ImagePageItem(images[page], page, this)
        }
        /*** 指示器部分 */
        HorizontalPagerIndicator(
            pagerState = pageState,
            activeColor = Color.White,              /*** 指示器选中的色彩 */
            inactiveColor = Color(0xff888888), /*** 指示器未选中的色彩 */
            indicatorHeight = 6.dp,                  /*** 指示器的高度 */
            indicatorWidth = 6.dp,                   /*** 指示器的宽度 */
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .padding(60.dp)
        )
    }
}
@OptIn(ExperimentalPagerApi::class)
@Composable
fun ImagePageItem(
    image: String,
    page: Int = 0,
    pagerScope: PagerScope
) {
    val context = LocalContext.current as Activity
    /*** 缩放份额 */
    var scale by remember { mutableStateOf(1f) }
    /*** 偏移量 */
    var offset  by remember { mutableStateOf(Offset.Zero) }
    /*** 监听手势改变 */
    val state = rememberTransformableState(onTransformation = { zoomChange, offsetChange, rotationChange ->
        scale = (zoomChange * scale).coerceAtLeast(1f)
        scale = if (scale > 4f) {
            4f
        } else {
            scale
        }
        offset += offsetChange
    })
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = Color.Black,
    ) {
        Image(
            painter = rememberCoilPainter(
                request = image
            ),
            contentDescription = null,
            contentScale = ContentScale.Fit,
            modifier = Modifier
                .transformable(state = state)         /** 检测手势元素的平移、缩放、旋转 */
                .graphicsLayer{  /**缩放、旋转、移动变换 */
                    scaleX = scale                    /** 等比缩放 */
                    scaleY = scale                    /** 等比缩放 */
                    translationX = offset.x           /** X轴位移量*/
                    translationY = offset.y           /** Y轴位移量*/
                    /** 核算页面的当前偏移 */
                    val pageOffset = pagerScope.calculateCurrentOffsetForPage(page = page).absoluteValue
                    if (pageOffset == 1.0f) {
                        scale = 1.0f
                    }
                }.pointerInput(Unit) {
                    detectTapGestures(
                        onDoubleTap = {
                            scale = if (scale <= 1f) {
                                2f
                            } else {
                                1f
                            }
                            offset = Offset.Zero
                        },
                        onTap = {
                            context.finish()
                        }
                    )
                }
        )
    }
}

到这儿,咱们就完成了仿微信朋友圈图片的预览功用了,一起来看下悉数作用

Jetpack Compose 实战之仿微信UI -完成朋友圈图片预览(四)

总结

咱们这一期运用了 HorizontalPager 完成了图片切换,运用 HorizontalPagerIndicator 完成了指示器的功用,运用 transformable 监听手势的缩放,平移等动作,运用了 graphicsLayer 进行动画,运用 pointerInput 来监听图片的单双击事情。

项目地址:ComposeWechat,假如对你有用,别忘了给个star