More compose view implementations

This commit is contained in:
MM20 2024-06-26 19:33:30 +02:00
parent 15cb4b4f29
commit 7ff161308b
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
14 changed files with 295 additions and 37 deletions

View File

@ -115,7 +115,7 @@ dependencies {
implementation(libs.androidx.core)
implementation(libs.androidx.exifinterface)
implementation(libs.materialcomponents.core)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.constraintlayout.views)
implementation(libs.bundles.androidx.lifecycle)

View File

@ -78,6 +78,7 @@ dependencies {
implementation(libs.androidx.compose.materialicons)
implementation(libs.androidx.compose.animation)
implementation(libs.androidx.compose.animationgraphics)
implementation(libs.androidx.constraintlayout.compose)
implementation(libs.androidx.navigation.compose)

View File

@ -5,21 +5,24 @@ import android.graphics.drawable.Drawable
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ListView
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material3.Text
@ -32,6 +35,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.nativeCanvas
@ -81,6 +85,8 @@ fun ComposeAndroidView(
when (view) {
is FrameLayout -> ComposeFrameLayout(view, mod)
is LinearLayout -> ComposeLinearLayout(view, mod)
is RelativeLayout -> ComposeRelativeLayout(view, mod)
is ImageButton -> ComposeImageButton(view, mod)
is ListView -> ComposeListView(view, mod)
is TextView -> ComposeTextView(view, mod)
is ImageView -> ComposeImageView(view, mod)
@ -115,6 +121,17 @@ internal fun Modifier.view(view: View): Modifier = this then Modifier.composed {
else -> Modifier
}
val foregroundDrawable = view.foreground
val foreground = if (foregroundDrawable != null) {
Modifier.drawWithContent {
drawContent()
foregroundDrawable.setBounds(
0, 0, size.width.roundToInt(), size.height.roundToInt()
)
foregroundDrawable.draw(this.drawContext.canvas.nativeCanvas)
}
} else Modifier
val padding = with(density) {
Modifier.padding(
start = view.paddingStart.toDp(),
@ -153,7 +170,22 @@ internal fun Modifier.view(view: View): Modifier = this then Modifier.composed {
cameraDistance = view.cameraDistance
}
background then clickable then padding then graphicsLayer
val minWidth = if (view.minimumWidth > 0)
Modifier.widthIn(
min = with(density) { view.minimumWidth.toDp() }
)
else Modifier
val minHeight = if (view.minimumHeight > 0)
Modifier.heightIn(
min = with(density) { view.minimumHeight.toDp() }
)
else Modifier
minWidth then minHeight then
background then foreground then
clickable then padding then graphicsLayer
}

View File

@ -36,7 +36,7 @@ private fun Modifier.frameLayoutChild(scope: BoxScope, params: ViewGroup.LayoutP
Modifier.layoutParams(params) then
with(scope) {
if (params !is FrameLayout.LayoutParams) return@with Modifier
val alignment = when (params.gravity) {
val alignment = when (params.gravity and (Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK or Gravity.VERTICAL_GRAVITY_MASK)) {
Gravity.START or Gravity.TOP -> Alignment.TopStart
Gravity.START or Gravity.BOTTOM -> Alignment.BottomStart
Gravity.END or Gravity.TOP -> Alignment.TopEnd

View File

@ -0,0 +1,50 @@
package de.mm20.launcher2.ui.component.view
import android.graphics.BlendModeColorFilter
import android.graphics.ColorMatrixColorFilter
import android.widget.ImageButton
import android.widget.ImageView
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import coil.compose.AsyncImage
@Composable
internal fun ComposeImageButton(
view: ImageButton,
modifier: Modifier,
) {
AsyncImage(
modifier = modifier.alpha(view.imageAlpha / 255f),
model = view.drawable,
contentDescription = view.contentDescription?.toString(),
contentScale = when (view.scaleType) {
ImageView.ScaleType.CENTER -> ContentScale.None
ImageView.ScaleType.FIT_XY -> ContentScale.FillBounds
ImageView.ScaleType.FIT_START,
ImageView.ScaleType.FIT_CENTER,
ImageView.ScaleType.FIT_END -> ContentScale.Fit
ImageView.ScaleType.CENTER_CROP -> ContentScale.Crop
ImageView.ScaleType.CENTER_INSIDE -> ContentScale.Inside
else -> ContentScale.None
},
alignment = when (view.scaleType) {
ImageView.ScaleType.FIT_XY,
ImageView.ScaleType.CENTER,
ImageView.ScaleType.FIT_CENTER,
ImageView.ScaleType.CENTER_CROP,
ImageView.ScaleType.CENTER_INSIDE -> Alignment.Center
ImageView.ScaleType.FIT_START -> Alignment.TopStart
ImageView.ScaleType.FIT_END -> Alignment.BottomEnd
else -> Alignment.Center
},
)
}

View File

@ -1,16 +1,10 @@
package de.mm20.launcher2.ui.component.view
import android.graphics.BlendModeColorFilter
import android.graphics.ColorMatrixColorFilter
import android.widget.ImageView
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import coil.compose.AsyncImage

View File

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -25,7 +24,7 @@ internal fun ComposeLinearLayout(
Gravity.START -> Alignment.Start
Gravity.CENTER_HORIZONTAL -> Alignment.CenterHorizontally
Gravity.END -> Alignment.End
else -> Alignment.Start
else -> Alignment.CenterHorizontally
}
val verticalArrangement = when (view.gravity and Gravity.VERTICAL_GRAVITY_MASK) {
Gravity.TOP -> Arrangement.Top
@ -60,7 +59,7 @@ internal fun ComposeLinearLayout(
Gravity.TOP -> Alignment.Top
Gravity.CENTER_VERTICAL -> Alignment.CenterVertically
Gravity.BOTTOM -> Alignment.Bottom
else -> Alignment.Top
else -> Alignment.CenterVertically
}
Row(
modifier = modifier,

View File

@ -1,9 +1,12 @@
package de.mm20.launcher2.ui.component.view
import android.widget.ListView
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import de.mm20.launcher2.ui.ktx.toDp
@Composable
fun ComposeListView(
@ -21,7 +24,9 @@ fun ComposeListView(
val itemView = adapter.getView(index, null, view)
ComposeAndroidView(
itemView,
modifier = Modifier.layoutParams(itemView.layoutParams)
modifier = Modifier
.padding(top = if (index != 0) view.dividerHeight.toDp() else 0.dp)
.layoutParams(itemView.layoutParams)
)
}
}

View File

@ -0,0 +1,158 @@
package de.mm20.launcher2.ui.component.view
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.constraintlayout.compose.ConstrainedLayoutReference
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintLayoutScope
import androidx.core.view.children
@Composable
internal fun ComposeRelativeLayout(
view: RelativeLayout,
modifier: Modifier,
) {
ConstraintLayout(
modifier = modifier,
) {
val refs = view.children.filterNot { it.id == View.NO_ID }.associate { it.id to createRef() }
for (child in view.children) {
ComposeAndroidView(
child,
modifier = Modifier.relativeLayoutChild(
this@ConstraintLayout,
child.layoutParams,
child.id,
refs,
)
)
}
}
}
private fun Modifier.relativeLayoutChild(
scope: ConstraintLayoutScope,
params: ViewGroup.LayoutParams,
id: Int,
refs: Map<Int, ConstrainedLayoutReference>
) = this then
Modifier.layoutParams(params) then
with(scope) {
if (params !is RelativeLayout.LayoutParams) return@with Modifier
val ref = refs[id] ?: return@with Modifier
Modifier.constrainAs(ref) {
val above = refs[params.getRule(RelativeLayout.ABOVE)]
if (above != null) {
bottom.linkTo(above.top)
}
val alignBaseline = refs[params.getRule(RelativeLayout.ALIGN_BASELINE)]
if (alignBaseline != null) {
baseline.linkTo(alignBaseline.baseline)
}
val alignBottom = refs[params.getRule(RelativeLayout.ALIGN_BOTTOM)]
if (alignBottom != null) {
bottom.linkTo(alignBottom.bottom)
}
val alignEnd = refs[params.getRule(RelativeLayout.ALIGN_END)]
if (alignEnd != null) {
end.linkTo(alignEnd.end)
}
val alignLeft = refs[params.getRule(RelativeLayout.ALIGN_LEFT)]
if (alignLeft != null) {
absoluteLeft.linkTo(alignLeft.absoluteLeft)
}
val alignParentBottom = params.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
if (alignParentBottom == RelativeLayout.TRUE) {
bottom.linkTo(parent.bottom)
}
val alignParentEnd = params.getRule(RelativeLayout.ALIGN_PARENT_END)
if (alignParentEnd == RelativeLayout.TRUE) {
end.linkTo(parent.end)
}
val alignParentLeft = params.getRule(RelativeLayout.ALIGN_PARENT_LEFT)
if (alignParentLeft == RelativeLayout.TRUE) {
absoluteLeft.linkTo(parent.absoluteLeft)
}
val alignParentRight = params.getRule(RelativeLayout.ALIGN_PARENT_RIGHT)
if (alignParentRight == RelativeLayout.TRUE) {
absoluteRight.linkTo(parent.absoluteRight)
}
val alignParentStart = params.getRule(RelativeLayout.ALIGN_PARENT_START)
if (alignParentStart == RelativeLayout.TRUE) {
start.linkTo(parent.start)
}
val alignParentTop = params.getRule(RelativeLayout.ALIGN_PARENT_TOP)
if (alignParentTop == RelativeLayout.TRUE) {
top.linkTo(parent.top)
}
val alignRight = refs[params.getRule(RelativeLayout.ALIGN_RIGHT)]
if (alignRight != null) {
absoluteRight.linkTo(alignRight.absoluteRight)
}
val alignStart = refs[params.getRule(RelativeLayout.ALIGN_START)]
if (alignStart != null) {
start.linkTo(alignStart.start)
}
val alignTop = refs[params.getRule(RelativeLayout.ALIGN_TOP)]
if (alignTop != null) {
top.linkTo(alignTop.top)
}
val below = refs[params.getRule(RelativeLayout.BELOW)]
if (below != null) {
top.linkTo(below.bottom)
}
val centerHorizontal = params.getRule(RelativeLayout.CENTER_HORIZONTAL)
if (centerHorizontal == RelativeLayout.TRUE) {
centerHorizontallyTo(parent)
}
val centerInParent = params.getRule(RelativeLayout.CENTER_IN_PARENT)
if (centerInParent == RelativeLayout.TRUE) {
centerTo(parent)
}
val centerVertical = params.getRule(RelativeLayout.CENTER_VERTICAL)
if (centerVertical == RelativeLayout.TRUE) {
centerVerticallyTo(parent)
}
val toEndOf = refs[params.getRule(RelativeLayout.END_OF)]
if (toEndOf != null) {
start.linkTo(toEndOf.end)
}
val toLeftOf = refs[params.getRule(RelativeLayout.LEFT_OF)]
if (toLeftOf != null) {
absoluteRight.linkTo(toLeftOf.absoluteLeft)
}
val toRightOf = refs[params.getRule(RelativeLayout.RIGHT_OF)]
if (toRightOf != null) {
absoluteLeft.linkTo(toRightOf.absoluteRight)
}
val toStartOf = refs[params.getRule(RelativeLayout.START_OF)]
if (toStartOf != null) {
end.linkTo(toStartOf.start)
}
}
}

View File

@ -8,10 +8,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.font.DeviceFontFamilyName
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.font.SystemFontFamily
import androidx.compose.ui.text.style.TextAlign
import de.mm20.launcher2.ktx.isAtLeastApiLevel
@ -24,29 +24,46 @@ internal fun ComposeTextView(
Text(
text = view.text.toString(),
color = Color(view.textColors.defaultColor),
style = MaterialTheme.typography.bodyMedium.copy(
fontSize = with(density) {
view.textSize.toSp()
},
fontWeight = if (isAtLeastApiLevel(28)) {
FontWeight(view.typeface.weight)
} else if (view.typeface.isBold) {
FontWeight.Bold
} else {
FontWeight.Normal
},
fontStyle = if (view.typeface.isItalic) {
FontStyle.Italic
} else {
FontStyle.Normal
},
),
style = MaterialTheme.typography.bodyMedium,
lineHeight = with(density) {
view.lineHeight.toSp()
},
maxLines = view.maxLines.coerceAtLeast(1),
modifier = modifier,
textAlign = when (view.textAlignment) {
TextView.TEXT_ALIGNMENT_CENTER -> TextAlign.Center
TextView.TEXT_ALIGNMENT_TEXT_START -> TextAlign.Start
TextView.TEXT_ALIGNMENT_TEXT_END -> TextAlign.End
else -> TextAlign.Start
}
},
letterSpacing = with(density) {
view.letterSpacing.toSp()
},
fontStyle = if (view.typeface.isItalic) {
FontStyle.Italic
} else {
FontStyle.Normal
},
fontSize = with(density) {
view.textSize.toSp()
},
fontWeight = if (isAtLeastApiLevel(28)) {
FontWeight(view.typeface.weight)
} else if (view.typeface.isBold) {
FontWeight.Bold
} else {
FontWeight.Normal
},
minLines = view.minLines.coerceAtLeast(1),
fontFamily = if (isAtLeastApiLevel(34)) {
view.typeface?.systemFontFamilyName?.let {
try {
FontFamily(Font(DeviceFontFamilyName(it)))
} catch (e: IllegalArgumentException) {
null
}
}
} else null
)
}

View File

@ -38,7 +38,7 @@ dependencies {
implementation(libs.bundles.kotlin)
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.constraintlayout.views)
implementation(libs.bundles.androidx.lifecycle)

View File

@ -33,6 +33,7 @@ androidx-browser = "1.8.0"
androidx-palette = "1.0.0"
androidx-media2 = "1.3.0"
androidx-room = "2.6.1"
androidx-constraint-layout = "1.1.0-alpha13"
accompanist = "0.33.2-alpha"
coil = "2.3.0"
@ -97,7 +98,8 @@ accompanist-pagerindicators = { group = "com.google.accompanist", name = "accomp
accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist" }
accompanist-navigationanimation = { group = "com.google.accompanist", name = "accompanist-navigation-animation", version.ref = "accompanist" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
androidx-constraintlayout-views = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
androidx-constraintlayout-compose = { group = "androidx.constraintlayout", name = "constraintlayout-compose", version.ref = "androidx-constraint-layout" }
androidx-transition = { group = "androidx.transition", name = "transition", version = "1.4.1" }
androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version = "1.3.7" }
androidx-securitycrypto = { group = "androidx.security", name = "security-crypto", version = "1.1.0-alpha03" }

View File

@ -43,7 +43,7 @@ dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.materialcomponents.core)
implementation(libs.androidx.browser)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.constraintlayout.views)
implementation(libs.androidx.securitycrypto)
implementation(libs.bundles.androidx.lifecycle)

View File

@ -43,7 +43,7 @@ dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.materialcomponents.core)
implementation(libs.androidx.browser)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.constraintlayout.views)
implementation(libs.androidx.securitycrypto)
implementation(libs.bundles.androidx.lifecycle)