how to know position for component android jet-pack compose Kotlin - kotlin

I found that function to drag and drop component like in my code simple image
but now i need to found the x and y axis for that image on screen
like for example when i move it to new position put the new position in variable
#Composable
fun ImgLatterMoving(painter: Painter,contentDescription: String) {
val offsetX = remember { mutableStateOf(0F) }
val offsetY = remember { mutableStateOf(0F) }
Box(
modifier = Modifier
.offset {
IntOffset(
x = offsetX.value.roundToInt(),
y = offsetY.value.roundToInt()
)
}
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consumeAllChanges()
offsetX.value += dragAmount.x
offsetY.value += dragAmount.y
}
}
.size(100.dp)
) {
Image(painter = painter,
contentDescription = contentDescription,
modifier = Modifier.clip(RoundedCornerShape(10.dp))
)
}
}

Related

Jetpack compose custom snackbar material 3

How to achieve a custom snackbar in compose using material 3? I want to change the alignment of the snackbar. Also I want dynamic icon on the snackbar either on left or right side.
You can customize your snackbar using SnackBar composable and can change alignment using SnackbarHost alignment inside a Box if that's what you mean.
val snackState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
Box(
modifier = Modifier
.fillMaxSize()
.padding(20.dp)
) {
Column(modifier = Modifier.fillMaxSize()) {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
coroutineScope.launch {
snackState.showSnackbar("Custom Snackbar")
}
}) {
Text("Show Snackbar")
}
}
SnackbarHost(
modifier=Modifier.align(Alignment.BottomStart),
hostState = snackState
) { snackbarData: SnackbarData ->
CustomSnackBar(
R.drawable.baseline_swap_horiz_24,
snackbarData.visuals.message,
isRtl = true,
containerColor = Color.Gray
)
}
}
#Composable
fun CustomSnackBar(
#DrawableRes drawableRes: Int,
message: String,
isRtl: Boolean = true,
containerColor: Color = Color.Black
) {
Snackbar(containerColor = containerColor) {
CompositionLocalProvider(
LocalLayoutDirection provides
if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr
) {
Row {
Icon(
painterResource(id = drawableRes),
contentDescription = null
)
Text(message)
}
}
}
}
Validated answer does not answer the question properly because the icon is provided in a static way, not dynamic. The icon is not passed to showSnackbar.
You can do it by having your own SnackbarVisuals :
// Your custom visuals
// Default values are the same than SnackbarHostState.showSnackbar
data class SnackbarVisualsCustom(
override val message: String,
override val actionLabel: String? = null,
override val withDismissAction: Boolean = false,
override val duration: SnackbarDuration = if (actionLabel == null) SnackbarDuration.Short else SnackbarDuration.Indefinite
// You can add custom things here (for you it's an icon)
#DrawableRes val drawableRes: Int
) : SnackbarVisuals
// The way you decide how to display your custom visuals
SnackbarHost(hostState = snackbarHostState) {
val customVisuals = it.visuals as SnackbarVisualsCustom
Snackbar {
// Here is your custom snackbar where you use your icon
}
}
// To display the custom snackbar
snackbarHostStateScope.launch {
snackbarHostState.showSnackbar(
SnackbarVisualsCustom(
message = "The message",
drawableRes = R.drawable.your_icon_id
)
)
}

How can to create a curved Text Button [duplicate]

This question already has an answer here:
Curved text Jetpack compose
(1 answer)
Closed 7 months ago.
I'm beginner in jetpack compose, especially in Canvas.
I want to create this with Canvas:
But I don't know how.
I could create curved Text but with fillMaxSize and it's not useful for me. Because I want to use this picture as a button in a page and I have some buttons like this one in one page. So I want to have this Canvas with wrapcontent size.
So how can I create that?
The answer mentioned in the comments above is mine. I did some adjustments in order to give you some direction. The code is listed below:
#Composable
fun ButtonWithCurvedText(
#DrawableRes icon: Int,
text: String,
iconSize: Dp,
onClick: () -> Unit,
) {
val density = LocalDensity.current
val imgSize = iconSize
val imageSizePx = with(density) { imgSize.toPx() }
val labelSize = 12.sp
val textToIconPadding = 4.dp
val textToIconPaddingPx = with(density) { textToIconPadding.toPx() }
val boxSize = imgSize + (textToIconPadding * 2) + with(density) { labelSize.roundToPx().toDp() }
val boxSizePx = with(density) { boxSize.toPx() }
val imageInset = ((boxSize - imgSize) / 2)
val imageInsetPx = with(density) { imageInset.toPx() }
val arcTop = imageInsetPx - (textToIconPaddingPx / 2f)
val arcLeft = imageInsetPx - (textToIconPaddingPx / 2f)
val arcBottom = boxSizePx - imageInsetPx + (textToIconPaddingPx / 2f)
val arcRight = boxSizePx - imageInsetPx + (textToIconPaddingPx / 2f)
val vectorPainter = rememberVectorPainter(
ImageVector.vectorResource(icon)
)
Canvas(
modifier = Modifier
.size(boxSize)
.background(Color.Gray)
.clickable {
onClick()
}
) {
drawCircle(Color.LightGray, radius = imageSizePx / 2f)
with(vectorPainter) {
inset(
horizontal = imageInsetPx,
vertical = imageInsetPx
) {
draw(Size(imageSizePx, imageSizePx))
}
}
drawIntoCanvas {
val path = Path().apply {
addArc(arcLeft, arcTop, arcRight, arcBottom, 270f, 180f)
}
it.nativeCanvas.drawTextOnPath(
text,
path,
0f,
0f,
Paint().apply {
textSize = labelSize.toPx()
textAlign = Paint.Align.CENTER
}
)
}
}
}
As you mentioned, you want something responsive. You just need to make some constants used here as parameters. The only "size" param required here is the icon size.
You can use like this:
Row() {
ButtonWithCurvedText(R.drawable.ic_share, "Share", 48.dp, {})
ButtonWithCurvedText(R.drawable.ic_cancel, "Cancel", 72.dp, {})
ButtonWithCurvedText(R.drawable.ic_check, "Check", 96.dp, {})
}
The result is like below:

