我开始学习Jetpack Compose.我整理了这app个我探索不同日常用例的地方,这个项目中的每个功能模块都应该处理不同的场景.

其中一个功能模块-chatexample功能模块,试图实现一个简单的ViewPager,其中每个页面都是Fragment,第一个页面"消息"应该显示一个围绕SwipeRefreshLayout的分页RecyclerView.现在,我们的目标是使用Jetpack Compose实现所有这些功能.这就是我现在遇到的问题:

enter image description here

我用来实现Pull-to-Refresh操作的PullRefreshIndicator像预期的那样工作,到目前为止一切似乎都很简单,但我不明白为什么ProgresBar保持在最前面.

到目前为止,我已经试过了;从父母Scaffold开始一直到现在.确保我明确设置大小以适应最大高度和宽度.在when语句中添加一个空的Box-但到目前为止都没有起作用,我猜如果我看到ViewModel不应该刷新,我可以只删除PullRefreshIndicator,但我认为这不是正确的做法.

为了快速解释我在这里使用的可组合组件,我有:

<Surface>
   <Scaffold> // Set with a topBar
       <Column>
           <ScrollableTabRow>
              <Tab/> // Set for the first "Messages" tab
              <Tab/> // Set for the second "Dashboard" tab
           </ScrollableTabRow>
           <HorizontalPager>
                // ChatExampleScreen
                <Box> // A Box set with the pullRefresh modifier
                   // Depending on the ChatExamleViewModel we might pull different composables here
                   </PullRefreshIndicator>
                </Box>
                // Another ChatExampleScreen for the second tab
           </HorizontalPager>
       </Column>
   <Scaffold> 
</Surface>

老实说,我不明白一个完全不同的Composable(ChatExampleScreen)中的PullRefreshIndicator和外面的ScrollableTabRow是如何重叠的.

希望这能让你更容易理解用户界面.任何提示、建议或推荐都是值得感谢的.谢谢!????

Edit:清楚地说,我在这里试图实现的是在每一页上都有一个PullREFREFRESH指示器.大概是这样的:

enter image description here

在每个页面上,您向下拉,看到ProgressBar出现,当它完成后,它就会消失,在同一个页面内.不与上面的选项卡重叠.

推荐答案

我想留下我的第一个答案,因为我觉得它对future 的读者仍然有用,所以这是另一个你可以考虑的.

但是很抱歉,因为我不能修改您当前的代码,所以我决定做一个简短的工作示例.所有这些都是可复制和粘贴的.我还尽量使其与您自己的实现保持一致.

虽然有一些细微的不同,比如其中的Box有一个滚动修改器,因为根据Accompanist Docs和实际的功能.

…该内容需要是‘垂直滚动’的SwipeRefresh() 能够对滑动手势做出react .像LazyColumn这样的布局是 自动垂直滚动,但其他类型,如Column或 LazyRow并非如此.在这些情况下,您可以提供一个 Modifier.VerticalScroll修改器…

它来自于关于API迁移的伴奏文档,但它仍然适用于Compose框架中的当前版本.

按照我的理解,这是一个滚动事件,PullRefresh应该是手动激活的.

我还将视图模型组件从LiveData更改为StateFlows.

练习:

class PullRefresh练习: ComponentActivity() {

    private val viewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    Scaffold(
                        modifier = Modifier.fillMaxSize(),
                        topBar = { TopAppBarSample() }
                    ) {
                        MyScreen(
                            modifier = Modifier.padding(it),
                            viewModel = viewModel
                        )
                    }
                }
            }
        }
    }
}

一些数据类:

data class MessageItems(
    val message: String = "",
    val author: String = ""
)

data class DashboardBanner(
    val bannerMessage: String = "",
    val content: String = ""
)

视图模型:

class My视图模型: ViewModel() {

    var isLoading by mutableStateOf(false)

    private val _messageState = MutableStateFlow(mutableStateListOf<MessageItems>())
    val messageState = _messageState.asStateFlow()

    private val _dashboardState = MutableStateFlow(DashboardBanner())
    val dashboardState = _dashboardState.asStateFlow()

    fun fetchMessages() {

        viewModelScope.launch {
            isLoading = true

            delay(2000L)

            _messageState.update {
                it.add(
                    MessageItems(
                        message = "Hello First Message",
                        author = "Author 1"
                    ),
                )
                it.add(
                    MessageItems(
                        message = "Hello Second Message",
                        author = "Author 2"
                    )
                )

                it
            }
            isLoading = false
        }
    }

    fun fetchDashboard() {

        viewModelScope.launch {
            isLoading = true

            delay(2000L)

            _dashboardState.update {
                it.copy(
                    bannerMessage = "Hello World!!",
                    content = "Welcome to Pull Refresh Content!"
                )
            }
            isLoading = false
        }
    }
}

选项卡屏幕可组合内容:

@Composable
fun MessageTab(
    myViewModel : MyViewModel
) {
    val messages by myViewModel.messageState.collectAsState()

    LazyColumn(
        modifier = Modifier.fillMaxSize()
    ) {
        items(messages) { item ->
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .border(BorderStroke(Dp.Hairline, Color.DarkGray)),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = item.message)
                Text(text = item.author)
            }
        }
    }
}

