Change an AsyncImage into a bitmap [duplicate] - android

I'm trying to get two images from an url and then I have a Composable that needs two bitmaps to draw them in a Canvas, I've tried it but the canvas don't get painted am I missing something?
val overlayImage =
"https://st2.depositphotos.com/1400069/5999/i/600/depositphotos_59995765-stock-photo-abstract-galaxy-background.jpg"
val baseImage =
"https://www.vitrinesdocomercio.com/uploads/1/3/9/4/13943900/1278180_orig.jpg"
val overlayImageLoaded = rememberAsyncImagePainter(
model = overlayImage,
)
val baseImageLoaded = rememberAsyncImagePainter(
model = baseImage
)
var overlayBitmap = remember<Bitmap?> {
null
}
var baseBitmap = remember<Bitmap?> {
null
}
val overlayImageLoadedState = overlayImageLoaded.state
if (overlayImageLoadedState is AsyncImagePainter.State.Success) {
overlayBitmap = overlayImageLoadedState.result.drawable.toBitmap()
}
val baseImageLoadedState = baseImageLoaded.state
if (baseImageLoadedState is AsyncImagePainter.State.Success) {
baseBitmap = baseImageLoadedState.result.drawable.toBitmap()
}
MyCanvasComposable(baseBitmap, overlayBitmap)

You should assign size for painter to be created, otherwise it returns error
val overlayPainter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data(overlayImage)
.size(coil.size.Size.ORIGINAL) // Set the target size to load the image at.
.build()
)
val basePainter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data(baseImage)
.size(coil.size.Size.ORIGINAL) // Set the target size to load the image at.
.build()
)
Result
You can send both of the ImageBitmaps when both states are success with
#Composable
private fun MyComposable() {
val overlayImage =
"https://st2.depositphotos.com/1400069/5999/i/600/depositphotos_59995765-stock-photo-abstract-galaxy-background.jpg"
val baseImage =
"https://www.vitrinesdocomercio.com/uploads/1/3/9/4/13943900/1278180_orig.jpg"
val overlayPainter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data(overlayImage)
.size(coil.size.Size.ORIGINAL) // Set the target size to load the image at.
.build()
)
val basePainter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data(baseImage)
.size(coil.size.Size.ORIGINAL) // Set the target size to load the image at.
.build()
)
val overlayImageLoadedState = overlayPainter.state
val baseImageLoadedState = basePainter.state
if (
baseImageLoadedState is AsyncImagePainter.State.Success &&
overlayImageLoadedState is AsyncImagePainter.State.Success
) {
SideEffect {
println("🔥 COMPOSING...")
}
val baseImageBitmap =
baseImageLoadedState.result.drawable.toBitmap()
.asImageBitmap()
val overlayImageBitmap =
overlayImageLoadedState.result.drawable
.toBitmap()
.asImageBitmap()
EraseBitmapSample(
baseImageBitmap = baseImageBitmap,
overlayImageBitmap = overlayImageBitmap,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(4 / 3f)
)
}
}
And what you wish to achieve
#Composable
fun EraseBitmapSample(
overlayImageBitmap: ImageBitmap,
baseImageBitmap: ImageBitmap,
modifier: Modifier
) {
var matchPercent by remember {
mutableStateOf(100f)
}
BoxWithConstraints(modifier) {
// Path used for erasing. In this example erasing is faked by drawing with canvas color
// above draw path.
val erasePath = remember { Path() }
var motionEvent by remember { mutableStateOf(MotionEvent.Idle) }
// This is our motion event we get from touch motion
var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
// This is previous motion event before next touch is saved into this current position
var previousPosition by remember { mutableStateOf(Offset.Unspecified) }
val imageWidth = constraints.maxWidth
val imageHeight = constraints.maxHeight
val drawImageBitmap = remember {
Bitmap.createScaledBitmap(
overlayImageBitmap.asAndroidBitmap(),
imageWidth,
imageHeight,
false
)
.asImageBitmap()
}
// Pixels of scaled bitmap, we scale it to composable size because we will erase
// from Composable on screen
val originalPixels: IntArray = remember {
val buffer = IntArray(imageWidth * imageHeight)
drawImageBitmap
.readPixels(
buffer = buffer,
startX = 0,
startY = 0,
width = imageWidth,
height = imageHeight
)
buffer
}
val erasedBitmap: ImageBitmap = remember {
Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888).asImageBitmap()
}
val canvas: Canvas = remember {
Canvas(erasedBitmap)
}
val paint = remember {
Paint()
}
val erasePaint = remember {
Paint().apply {
blendMode = BlendMode.Clear
this.style = PaintingStyle.Stroke
strokeWidth = 30f
}
}
canvas.apply {
val nativeCanvas = this.nativeCanvas
val canvasWidth = nativeCanvas.width.toFloat()
val canvasHeight = nativeCanvas.height.toFloat()
when (motionEvent) {
MotionEvent.Down -> {
erasePath.moveTo(currentPosition.x, currentPosition.y)
previousPosition = currentPosition
}
MotionEvent.Move -> {
erasePath.quadraticBezierTo(
previousPosition.x,
previousPosition.y,
(previousPosition.x + currentPosition.x) / 2,
(previousPosition.y + currentPosition.y) / 2
)
previousPosition = currentPosition
}
MotionEvent.Up -> {
erasePath.lineTo(currentPosition.x, currentPosition.y)
currentPosition = Offset.Unspecified
previousPosition = currentPosition
motionEvent = MotionEvent.Idle
matchPercent = compareBitmaps(
originalPixels,
erasedBitmap,
imageWidth,
imageHeight
)
}
else -> Unit
}
with(canvas.nativeCanvas) {
drawColor(android.graphics.Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
drawImageRect(
image = drawImageBitmap,
dstSize = IntSize(canvasWidth.toInt(), canvasHeight.toInt()),
paint = paint
)
drawPath(
path = erasePath,
paint = erasePaint
)
}
}
val canvasModifier = Modifier.pointerMotionEvents(
Unit,
onDown = { pointerInputChange ->
motionEvent = MotionEvent.Down
currentPosition = pointerInputChange.position
pointerInputChange.consume()
},
onMove = { pointerInputChange ->
motionEvent = MotionEvent.Move
currentPosition = pointerInputChange.position
pointerInputChange.consume()
},
onUp = { pointerInputChange ->
motionEvent = MotionEvent.Up
pointerInputChange.consume()
},
delayAfterDownInMillis = 20
)
Image(
bitmap = baseImageBitmap,
contentDescription = null
)
Image(
modifier = canvasModifier
.clipToBounds()
.matchParentSize()
.border(2.dp, Color.Green),
bitmap = erasedBitmap,
contentDescription = null,
contentScale = ContentScale.FillBounds
)
}
Text(
text = "Bitmap match ${matchPercent}%",
color = Color.Red,
fontSize = 22.sp,
)
}

