Skip to content

Commit e55b967

Browse files
update ImageScaleDemo
1 parent 0c362b8 commit e55b967

File tree

2 files changed

+276
-17
lines changed

2 files changed

+276
-17
lines changed

app/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ dependencies {
5353
implementation 'androidx.core:core-ktx:1.8.0'
5454
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
5555

56+
implementation 'com.github.SmartToolFactory:Compose-Extended-Gestures:1.2.0'
57+
5658
implementation "androidx.compose.ui:ui:$compose_version"
5759
// Tooling support (Previews, etc.)
5860
implementation "androidx.compose.ui:ui-tooling:$compose_version"

app/src/main/java/com/smarttoolfactory/composeimage/demo/ImageScaleDemo.kt

Lines changed: 274 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
package com.smarttoolfactory.composeimage.demo
22

3+
import android.graphics.Bitmap
34
import androidx.compose.foundation.*
45
import androidx.compose.foundation.layout.*
56
import androidx.compose.foundation.shape.RoundedCornerShape
67
import androidx.compose.material3.*
78
import androidx.compose.runtime.*
89
import androidx.compose.ui.Modifier
9-
import androidx.compose.ui.graphics.Color
10-
import androidx.compose.ui.graphics.ImageBitmap
10+
import androidx.compose.ui.draw.clipToBounds
11+
import androidx.compose.ui.geometry.Offset
12+
import androidx.compose.ui.graphics.*
13+
import androidx.compose.ui.graphics.drawscope.Stroke
14+
import androidx.compose.ui.input.pointer.PointerInputChange
1115
import androidx.compose.ui.layout.ContentScale
1216
import androidx.compose.ui.platform.LocalContext
1317
import androidx.compose.ui.res.imageResource
1418
import androidx.compose.ui.text.font.FontWeight
15-
import androidx.compose.ui.unit.dp
16-
import androidx.compose.ui.unit.sp
19+
import androidx.compose.ui.unit.*
1720
import com.smarttoolfactory.composeimage.ContentScaleSelectionMenu
1821
import com.smarttoolfactory.composeimage.ImageSelectionButton
1922
import com.smarttoolfactory.composeimage.R
23+
import com.smarttoolfactory.gesture.MotionEvent
24+
import com.smarttoolfactory.gesture.pointerMotionEvents
2025
import com.smarttoolfactory.image.ImageWithConstraints
2126

2227
/**
@@ -44,7 +49,6 @@ fun ImageWithConstraintsDemo() {
4449
modifier = Modifier
4550
.padding(paddingValues)
4651
.fillMaxSize()
47-
.verticalScroll(rememberScrollState())
4852
.padding(10.dp)
4953
) {
5054
ImageScale(imageBitmap = imageBitmap)
@@ -56,28 +60,52 @@ fun ImageWithConstraintsDemo() {
5660
@Composable
5761
fun ImageScale(imageBitmap: ImageBitmap) {
5862

59-
val modifier = Modifier
60-
.background(Color.LightGray)
61-
.border(2.dp, Color.Red)
62-
.fillMaxWidth()
63-
.aspectRatio(4 / 3f)
63+
var contentScale by remember { mutableStateOf(ContentScale.Fit) }
64+
ContentScaleSelectionMenu(contentScale = contentScale) {
65+
contentScale = it
66+
}
67+
68+
Column(
69+
modifier = Modifier
70+
.fillMaxSize()
71+
.verticalScroll(rememberScrollState())
72+
) {
73+
val modifier = Modifier
74+
.background(Color.LightGray)
75+
.border(2.dp, Color.Red)
76+
.fillMaxWidth()
77+
.aspectRatio(4 / 3f)
78+
79+
80+
ImageWithConstraintsSample(modifier, imageBitmap, contentScale)
81+
Spacer(modifier = Modifier.height(30.dp))
82+
DrawSample(modifier, imageBitmap, contentScale)
83+
Spacer(modifier = Modifier.height(30.dp))
84+
CropSample(modifier, imageBitmap, contentScale)
85+
Spacer(modifier = Modifier.height(30.dp))
86+
ImageSample(modifier, imageBitmap, contentScale)
87+
}
88+
89+
}
90+
91+
@Composable
92+
private fun ImageWithConstraintsSample(
93+
modifier: Modifier,
94+
imageBitmap: ImageBitmap,
95+
contentScale: ContentScale
96+
) {
6497

6598
var text by remember { mutableStateOf("") }
6699

67100
Spacer(modifier = Modifier.height(20.dp))
68101
Text(
69102
text = "ImageWithConstraints ContentScale",
70-
fontSize = 20.sp,
103+
fontSize = 16.sp,
71104
fontWeight = FontWeight.Bold,
72105
color = MaterialTheme.colorScheme.primary,
73106
modifier = Modifier.padding(8.dp)
74107
)
75108

76-
var contentScale by remember { mutableStateOf(ContentScale.Fit) }
77-
ContentScaleSelectionMenu(contentScale = contentScale) {
78-
contentScale = it
79-
}
80-
81109
ImageWithConstraints(
82110
modifier = modifier,
83111
imageBitmap = imageBitmap,
@@ -118,10 +146,157 @@ fun ImageScale(imageBitmap: ImageBitmap) {
118146
modifier = Modifier.padding(8.dp)
119147
)
120148
}
149+
}
150+
151+
@Composable
152+
private fun CropSample(
153+
modifier: Modifier,
154+
imageBitmap: ImageBitmap,
155+
contentScale: ContentScale
156+
) {
121157

158+
Text(
159+
text = "Crop Using ImageScope",
160+
fontSize = 16.sp,
161+
fontWeight = FontWeight.Bold,
162+
color = MaterialTheme.colorScheme.primary,
163+
modifier = Modifier.padding(8.dp)
164+
)
165+
166+
var showDialog by remember { mutableStateOf(false) }
167+
168+
var bitmapRect by remember(imageBitmap, contentScale) {
169+
mutableStateOf(
170+
IntRect(
171+
offset = IntOffset.Zero,
172+
size = IntSize(imageBitmap.width, imageBitmap.height)
173+
)
174+
)
175+
}
176+
177+
val croppedImage = remember(imageBitmap, bitmapRect) {
178+
Bitmap.createBitmap(
179+
imageBitmap.asAndroidBitmap(),
180+
bitmapRect.left,
181+
bitmapRect.top,
182+
bitmapRect.width,
183+
bitmapRect.height
184+
).asImageBitmap()
185+
}
186+
187+
ImageWithConstraints(
188+
modifier = modifier,
189+
imageBitmap = imageBitmap,
190+
contentDescription = null,
191+
contentScale = contentScale
192+
) {
193+
bitmapRect = this.rect
194+
}
195+
196+
197+
Button(
198+
modifier = Modifier
199+
.padding(8.dp)
200+
.fillMaxSize(),
201+
onClick = { showDialog = true }) {
202+
Text("Crop")
203+
}
204+
205+
if (showDialog) {
206+
AlertDialog(onDismissRequest = { showDialog = !showDialog },
207+
208+
text = {
209+
Column(
210+
modifier = Modifier
211+
.fillMaxWidth()
212+
) {
213+
214+
Text(
215+
text = "Original Image",
216+
fontSize = 16.sp,
217+
fontWeight = FontWeight.Bold,
218+
color = MaterialTheme.colorScheme.primary,
219+
modifier = Modifier.padding(8.dp)
220+
)
221+
Image(
222+
modifier = Modifier
223+
.fillMaxWidth()
224+
.aspectRatio(4 / 3f),
225+
bitmap = imageBitmap,
226+
contentScale = ContentScale.FillBounds,
227+
contentDescription = "original image"
228+
)
229+
230+
Text(
231+
text = "Cropped Image",
232+
fontSize = 16.sp,
233+
fontWeight = FontWeight.Bold,
234+
color = MaterialTheme.colorScheme.primary,
235+
modifier = Modifier.padding(8.dp)
236+
)
237+
Image(
238+
modifier = Modifier
239+
.fillMaxWidth()
240+
.aspectRatio(4 / 3f),
241+
bitmap = croppedImage,
242+
contentScale = ContentScale.FillBounds,
243+
contentDescription = "cropped image"
244+
)
245+
}
246+
},
247+
confirmButton = {
248+
Button(onClick = { showDialog = !showDialog }) {
249+
Text("Confirm")
250+
}
251+
},
252+
dismissButton = {
253+
Button(onClick = { showDialog = !showDialog }) {
254+
Text("Dismiss")
255+
}
256+
}
257+
)
258+
}
259+
}
260+
261+
@Composable
262+
private fun DrawSample(
263+
modifier: Modifier,
264+
imageBitmap: ImageBitmap,
265+
contentScale: ContentScale
266+
) {
267+
268+
Text(
269+
text = "Draw Using ImageScope",
270+
fontSize = 16.sp,
271+
fontWeight = FontWeight.Bold,
272+
color = MaterialTheme.colorScheme.primary,
273+
modifier = Modifier.padding(8.dp)
274+
)
275+
276+
ImageWithConstraints(
277+
modifier = modifier,
278+
imageBitmap = imageBitmap,
279+
contentDescription = null,
280+
contentScale = contentScale
281+
) {
282+
283+
val imageWidth = this.imageWidth
284+
val imageHeight = this.imageHeight
285+
286+
Drawing(modifier = Modifier.size(imageWidth, imageHeight))
287+
}
288+
}
289+
290+
// This is a Sample to show ImageWithConstraints match with Compose Image content scale changes
291+
@Composable
292+
private fun ImageSample(
293+
modifier: Modifier,
294+
imageBitmap: ImageBitmap,
295+
contentScale: ContentScale
296+
) {
122297
Text(
123298
text = "Image Content Scale",
124-
fontSize = 20.sp,
299+
fontSize = 16.sp,
125300
fontWeight = FontWeight.Bold,
126301
color = MaterialTheme.colorScheme.primary,
127302
modifier = Modifier.padding(8.dp)
@@ -133,5 +308,87 @@ fun ImageScale(imageBitmap: ImageBitmap) {
133308
contentDescription = null,
134309
contentScale = contentScale
135310
)
311+
}
312+
313+
@Composable
314+
private fun Drawing(modifier: Modifier) {
315+
316+
var motionEvent by remember { mutableStateOf(MotionEvent.Idle) }
317+
// This is our motion event we get from touch motion
318+
var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
319+
// This is previous motion event before next touch is saved into this current position
320+
var previousPosition by remember { mutableStateOf(Offset.Unspecified) }
321+
322+
val brush = remember {
323+
Brush.verticalGradient(
324+
colors = listOf(
325+
Color.Red,
326+
Color.Green,
327+
Color.Yellow,
328+
Color.Blue,
329+
Color.Cyan,
330+
Color.Magenta,
331+
Color.Red,
332+
)
333+
)
334+
}
335+
336+
// Path is what is used for drawing line on Canvas
337+
val path = remember(modifier) { Path() }
338+
339+
val drawModifier = modifier
340+
.clipToBounds()
341+
.pointerMotionEvents(
342+
onDown = { pointerInputChange: PointerInputChange ->
343+
currentPosition = pointerInputChange.position
344+
motionEvent = MotionEvent.Down
345+
pointerInputChange.consume()
346+
},
347+
onMove = { pointerInputChange: PointerInputChange ->
348+
currentPosition = pointerInputChange.position
349+
motionEvent = MotionEvent.Move
350+
pointerInputChange.consume()
351+
},
352+
onUp = { pointerInputChange: PointerInputChange ->
353+
motionEvent = MotionEvent.Up
354+
pointerInputChange.consume()
355+
},
356+
delayAfterDownInMillis = 25L
357+
)
136358

359+
Canvas(modifier = drawModifier) {
360+
when (motionEvent) {
361+
MotionEvent.Down -> {
362+
path.moveTo(currentPosition.x, currentPosition.y)
363+
previousPosition = currentPosition
364+
}
365+
366+
MotionEvent.Move -> {
367+
path.quadraticBezierTo(
368+
previousPosition.x,
369+
previousPosition.y,
370+
(previousPosition.x + currentPosition.x) / 2,
371+
(previousPosition.y + currentPosition.y) / 2
372+
373+
)
374+
375+
previousPosition = currentPosition
376+
}
377+
378+
MotionEvent.Up -> {
379+
path.lineTo(currentPosition.x, currentPosition.y)
380+
currentPosition = Offset.Unspecified
381+
previousPosition = currentPosition
382+
motionEvent = MotionEvent.Idle
383+
}
384+
385+
else -> Unit
386+
}
387+
388+
drawPath(
389+
brush = brush,
390+
path = path,
391+
style = Stroke(width = 4.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round)
392+
)
393+
}
137394
}

0 commit comments

Comments
 (0)