@Composable
fun DashboardTab(
    my视图模型: MyViewModel
) {

    val banner by myViewModel.dashboardState.collectAsState()

    Box(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState()),
        contentAlignment = Alignment.Center
    ) {
        Column {
            Text(
                text = banner.bannerMessage,
                fontSize = 52.sp
            )

            Text(
                text = banner.content,
                fontSize = 16.sp
            )
        }
    }
}

最后是包含PullRefreshPager/Tab个组件的屏幕:

编辑:我修改了设置,所有组件都是ConstraintLayout的直接子项,不再是Column的子项.现在,实现PullRefreshTabs之后,但仍然在HorizontalPager之上的唯一方法是,首先我必须将HorizontalPager作为first个子元素,PullRefresh作为second个子元素,Tabs作为last个子元素,相应地限制它们以保持其视觉排列.

@OptIn(ExperimentalMaterialApi::class, ExperimentalPagerApi::class)
@Composable
fun MyScreen(
    modifier : Modifier = Modifier,
    viewModel: MyViewModel
) {
    val refreshing = viewModel.isLoading
    val pagerState = rememberPagerState()

    val pullRefreshState = rememberPullRefreshState(
        refreshing = refreshing,
        onRefresh = {
            when (pagerState.currentPage) {
                0 -> {
                    viewModel.fetchMessages()
                }
                1 -> {
                    viewModel.fetchDashboard()
                }
            }
        },
        refreshingOffset = 100.dp // just an arbitrary offset where the refresh will animate
    )

    ConstraintLayout(
        modifier = modifier
            .fillMaxSize()
            .pullRefresh(pullRefreshState)
    ) {
        val (pager, pullRefresh, tabs) = createRefs()

        HorizontalPager(
            count = 2,
            state = pagerState,
            modifier = Modifier.constrainAs(pager) {
                top.linkTo(tabs.bottom)
                start.linkTo(parent.start)
                end.linkTo(parent.end)
                bottom.linkTo(parent.bottom)
                height = Dimension.fillToConstraints
            }
        ) { page ->
            when (page) {
                0 -> {
                    MessageTab(
                        myViewModel = viewModel
                    )
                }
                1 -> {
                    DashboardTab(
                        myViewModel = viewModel
                    )
                }
            }
        }

        PullRefreshIndicator(
            modifier = Modifier.constrainAs(pullRefresh) {
                top.linkTo(parent.top)
                start.linkTo(parent.start)
                end.linkTo(parent.end)
            },
            refreshing = refreshing,
            state = pullRefreshState,
        )

        ScrollableTabRow(
            modifier = Modifier.constrainAs(tabs) {
                top.linkTo(parent.top)
                start.linkTo(parent.start)
                end.linkTo(parent.end)
            },
            selectedTabIndex = pagerState.currentPage,
            indicator = { tabPositions ->
                TabRowDefaults.Indicator(
                    modifier = Modifier.tabIndicatorOffset(
                        currentTabPosition = tabPositions[pagerState.currentPage],
                    )
                )
            },
        ) {
            Tab(
                selected = pagerState.currentPage == 0,
                onClick = {},
                text = {
                    Text(
                        text = "Messages"
                    )
                }
            )

            Tab(
                selected = pagerState.currentPage == 1,
                onClick = {},
                text = {
                    Text(
                        text = "Dashboard"
                    )
                }
            )
        }
    }
}

输出:

enter image description here

<Surface>
    <Scaffold>
        <ConstraintLayout>

            // top to ScrollableTabRow's bottom
            // start, end, bottom to parent's start, end and bottom
            // 0.dp (view), fillToConstraints (compose)
            <HorizontalPager>
                <PagerScreens/>
            </HorizontalPager>

            // top, start, end of parent
            <PullRefreshIndicator/>

            // top, start and end of parent
            <ScrollableTabRow>
                <Tab/> // Set for the first "Messages" tab
                <Tab/> // Set for the second "Dashboard" tab
            </ScrollableTabRow>
        </ConstraintLayout>
   <Scaffold>
</Surface>

Android相关问答推荐

理解修饰符<;方法>;与修饰符<;方法>;:效果和行为解释(Android开发者Jetpack Compose)

Android开发:主题排版不适用于按钮文本

为什么当我在装有Firebase的安卓系统中登录苹果时,收到的邮箱为空?

Jetpack Compose主导航中的全屏图标列表

Android Kotlin ImageView内置于Kotlin ImageView中.适配器未按预期更新

在Delphi中使用OpenCV for Android在使用JLIST时抛出错误

升级到 Jetpack Compose 物料 list 2023.08.00 需要我将 targetSdk 更改为 34

FFmpeg Android 错误

Android:使用依赖项 ViewModelProviderFactory 初始化 ViewModel 的正确方法

Jetpack Compose 如何使一个项目相对于另一个项目垂直居中

任务 ':app:kaptGenerateStubsDebugKotlin' 执行失败

Android工作室未成立

在 Jetpack Compose 中更改列中子项的对齐方式

我们也可以或应该对主要小部件使用预览 compose 功能吗?

如何使用底页,启用和展开父交互

在alert 对话框生成器中启动协程

CenterAlignedTopAppBar 滚动行为:未为参数状态传递值

设备文件资源管理器-ROOM 数据库中的数据库文件夹为空

SavedStateViewModelFactory 用空构造函数构造

约束布局垂直对齐中心