Related

Create a draggable element that will return to its original position when the user lifts their finger

I'm working on implementing a draggable element using Android Jetpack Compose that will smoothly return to its original position when the user lifts their finger. I came to some implementation by I do not really like it.
#Composable
fun dragAndBack2() {
var offset by remember { mutableStateOf(IntOffset.Zero) }
var isAnimating by remember { mutableStateOf(false) }
var animate = animateIntOffsetAsState(
targetValue = if (isAnimating) {
IntOffset.Zero
} else offset,
animationSpec = tween(
durationMillis = 1000,
easing = LinearEasing
),
finishedListener = {
offset = IntOffset.Zero
}
)
Box(
modifier = Modifier
.offset { if (isAnimating) animate.value else offset }
.pointerInput(Unit) {
detectDragGestures(
onDragCancel = { isAnimating = true },
onDragStart = { isAnimating = false },
onDragEnd = { isAnimating = true },
onDrag = { change, dragAmount ->
change.consumeAllChanges()
val offsetChange =
IntOffset(dragAmount.x.roundToInt(), dragAmount.y.roundToInt())
offset = offset.plus(offsetChange)
}
)
}
.background(Color.Blue)
.size(50.dp)
)
}
It works, but when the finger is lifted, the element jumps some distance away from the finger, as if it is jumping to the last saved position.
#Composable
fun AnimatedBox() {
var isAnimating by remember { mutableStateOf(false) }
val transition = updateTransition(targetState = !isAnimating)
var offset by remember { mutableStateOf(IntOffset.Zero) }
val transitionOffset by transition.animateIntOffset(
transitionSpec = {
keyframes {
durationMillis = 1000
offset at 0 with LinearOutSlowInEasing // start value
IntOffset.Zero at 500
}
},
label = "boxSize"
) { b ->
if (b) {
offset
} else {
IntOffset.Zero
}
}
Box(
modifier = Modifier
.offset { transitionOffset }
.pointerInput(Unit) {
detectDragGestures(
onDragStart = {
offset = IntOffset.Zero
isAnimating = false
},
onDragEnd = {
isAnimating = true
},
onDrag = { change, dragAmount ->
change.consumeAllChanges()
val offsetChange =
IntOffset(dragAmount.x.roundToInt(), dragAmount.y.roundToInt())
offset = offset.plus(offsetChange)
}
)
}
.background(Color.Blue)
.size(50.dp)
)
}
It also works, but if the finger doesn't move, the element quickly moves back to its original position.
There is also some delay in both versions of the realization. Thanks in advance.
Finally i solved it. Here is some delay between finger and element. But it works like I want.
#Composable
fun onTouchWithTransition(position: IntOffset, sizeDp: Dp) {
val fingerPosition by remember { mutableStateOf(Position(position)) }
val placeToReturn by remember { mutableStateOf(Position(position)) }
var testTransition: Position by remember {
mutableStateOf(fingerPosition)
}
var transition: Transition<Position> = updateTransition(targetState = testTransition)
val transitionOffset by transition.animateIntOffset(
transitionSpec = {
keyframes {
durationMillis = 500
}
},
label = "label"
) {
it.offset
}
Box(modifier = Modifier
.offset { transitionOffset }
.clip(CircleShape)
.background(Color.Red)
.pointerInput(Unit) {
detectDragGestures(
onDragStart = {
fingerPosition.offset = position
testTransition = fingerPosition
},
onDragEnd = { testTransition = placeToReturn },
onDrag = { change, dragAmount ->
change.consumeAllChanges()
val offsetChange =
IntOffset(dragAmount.x.roundToInt(), dragAmount.y.roundToInt())
fingerPosition.offset = fingerPosition.offset.plus(offsetChange)
}
)
}
.size(sizeDp, sizeDp)
)
}
class Position(private var off: IntOffset) {
var offset: IntOffset by mutableStateOf(off)
}

