当前位置:网站首页>Using compose to realize visible scrollbar
Using compose to realize visible scrollbar
2022-07-02 07:24:00 【Drizzle】
Use Compose The implementation is visible ScrollBar
Preface
as everyone knows , If in a View Put a bigger one inside View You need to scroll to work properly .
Implementation platform :Compose-Desktop (Compose-Android etc. Multiplatform It's the same )
Achieve the effect diagram

Let's get started
Define a UniversalScrollBox Method , Inside one Box Used to pack all the pieces Composable Content . The content data of this example is 10 * 10 individual Text Components .
Width of scroll bar : roll dynamic strip wide degree can see Inside Rong wide degree = can see Inside Rong wide degree Inside Rong total wide degree \frac{ Width of scroll bar }{ Visible content width } = \frac{ Visible content width }{ Total content width } can see Inside Rong wide degree roll dynamic strip wide degree = Inside Rong total wide degree can see Inside Rong wide degree
Scroll bar height : Same width principle .
private const val TAG = "UniversalScrollBox"
private fun TwoFloats(width: Float, height: Float) = androidx.compose.ui.geometry.Size(width = width, height = height)
inline val Int.ddp: Dp get() = Dp(value = this.toFloat())
inline val Float.ddp: Dp get() = Dp(value = this)
/** * Be careful content There must be something in it , There is no air judgment protection , Array will be out of bounds */
@Composable
internal fun UniversalScrollBox(
modifier: Modifier = Modifier,
scrollBarStroke: Int = 16,
scrollBarColor: Color = MaterialTheme.colors.secondary.copy(alpha = 0.5F),
content: @Composable () -> Unit
) {
Box(
modifier = modifier
) {
var outerSize by remember {
mutableStateOf(IntSize(width = 0, height = 0)) } // Width and height of outer layer , That is, the width of the content visible
var sizeRatio by remember {
mutableStateOf(TwoFloats(width = 0F, height = 0F)) } // Internal and external size ratio
var barSize by remember {
mutableStateOf(TwoFloats(width = 0F, height = 0F)) } // Horizontal scroll bar width 、 Vertical scroll bar height ,$\frac{ Width of scroll bar }{ Visible content width } = \frac{ Visible content width }{ Total content width }$
var dragOffset by remember {
mutableStateOf(TwoFloats(width = 0F, height = 0F)) } // The scroll bar is horizontal 、 The distance dragged vertically
/** * The horizontal scroll bar will block the vertical content , therefore , When the horizontal scroll bar exists , You need to set the vertical direction padding, The same goes for vertical scroll bars . * When content change , This causes the scrollbar to appear for the first time ,padding Change with it , When drawing outerSize change , This may cause another scroll bar to appear , Continue to cause padding change ,outerSize change , Scroll bar width changes . * But after these changes , padding It won't change , Therefore, the scroll bar will not continue to change , Change ends . Although when it first changed , Can predict subsequent changes , But I'm too lazy to calculate . */
Layout(
modifier = Modifier.fillMaxSize()
.padding(bottom = if (barSize.width > 0) 16.ddp else 0.ddp, end = if (barSize.height > 0) 16.ddp else 0.ddp)
.clipToBounds()
.align(Alignment.TopStart)
.wrapContentSize(unbounded = false),
content = content,
measurePolicy = object : MeasurePolicy {
override fun MeasureScope.measure(measurables: List<Measurable>, constraints: Constraints): MeasureResult {
// Outer layer modifier Use unbounded false, Can pass constraints.maxWidth maxHeight Calculate the maximum size of the outer layer , Pay attention to this Size yes padding After that
outerSize = IntSize(width = constraints.maxWidth, height = constraints.maxHeight)
Log.d(TAG, "outerSize $outerSize")
val placeables = measurables.map {
measurable ->
// Actual calculation Son Node When , Use maxWidth = Int.MAX_VALUE, You can get Son Node The true width of
measurable.measure(constraints = constraints.copy(maxHeight = Int.MAX_VALUE, maxWidth = Int.MAX_VALUE))
}
val innerSize = IntSize(width = placeables[0].width, height = placeables[0].height)
// Calculate whether the level is required 、 Vertical scroll bar
val needWidth = innerSize.width > outerSize.width
val needHeight = innerSize.height > outerSize.height
sizeRatio = TwoFloats(
width = if (needWidth) innerSize.width.toFloat() / outerSize.width.toFloat() else 0F,
height = if (needHeight) innerSize.height.toFloat() / outerSize.height.toFloat() else 0F,
)
barSize = TwoFloats(
width = if (needWidth) (outerSize.width * outerSize.width).toFloat() / innerSize.width else 0F,
height = if (needHeight) (outerSize.height * outerSize.height).toFloat() / innerSize.height else 0F
)
Log.d(TAG, "bar(width: ${
barSize.width}, height ${
barSize.height}) ratio(width: ${
sizeRatio.width}, height ${
sizeRatio.height})")
return layout(width = outerSize.width, height = outerSize.height) {
placeables.forEach {
it.place(x = (-dragOffset.width * sizeRatio.width).toInt(), y = (-dragOffset.height * sizeRatio.height).toInt(), zIndex = 0F) }
}
}
}
)
if (barSize.width > 0) {
Box(
modifier = Modifier.fillMaxWidth()
.height(scrollBarStroke.ddp)
.align(Alignment.BottomStart)
.background(Color.Transparent)
) {
Box(
modifier = Modifier.align(Alignment.BottomStart)
.fillMaxHeight()
.offset {
IntOffset(dragOffset.width.roundToInt(), 0) }
.width(barSize.width.ddp)
.clip(RoundedCornerShape(4.ddp)) // Pay attention first clip Again background
.background(scrollBarColor) // Pay attention first offset Again background
.draggable(state = rememberDraggableState {
var widthOffset = dragOffset.width
widthOffset += it
// Limit dragging to no more than two ends
// Attention level 、 Vertical scrollbars will affect each other , When two parties exist at the same time , The size should be controlled , Don't intersect in BottomEnd
if (widthOffset < 0) {
widthOffset = 0F
} else if (widthOffset > outerSize.width - barSize.width) {
widthOffset = (outerSize.width - barSize.width)
}
dragOffset = TwoFloats(
width = widthOffset,
height = dragOffset.height
)
}, orientation = Orientation.Horizontal)
)
}
}
if (barSize.height > 0) {
Box(
modifier = Modifier.fillMaxHeight()
.width(scrollBarStroke.ddp)
.align(Alignment.TopEnd)
.background(Color.Transparent)
) {
Box(
modifier = Modifier.align(Alignment.TopEnd)
.fillMaxWidth()
.offset {
IntOffset(0, dragOffset.height.roundToInt()) }
.height(barSize.height.ddp)
.clip(RoundedCornerShape(4.ddp))
.background(scrollBarColor)
.draggable(state = rememberDraggableState {
var heightOffset = dragOffset.height
heightOffset += it
if (heightOffset < 0) {
heightOffset = 0F
} else if (heightOffset > outerSize.height - barSize.height) {
heightOffset = (outerSize.height - barSize.height)
}
dragOffset = TwoFloats(
width = dragOffset.width,
height = heightOffset
)
}, orientation = Orientation.Vertical)
)
}
}
}
}
3… Use
fun main(args: Array<String>) = application {
YCRWindow(
onCloseRequest = {
exitApplication()
},
title = "Test",
state = rememberWindowState(width = 150.ddp, height = 150.ddp, position = WindowPosition.Aligned(Alignment.Center)),
icon = loadSvgPainter("love.svg")
) {
Box(
modifier = Modifier.size(100.ddp, 100.ddp)
) {
UniversalScrollBox(
modifier = Modifier.fillMaxSize()
) {
Row(modifier = Modifier.wrapContentSize()) {
repeat(10) {
Column(modifier = Modifier.wrapContentSize()) {
repeat(10) {
Text(
text = " 123 "
)
}
}
}
}
}
}
}
}
边栏推荐
- RMAN增量恢复示例(1)-不带未备份的归档日志
- Cognitive science popularization of middle-aged people
- [introduction to information retrieval] Chapter 7 scoring calculation in search system
- Use matlab to realize: chord cut method, dichotomy, CG method, find zero point and solve equation
- TCP attack
- Analysis of MapReduce and yarn principles
- 一份Slide两张表格带你快速了解目标检测
- Two table Association of pyspark in idea2020 (field names are the same)
- @Transational踩坑
- Only the background of famous universities and factories can programmers have a way out? Netizen: two, big factory background is OK
猜你喜欢