Jetpack compose scrollable table

I need to have a component that scrolls vertically and horizontally.
Here is what I did so far:
#Composable
fun Screen() {
val scope = rememberCoroutineScope()
val scrollOffset = remember {
mutableStateOf(value = 0F)
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
items(10) {
SimpleRow(
onScrollOffsetChange = {
scrollOffset.value = it
}
)
}
}
}
#Composable
private fun SimpleRow(
onScrollOffsetChange: (Float) -> Unit,
) {
val lazyRowState = rememberLazyListState()
LazyRow(
modifier = Modifier.fillMaxWidth(),
state = lazyRowState
) {
item {
Text(
text = "firstText"
)
}
for (i in 1..30) {
item {
Text(text = "qwerty")
}
}
}
}
When anyone of these SimpleRow is scrolled I want to scroll all of the SimpleRows together.
And I do not know how many SimpleRows I have because they came from our server.
Is there any kind of composable or trick that can do it for me?
You can use multiple rows inside a column that scrolls horizontally and vertically, like this:
#Composable
fun MainContent() {
val scrollStateHorizontal = rememberScrollState()
val scrollStateVertical = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(state = scrollStateVertical)
.horizontalScroll(state = scrollStateHorizontal)
) {
repeat(40){ c ->
Row {
repeat(40){ r ->
Item("col: $c | row: $r")
}
}
}
}
}
#Composable
fun Item(text: String) {
Text(
modifier = Modifier
.padding(5.dp)
.background(Color.Gray),
text = text,
color = Color.White
)
}

Infinite looping row of images