Composable LazyColumn/Pager Scroll while zoomed

I need to create the pdf reader, I am sending images bitmap array to lazy compose view, it works fine when there is no zoom but with zoom the issue I am facing is that while scrolling to bottom it became too slow to scroll.
Following is the code
#OptIn(ExperimentalPagerApi::class, ExperimentalFoundationApi::class)
#Composable
fun ViewPager(
list: List<Pair<ImageBitmap, List<Fields>>>,
minScale: Float = 1f,
maxScale: Float = 3f,
scrollEnabled: MutableState<Boolean>,
isRotation: Boolean = false
) {
var targetScale by remember { mutableStateOf(1f) }
val scale = animateFloatAsState(targetValue = maxOf(minScale, minOf(maxScale, targetScale)))
var rotationState by remember { mutableStateOf(1f) }
var offsetX by remember { mutableStateOf(1f) }
var offsetY by remember { mutableStateOf(1f) }
val configuration = LocalConfiguration.current
val screenWidthPx = with(LocalDensity.current) { configuration.screenWidthDp.dp.toPx() }
val screenHeightPx = with(LocalDensity.current) { configuration.screenHeightDp.dp.toPx() }
val focusManager = LocalFocusManager.current
VerticalPager(
modifier= Modifier
.wrapContentSize()
// .verticalScroll(scrollState)
.fillMaxSize(1f)
.background(color = Color.Blue)
.combinedClickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = {
focusManager.clearFocus()
},
onDoubleClick = {
if (targetScale >= 2f) {
targetScale = 1f
offsetX = 1f
offsetY = 1f
scrollEnabled.value = true
} else targetScale = 3f
}
)
.graphicsLayer {
this.scaleX = scale.value
this.scaleY = scale.value
if (isRotation) {
rotationZ = rotationState
}
this.translationX = offsetX
this.translationY = offsetY
Log.e("Ramesh lazycolumn size", "${this.size.width} ${this.size.height}")
}
.pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
awaitFirstDown()
do {
val event = awaitPointerEvent()
val zoom = event.calculateZoom()
targetScale *= zoom
val offset = event.calculatePan()
if (targetScale <= 1) {
offsetX = 1f
offsetY = 1f
targetScale = 1f
scrollEnabled.value = true
} else {
offsetX += offset.x
// todo change
if (offsetY + offset.y < screenHeightPx && offsetY + offset.y > -screenHeightPx) {
offsetY += offset.y
}
if (zoom > 1) {
scrollEnabled.value = false
rotationState += event.calculateRotation()
}
val imageWidth = screenWidthPx * scale.value
val borderReached =
imageWidth - screenWidthPx - 2 * abs(offsetX)
scrollEnabled.value = borderReached <= 0
if (borderReached < 0) {
offsetX =
((imageWidth - screenWidthPx) / 2f).withSign(offsetX)
if (offset.x != 0f) offsetY -= offset.y
}
}
Log.e("Ramesh lazycolumn xy ", "$offsetX $offsetY")
} while (event.changes.any { it.pressed })
}
}
},
count = list.size,
contentPadding = PaddingValues(0.dp),
flingBehavior = ScrollableDefaults.flingBehavior()
) { page ->
val it = list[page]
Page(
pageContent = {
PageContent(
modifier = Modifier,
imagePainter = BitmapPainter(it.first),
fields = it.second
)
},
fields = it.second,
color = if (page % 2 == 0) Color.Yellow else Color.Cyan
)
}
}
I have used the LazyColumn, Pager, Scrollable column but still same issue.
#Composable
fun Page(
modifier: Modifier = Modifier,
fields: List<Fields>,
pageContent: #Composable () -> Unit,
color: Color
) {
Box(
modifier = modifier
// Modifier.fillMaxHeight().background(color)
// .height(placeables[0].height)
) {
Layout(
content = pageContent
) { measurables, constraints ->
val placeables = measurables.map { measurable -> measurable.measure(constraints) }
Log.e(
"Ramesh",
"Placeable ${placeables[0].height} ${placeables[0].width} \nConstraints ${constraints.maxWidth}, ${constraints.maxHeight}"
)
val factor = placeables[0].height
layout(
width = constraints.maxWidth,
height = placeables[0].height
) {
placeables[0].place(0, 0, 0f)
for (index in 1..placeables.lastIndex) {
val fieldValue = fields[index - 1]
val offset = IntOffset(fieldValue.x, fieldValue.y - 70)
placeables[index].placeRelative(offset, 0f)
// placeables[index].placeRelative(fieldValue.x, fieldValue.y, 0f)
// placeables[index].place(fieldValue.x, fieldValue.y - 120, 0f)
}
}
}
}
}
#Composable
fun PageContent(
modifier: Modifier = Modifier,
fields: List<Fields>,
imagePainter: Painter
) {
Image(
painter = imagePainter,
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = modifier
// .onGloballyPositioned {
// val position = it.positionInRoot()
// Log.e("ramesh image coordinates", "x = ${position.x} y = ${position.y}")
// Log.e("ramesh image size", "x = ${it.size.width} y = ${it.size.height}")
// }
)
// fields.forEach {
// InputField(it)
// }
}
Can anyone help me on this?
thanks