SSM second hand trading website

类加载器及双亲委派机制

Oracle EBS数据库监控-Zabbix+zabbix-agent2+orabbix

Analysis of MapReduce and yarn principles

The boss said: whoever wants to use double to define the amount of goods, just pack up and go

Sqli labs customs clearance summary-page2

ORACLE EBS中消息队列fnd_msg_pub、fnd_message在PL/SQL中的应用

The first quickapp demo

ORACLE EBS 和 APEX 集成登录及原理分析
![[Bert, gpt+kg research] collection of papers on the integration of Pretrain model with knowledge](/img/2e/e74d7a9efbf9fe617f4d7b46867c0a.png)
[Bert, gpt+kg research] collection of papers on the integration of Pretrain model with knowledge
随机推荐
SSM学生成绩信息管理系统
Pyspark build temporary report error
DNS攻击详解
ORACLE EBS DATAGUARD 搭建
实现接口 Interface Iterable&lt;T&gt;
Error in running test pyspark in idea2020
User login function: simple but difficult
MySQL无order by的排序规则因素
Typeerror in allenlp: object of type tensor is not JSON serializable error
Write a thread pool by hand, and take you to learn the implementation principle of ThreadPoolExecutor thread pool
Oracle segment advisor, how to deal with row link row migration, reduce high water level
Interpretation of ernie1.0 and ernie2.0 papers
Three principles of architecture design
oracle EBS标准表的后缀解释说明
Ding Dong, here comes the redis om object mapping framework
view的绘制机制(三)
【Ranking】Pre-trained Language Model based Ranking in Baidu Search
ERNIE1.0 与 ERNIE2.0 论文解读
ORACLE EBS中消息队列fnd_msg_pub、fnd_message在PL/SQL中的应用
一个中年程序员学习中国近代史的小结