How can I create a scrolling row that scrolls automatically at a fixed speed that loops around the content of a list of images?
I have a lazy row of images as defined below, but haven't found a good way to loop it (like a carousel).
var images: List<String> = listOf()
repeat(8) {
images = images.plus("https://place-puppy.com/300x300")
}
val state = rememberLazyListState()
LazyRow(
modifier = modifier.fillMaxWidth(),
state = state
) {
items(count = images.size) { i ->
val image = images.get(i)
Column(
modifier = Modifier
.width(40.dp)
.aspectRatio(1f)
) {
Image(
painter = rememberImagePainter(image),
contentDescription = null,
modifier = Modifier
.fillMaxSize()
}
}
}
firstly, create an infinite auto-scrolling effect that will be running as long as the composable is active & displayed:
LazyRow() {
....
}
LaunchedEffect(Unit) {
autoScroll(lazyListState)
}
private tailrec suspend fun autoScroll(lazyListState: LazyListState) {
lazyListState.scroll(MutatePriority.PreventUserInput) {
scrollBy(SCROLL_DX)
}
delay(DELAY_BETWEEN_SCROLL_MS)
autoScroll(lazyListState)
}
private const val DELAY_BETWEEN_SCROLL_MS = 8L
private const val SCROLL_DX = 1f
Secondly, update positions of items in the list accordingly:
val lazyListState = rememberLazyListState()
LazyRow(
state = lazyListState,
modifier = modifier,
) {
items(images) {
...
if (it == itemsListState.last()) {
val currentList = images
val secondPart = currentList.subList(0, lazyListState.firstVisibleItemIndex)
val firstPart = currentList.subList(lazyListState.firstVisibleItemIndex, currentList.size)
rememberCoroutineScope().launch {
lazyListState.scrollToItem(0, maxOf(0, lazyListState.firstVisibleItemScrollOffset - SCROLL_DX.toInt()))
}
images = firstPart + secondPart
}
}
}
That should give you the looping behavior.
Credits: https://proandroiddev.com/infinite-auto-scrolling-lists-with-recyclerview-lazylists-in-compose-1c3b44448c8

Recompose a Canvas while maintaining the previous drawing

This function draw figures on canvas but when I clicked on button its shows only latest clicked figure but I want to show all figures which was clicked previous.
I think drawScope state can be remembered as a mutable state but how to achieve it.
#Composable
fun PaintBrushLayout(
modifier: Modifier = Modifier,
) {
var clicked by remember {
mutableStateOf(false)
}
var figName by remember {
mutableStateOf("")
}
Column(
verticalArrangement = Arrangement.Bottom, modifier = modifier.fillMaxSize()
) {
Canvas(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(.9f)
) {
Log.d("in Paint", "PaintBrushLayout: $this")
if (clicked) {
Log.d("TAG", "PaintBrushLayout: $figName")
when (figName) {
"Circle" -> {
drawCircleFig(color = Color.Blue)
}
"Rectangle" -> {
drawRectangleFig()
}
"Oval" -> {
drawOvalFig(color = Color.Gray)
}
}
}
}
Row(
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
) {
BottomLayout("Circle") { click, name ->
clicked = click
figName = name
}
BottomLayout("Rectangle") { click, name ->
clicked = click
figName = name
}
BottomLayout("Oval") { click, name ->
clicked = click
figName = name
}
BottomLayout("Choose Color") { click, name ->
clicked = click
// figName = name
}
}
}
}
Create Bottom Layout of Buttons
#Composable
fun BottomLayout(
btnName: String,
click: (Boolean, String) -> Unit
) {
Button(onClick = {
click(true, btnName)
}, modifier = Modifier.padding(4.dp)) {
Text(text = btnName)
}
}
Draw Circle Figure on canvas
fun DrawScope.drawCircleFig(color: Color = Color.Red) {
drawCircle(
color = color
)
}
Draw Rectangle Figure on canvas
fun DrawScope.drawRectangleFig(color: Color = Color.Red) {
drawRect(
color = color
)
}
Draw Oval Figure on canvas
fun DrawScope.drawOvalFig(color: Color = Color.Red) {
drawOval(
color = color
)
}
I don't know if it's possible to recompose a Canvas and maintain its state prior to what it was previously, allowing you to drawn on top of what is already there. One solution is to keep track of a queue of the operations that you want to perform and then each time the Canvas is recomposed, you iterate through the queue and draw each item. If this is a drawingn app, it has the benefit of allowing the user to undo the previous steps. I also noticed that the code you provided doesn't recompose when you click on a button. I've noticed this issue before. The solution is to assign the remembered value to another value. Here is the sample on using a queue:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val queue = mutableListOf<DrawingObject>()
PaintBrushLayout(queue = queue)
}
}
}
#Composable
fun PaintBrushLayout(
modifier: Modifier = Modifier,
queue: MutableList<DrawingObject>
) {
var currentDrawingObject by remember { mutableStateOf(DrawingObject.None) }
var c = currentDrawingObject // This fixes a bug in recomposition.
fun addToQueue(drawingObject: DrawingObject) {
queue.add(drawingObject)
currentDrawingObject = drawingObject
}
Column(
verticalArrangement = Arrangement.Bottom, modifier = modifier.fillMaxSize()
) {
Canvas(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(.9f)
) {
queue.forEach { drawingObject ->
when (drawingObject) {
DrawingObject.Circle -> {
drawCircleFig(color = Color.Blue)
}
DrawingObject.Rectangle -> {
drawRectangleFig()
}
DrawingObject.Oval -> {
drawOvalFig(color = Color.Gray)
}
}
}
}
Row(
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
) {
BottomLayout("Circle") { click, name ->
addToQueue(DrawingObject.Circle)
}
BottomLayout("Rectangle") { click, name ->
addToQueue(DrawingObject.Rectangle)
}
BottomLayout("Oval") { click, name ->
addToQueue(DrawingObject.Oval)
}
BottomLayout("Choose Color") { click, name ->
}
}
}
}
#Composable
fun BottomLayout(
btnName: String,
click: (Boolean, String) -> Unit
) {
Button(onClick = {
click(true, btnName)
}, modifier = Modifier.padding(4.dp)) {
Text(text = btnName)
}
}
fun DrawScope.drawCircleFig(color: Color = Color.Red) {
drawCircle(
radius = 300f,
color = color
)
}
fun DrawScope.drawRectangleFig(color: Color = Color.Red) {
drawRect(
size = Size(200f, 200f),
color = color
)
}
fun DrawScope.drawOvalFig(color: Color = Color.Red) {
drawOval(
size = Size(100f, 100f),
color = color
)
}
enum class DrawingObject {
None,
Circle,
Rectangle,
Oval
}