Elliptical list in Compose

was wondering if anyone knows how to produce elliptical/arched list in Compose?
Something along these lines:
Not sure If I'm overlooking an 'easy' way of doing it in Compose. Cheers!
I have an article here that shows how to do this. It is not a LazyList in that it computes all the items (but only renders the visible ones); you can use this as a starting point to build upon.
The full code is below as well:
data class CircularListConfig(
val contentHeight: Float = 0f,
val numItems: Int = 0,
val visibleItems: Int = 0,
val circularFraction: Float = 1f,
val overshootItems: Int = 0,
)
#Stable
interface CircularListState {
val verticalOffset: Float
val firstVisibleItem: Int
val lastVisibleItem: Int
suspend fun snapTo(value: Float)
suspend fun decayTo(velocity: Float, value: Float)
suspend fun stop()
fun offsetFor(index: Int): IntOffset
fun setup(config: CircularListConfig)
}
class CircularListStateImpl(
currentOffset: Float = 0f,
) : CircularListState {
private val animatable = Animatable(currentOffset)
private var itemHeight = 0f
private var config = CircularListConfig()
private var initialOffset = 0f
private val decayAnimationSpec = FloatSpringSpec(
dampingRatio = Spring.DampingRatioLowBouncy,
stiffness = Spring.StiffnessLow,
)
private val minOffset: Float
get() = -(config.numItems - 1) * itemHeight
override val verticalOffset: Float
get() = animatable.value
override val firstVisibleItem: Int
get() = ((-verticalOffset - initialOffset) / itemHeight).toInt().coerceAtLeast(0)
override val lastVisibleItem: Int
get() = (((-verticalOffset - initialOffset) / itemHeight).toInt() + config.visibleItems)
.coerceAtMost(config.numItems - 1)
override suspend fun snapTo(value: Float) {
val minOvershoot = -(config.numItems - 1 + config.overshootItems) * itemHeight
val maxOvershoot = config.overshootItems * itemHeight
animatable.snapTo(value.coerceIn(minOvershoot, maxOvershoot))
}
override suspend fun decayTo(velocity: Float, value: Float) {
val constrainedValue = value.coerceIn(minOffset, 0f).absoluteValue
val remainder = (constrainedValue / itemHeight) - (constrainedValue / itemHeight).toInt()
val extra = if (remainder <= 0.5f) 0 else 1
val target =((constrainedValue / itemHeight).toInt() + extra) * itemHeight
animatable.animateTo(
targetValue = -target,
initialVelocity = velocity,
animationSpec = decayAnimationSpec,
)
}
override suspend fun stop() {
animatable.stop()
}
override fun setup(config: CircularListConfig) {
this.config = config
itemHeight = config.contentHeight / config.visibleItems
initialOffset = (config.contentHeight - itemHeight) / 2f
}
override fun offsetFor(index: Int): IntOffset {
val maxOffset = config.contentHeight / 2f + itemHeight / 2f
val y = (verticalOffset + initialOffset + index * itemHeight)
val deltaFromCenter = (y - initialOffset)
val radius = config.contentHeight / 2f
val scaledY = deltaFromCenter.absoluteValue * (config.contentHeight / 2f / maxOffset)
val x = if (scaledY < radius) {
sqrt((radius * radius - scaledY * scaledY))
} else {
0f
}
return IntOffset(
x = (x * config.circularFraction).roundToInt(),
y = y.roundToInt()
)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as CircularListStateImpl
if (animatable.value != other.animatable.value) return false
if (itemHeight != other.itemHeight) return false
if (config != other.config) return false
if (initialOffset != other.initialOffset) return false
if (decayAnimationSpec != other.decayAnimationSpec) return false
return true
}
override fun hashCode(): Int {
var result = animatable.value.hashCode()
result = 31 * result + itemHeight.hashCode()
result = 31 * result + config.hashCode()
result = 31 * result + initialOffset.hashCode()
result = 31 * result + decayAnimationSpec.hashCode()
return result
}
companion object {
val Saver = Saver<CircularListStateImpl, List<Any>>(
save = { listOf(it.verticalOffset) },
restore = {
CircularListStateImpl(it[0] as Float)
}
)
}
}
#Composable
fun rememberCircularListState(): CircularListState {
val state = rememberSaveable(saver = CircularListStateImpl.Saver) {
CircularListStateImpl()
}
return state
}
#Composable
fun CircularList(
visibleItems: Int,
modifier: Modifier = Modifier,
state: CircularListState = rememberCircularListState(),
circularFraction: Float = 1f,
overshootItems: Int = 3,
content: #Composable () -> Unit,
) {
check(visibleItems > 0) { "Visible items must be positive" }
check(circularFraction > 0f) { "Circular fraction must be positive" }
Layout(
modifier = modifier.clipToBounds().drag(state),
content = content,
) { measurables, constraints ->
val itemHeight = constraints.maxHeight / visibleItems
val itemConstraints = Constraints.fixed(width = constraints.maxWidth, height = itemHeight)
val placeables = measurables.map { measurable -> measurable.measure(itemConstraints) }
state.setup(
CircularListConfig(
contentHeight = constraints.maxHeight.toFloat(),
numItems = placeables.size,
visibleItems = visibleItems,
circularFraction = circularFraction,
overshootItems = overshootItems,
)
)
layout(
width = constraints.maxWidth,
height = constraints.maxHeight,
) {
for (i in state.firstVisibleItem..state.lastVisibleItem) {
placeables[i].placeRelative(state.offsetFor(i))
}
}
}
}
private fun Modifier.drag(
state: CircularListState,
) = pointerInput(Unit) {
val decay = splineBasedDecay<Float>(this)
coroutineScope {
while (true) {
val pointerId = awaitPointerEventScope { awaitFirstDown().id }
state.stop()
val tracker = VelocityTracker()
awaitPointerEventScope {
verticalDrag(pointerId) { change ->
val verticalDragOffset = state.verticalOffset + change.positionChange().y
launch {
state.snapTo(verticalDragOffset)
}
tracker.addPosition(change.uptimeMillis, change.position)
change.consumePositionChange()
}
}
val velocity = tracker.calculateVelocity().y
val targetValue = decay.calculateTargetValue(state.verticalOffset, velocity)
launch {
state.decayTo(velocity, targetValue)
}
}
}
}

