Jetpack Compose 拖动效果

Compose 版本:1.0.0-beta08

效果

代码示例

  • 列表页面
@Composable
fun VideoList() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Gray)
            .verticalScroll(rememberScrollState())
    ) {
        for (i in 0..20) {
            Card(modifier = Modifier.padding(10.dp)) {
                Row(
                    modifier = Modifier
                        .background(Color.White)
                        .fillMaxWidth()
                        .height(70.dp)
                ) {
                    Box(
                        modifier = Modifier
                            .background(Color.DarkGray)
                            .fillMaxWidth(0.2f)
                            .fillMaxHeight()
                    )
                    Text(
                        text = "item $i",
                        color = Color.Black,
                        modifier = Modifier
                            .weight(0.8f)
                            .fillMaxHeight()
                    )
                }
            }
        }
    }
}
  • 详情页面

@ExperimentalAnimationApi
@RequiresApi(Build.VERSION_CODES.R)
@Composable
fun PlayerPage() {
    //拖动距离
    val offsetY = remember {
        mutableStateOf(0f)
    }
    //获取设备信息
    val outMetrics = DisplayMetrics()
    val display = LocalContext.current.display
    display?.getRealMetrics(outMetrics)
    val scale: Float = LocalContext.current.resources.displayMetrics.density
    val deviceWidth = (outMetrics.widthPixels / scale + 0.5f).toInt().dp
    val deviceHeight = (outMetrics.heightPixels / scale + 0.5f).toInt().dp
    //关键帧
    val keyframe0 = deviceHeight / 3
    val keyframe1 = deviceHeight - playerHeight
    //页面状态
    val playerState = remember {
        mutableStateOf(PlayerState.EXPAND)
    }

    //宽度变化
    val widthAnim = animateDpAsState(
        targetValue = when {
            offsetY.value < keyframe1.value -> {
                deviceWidth
            }
            else -> {
                miniPlayerWidth
            }
        }
    )

    //高度变化
    val heigtAnim = animateDpAsState(
        targetValue = when {
            offsetY.value < keyframe1.value -> {
                playerState.value = PlayerState.HALF
                (deviceHeight.value - offsetY.value).dp
            }
            else -> {
                playerState.value = PlayerState.MINI
                miniPlayerHeight
            }
        }
    )

    Column(
        modifier = Modifier
            .padding(if (playerState.value == PlayerState.MINI) 10.dp else 0.dp)
            .height(heigtAnim.value)
            .draggable(
                state = rememberDraggableState {
                    offsetY.value += it
                },
                orientation = Orientation.Vertical,
                onDragStopped = {
                    //停止时,根据拖动距离,改变状态
                    if (playerState.value == PlayerState.HALF) {
                        if (offsetY.value > keyframe0.value / 2) {
                            offsetY.value = keyframe0.value
                        } else {
                            offsetY.value = 0f
                        }
                    }
                }
            )
    ) {
        Row(
            horizontalArrangement = Arrangement.End,
            modifier = Modifier
                .background(Color.White)
                .border(width = 1.dp, color = Color.Black)
                .fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Box(
                modifier = Modifier
                    .background(Color.Black)
                    .height(playerHeight)
                    .width(widthAnim.value)
            ) {
                Text(text = "Player", modifier = Modifier.align(Alignment.Center))
            }
            Text(text = "video 123", modifier = Modifier.weight(1f))
            Icon(
                imageVector = Icons.Default.PlayArrow,
                contentDescription = null,
                modifier = Modifier.weight(1f)
            )
        }

        Column(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.White)
        ) {
            Text(
                text = "summary 123", modifier = Modifier
                    .padding(10.dp)
            )
        }
    }
}
  • 使用

val miniPlayerHeight = 50.dp
val miniPlayerWidth = 100.dp
val playerHeight = 350.dp

enum class PlayerState {
    EXPAND, MINI, HALF
}

class MainActivity : ComponentActivity() {
    @ExperimentalAnimationApi
    @RequiresApi(Build.VERSION_CODES.R)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PermissionTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Box(
                        modifier = Modifier.fillMaxSize(),
                        contentAlignment = Alignment.BottomCenter
                    ) {
                        VideoList()
                        PlayerPage()
                    }
                }
            }
        }
    }
}