跳转至

GMusic-Compose-Samples

前言

在学习了 Jetpack Compose 后,作为新人的第一个实战项目实现了一个简单的本地音乐播放器

项目真机图

主要实现

关于读取本地音乐文件以及控制播放等操作在这里不做过多赘述,可以参考源代码中的 mainVM.kt 内的内容,这里主要讲界面的实现

搜索框的实现

@ExperimentalAnimationApi
@Composable
fun MainActSV(
    vm: MainViewModel = viewModel()
) {
    // 搜索框可见性
    val visible by vm.expanded.observeAsState(false)

    // 待搜索的歌曲
    var targetSong by rememberSaveable { mutableStateOf("") }

    AnimatedVisibility(
        visible = visible
    ) {
        Surface(
            Modifier.fillMaxWidth(),
            elevation = 8.dp
        ) {
            Row(modifier = Modifier.fillMaxWidth()) {
                TextField(
                    value = targetSong,
                    onValueChange = {
                        targetSong = it
                        vm.searchSong(it)
                    },
                    label = { Text("请输入歌曲名") },
                    modifier = Modifier.fillMaxWidth(),
                    keyboardOptions = KeyboardOptions(
                        keyboardType = KeyboardType.Text,
                        imeAction = ImeAction.Search
                    ),
                    leadingIcon = {
                        Icon(Icons.Filled.Search, "搜索")
                    },
                )
            }
        }
    }
}

歌曲列表布局

@RequiresApi(Build.VERSION_CODES.R)
@Composable
fun LocalMusicRVItem(localMusicBean: LocalMusicBean, vm: MainViewModel = viewModel()) {

    Column(modifier = Modifier
        .clickable {
            vm.playMusicInMusicBean(localMusicBean)
        }
        .height(IntrinsicSize.Min)) {

        Text(
            localMusicBean.song,
            modifier = Modifier
                .padding(5.dp),
            style = TextStyle(
                fontWeight = FontWeight.Bold,
                fontSize = 20.sp,
                letterSpacing = 0.15.sp,
                color = song_tv_color
            ),
            maxLines = 1
        )

        Row{
            Text(
                localMusicBean.singer,
                modifier = Modifier
                    .padding(5.dp)
                    .wrapContentWidth(Alignment.Start) //作者名在左侧
                    .weight(1f),
                style = TextStyle(
                    color = song_tv_color
                )
            )

            Text(
                localMusicBean.duration,
                modifier = Modifier
                    .padding(5.dp)
                    .wrapContentWidth(Alignment.End) //时间在右侧
                    .weight(1f),
                style = TextStyle(
                    color = song_tv_color
                )
            )
        }
    }
}

底部控制栏的实现

@RequiresApi(Build.VERSION_CODES.R)
@Composable
fun BottomControlLayout() {

    val localMusicBean: LocalMusicBean by viewModel.localMusicBean.observeAsState(LocalMusicBean())

    Row(
        modifier = Modifier
            .fillMaxWidth()
            .height(IntrinsicSize.Min)
            .defaultMinSize(0.dp, 80.dp)
            .background(bottom_layout_main_bg_color),
        verticalAlignment = Alignment.CenterVertically
    ) {

        // 歌曲图标
        Box(Modifier.weight(1f), contentAlignment = Alignment.Center) {
            Image(
                painter = painterResource(id = R.drawable.user),
                contentDescription = "歌曲图标",
                modifier = Modifier
                    .clip(CircleShape)
                    .size(60.dp)
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
        }

        // 歌曲信息
        Column(Modifier.fillMaxWidth().weight(2f)) {
            Text(localMusicBean.song, style = TextStyle(color = Color.White))
            Text(localMusicBean.singer, style = TextStyle(color = Color.White))
        }

        Row(
            Modifier
                .fillMaxWidth()
                .wrapContentWidth(Alignment.CenterHorizontally)
                .weight(2f),
            verticalAlignment = Alignment.CenterVertically, //设置垂直方向对齐
            horizontalArrangement = Arrangement.spacedBy(10.dp) //设置子项的间距
        ) {

            val isPlaying by viewModel.isPlaying.observeAsState(false)

            // 上一首按钮
            Image(painter = painterResource(id = R.drawable.ic_last),
                contentDescription = "上一首",
                modifier = Modifier
                    .clickable { viewModel.playLastMusic() }
                    .size(30.dp)
            )

            // 播放暂停按钮
            Image(painter = if (isPlaying) {
                painterResource(id = R.drawable.ic_pause)
            } else {
                painterResource(id = R.drawable.ic_play)
            },
                contentDescription = "播放或者暂停",
                modifier = Modifier
                    .clickable { viewModel.playCurrentMusic() }
                    .size(40.dp)
            )

            // 下一首按钮
            Image(painter = painterResource(id = R.drawable.ic_next),
                contentDescription = "下一首",
                modifier = Modifier
                    .clickable { viewModel.playNextMusic() }
                    .size(30.dp)
            )
        }
    }
}

歌曲列表

@Composable
fun MainActRV(
    modifier: Modifier = Modifier,
    vm: MainViewModel = viewModel()
) {
    val targetSong by vm.targetSong.observeAsState(ArrayList<LocalMusicBean>())

    LazyColumn(modifier = modifier.background(Color(0xFFdcdde1))) {
        items(
            items = targetSong
        ){
            LocalMusicRVItem(localMusicBean = it)
        }
    }
}

项目地址

GMusic-Compose-Samples 如果项目对你有所帮助,欢迎点赞👍,Star⭐,收藏😍,如果有改进意见还可以提交 issue

外部依赖

PermissionX

An open source Android library that makes handling runtime permissions extremely easy

Android Jetpack Compose 沉浸式/透明状态栏 ProvideWindowInsets SystemUiController


最后更新: October 10, 2021