Android XYPlot graph bottom label size change?

Android graphview load widget using androidplot library in build gradle
implementation "com.androidplot:androidplot-core:1.5.7"
Dynamicaally create chart using below code getresponse based from network call.
In class file my sample code...
val plot = XYPlot(context, "")
val h = context.resources.getDimension(R.dimen.sample_widget_height).toInt()
val w = context.resources.getDimension(R.dimen.sample_widget_width).toInt()
plot.graph.setMargins(100f, 0f, 0f, 16f)
plot.graph.setPadding(0f, 0f, 0f, 0f)
plot.graph.gridInsets.left = 100f
plot.graph.gridInsets.top = 0f
plot.graph.gridInsets.right = 0f
plot.graph.gridInsets.bottom = 40f
plot.legend.textPaint.textSize = 1f
plot.linesPerRangeLabel = 8
plot.linesPerDomainLabel = 8
plot.graph.setSize(Size.FILL)
plot.measure(w, h);
plot.layout(0, 0, w, h);
plot.graph.position(0f, HorizontalPositioning.ABSOLUTE_FROM_LEFT, 0f,
VerticalPositioning.ABSOLUTE_FROM_TOP, Anchor.LEFT_TOP)
val series1Numbers = mutableListOf<Int>()
val series2Numbers = mutableListOf<Int>()
val xLabels = mutableListOf<String>()
for (i in 0 until model.Details.Items.size) {
var item = model.Details.Items[i]
series1Numbers.add(item.TotalScore)
series2Numbers.add(0)
xLabels.add(item.Date)
}
val series1 = SimpleXYSeries(series1Numbers, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY,
"Series1")
val series2 = SimpleXYSeries(series2Numbers, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY,
"Series1")
val series1Format = LineAndPointFormatter(context, R.xml.line_point_formatter_with_labels)
val series2Format = LineAndPointFormatter(context,
R.xml.line_point_formatter_with_labels_2)
plot.graph.setLineLabelEdges(XYGraphWidget.Edge.LEFT, XYGraphWidget.Edge.BOTTOM)
plot.setRangeBoundaries(-110, 110, BoundaryMode.FIXED)
plot.graph.getLineLabelStyle(XYGraphWidget.Edge.LEFT).format = object : Format() {
override fun format(obj: Any, toAppendTo:
StringBuffer, pos: FieldPosition): StringBuffer {
val i = Math.round((obj as Number).toFloat())
L.m("widget y axos label value ", i.toString())
plot.graph.setLineLabelRenderer(XYGraphWidget.Edge.LEFT, MyLineLabelRenderer())
return if (i > 50) {
return toAppendTo.append(context.getString(R.string.str_excellent))
} else if (i > 25 && i <= 50) {
return toAppendTo.append(context.getString(R.string.str_very_good))
} else if (i > 0 && i <= 25) {
return toAppendTo.append(context.getString(R.string.str_good))
} else if (i == 0) {
return toAppendTo.append(context.getString(R.string.str_neutral))
} else if (i < 0 && i >= -25) {
return toAppendTo.append(context.getString(R.string.str_not_good))
} else if (i < -25 && i >= -50) {
return toAppendTo.append(context.getString(R.string.str_be_aware))
} else if (i < -50) {
return toAppendTo.append(context.getString(R.string.str_time_out))
} else {
return toAppendTo.append(context.getString(R.string.str_neutral))
}
}
override fun parseObject(source: String, pos: ParsePosition): Any {
// unused
return ""
}
}
plot.legend.setVisible(false)
plot.getGraph().linesPerDomainLabel = 5
plot.getGraph().linesPerRangeLabel = 5
plot.graph.getLineLabelStyle(XYGraphWidget.Edge.BOTTOM).format = object : Format() {
override fun format(obj: Any, #NonNull toAppendTo:
StringBuffer, #NonNull pos: FieldPosition): StringBuffer {
val i = Math.round((obj as Number).toFloat())
val displayFormat = SimpleDateFormat(prefs.selectedTimeFormat, Locale.US)
val originalFormat = SimpleDateFormat(Constants.DATE_yyyyMMddHHmmss, Locale.US)
var displayTime = ""
if (prefs.selectedTimeFormat.equals(Constants.TWENTYFOUR_HOUR_FORMAT))
displayTime = displayFormat.format(originalFormat.parse(xLabels.get(i)))
else {
displayTime = displayFormat.format(originalFormat.parse(xLabels.get(i)))
displayTime.replace("AM", " am")
displayTime.replace("PM", " pm")
}
plot.graph.setLineLabelRenderer(XYGraphWidget.Edge.BOTTOM, MyLineLabelRenderer())
return toAppendTo.append(displayTime)
}
override fun parseObject(source: String, #NonNull pos: ParsePosition): Any {
return ""
}
}
plot.addSeries(series1, series1Format)
plot.addSeries(series2, series2Format)
val bitmap: Bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
plot.draw(Canvas(bitmap))
remoteViews!!.setImageViewBitmap(R.id.img_graph_view, bitmap)
appWidgetManager!!.updateAppWidget(appWidgetId, remoteViews)
I am tried to reduce bottom label shown text size is not working it's overlapping one another
How to reduce bottom label text size?
setTextsize used below code
plot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.LEFT).getPaint().textSize = 16f

Right way for use kotlin coroutine

I am using kotlin coroutinescope for loading thumnails from video uri and add imageview with that bitmaps in linear layout.
Currently after all thumnail loaded, I am adding into linearlayout. Can anyone suggest me for getting one by one bitmao and adding into linearlayout?
private fun loadThumbnails(uri: Uri) {
val metaDataSource = MediaMetadataRetriever()
metaDataSource.setDataSource(context, uri)
val videoLength = (metaDataSource.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_DURATION).toInt() * 1000).toLong()
val thumbnailCount = 8
val interval = videoLength / thumbnailCount
var listOfImage: ArrayList<Bitmap?> = ArrayList()
for (i in 0 until thumbnailCount - 1) {
try {
var bitmap: Bitmap? = null
val job = CoroutineScope(Dispatchers.IO).launch {
val frameTime = i * interval
bitmap = metaDataSource.getFrameAtTime(frameTime, MediaMetadataRetriever.OPTION_CLOSEST)
bitmap?.let {
val targetWidth: Int
val targetHeight: Int
if (it.height > it.width) {
targetHeight = frameDimension
val percentage = frameDimension.toFloat() / it.height
targetWidth = (it.width * percentage).toInt()
} else {
targetWidth = frameDimension
val percentage = frameDimension.toFloat() / it.width
targetHeight = (it.height * percentage).toInt()
}
bitmap = Bitmap.createScaledBitmap(it, targetWidth, targetHeight, false)
}
listOfImage.add(bitmap)
metaDataSource.release()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
listOfImage.forEach {
container_thumbnails.addView(ThumbnailView(context).apply { setImageBitmap(it) })
}
}
Please try next approach:
val job = CoroutineScope(Dispatchers.Main).launch {
val frameTime = i * interval
val bitmap = loadBitmap(frameTime) // loads bitmap asynchronously using withContext(Dispatchers.IO)
// ... use bitmap to set into a view
}
suspend fun loadBitmap(frameTime: Int): Bitmap? = withContext(Dispatchers.IO) {
bitmap = metaDataSource.getFrameAtTime(frameTime, MediaMetadataRetriever.OPTION_CLOSEST)
bitmap?.let {
val targetWidth: Int
val targetHeight: Int
if (it.height > it.width) {
targetHeight = frameDimension
val percentage = frameDimension.toFloat() / it.height
targetWidth = (it.width * percentage).toInt()
} else {
targetWidth = frameDimension
val percentage = frameDimension.toFloat() / it.width
targetHeight = (it.height * percentage).toInt()
}
Bitmap.createScaledBitmap(it, targetWidth, targetHeight, false)
}
}

Categories

Resources