Usage of "with" function - android

is there somebody who can explain me what "with" function is used for?
Signature
public inline fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
Doc
Calls the specified function f with the given receiver as its receiver and returns its result.
And I found its using on this project Antonio Leiva. It was using for moving view :
fun View.animateTranslationY(translationY: Int, interpolator: Interpolator) {
with(ObjectAnimator.ofFloat(this, "translationY", translationY.toFloat())) {
setDuration(context.resources.getInteger(R.integer.config_mediumAnimTime).toLong())
setInterpolator(interpolator)
start()
}
}
I was thinking that I know the meaning to I transfer it to
fun View.animateTranslationX(translationX: Int, interpolator: Interpolator) {
with(ObjectAnimator()) {
ofFloat(this, "translationX", translationX.toFloat())
setDuration(context.resources.getInteger(R.integer.config_mediumAnimTime).toLong())
setInterpolator(interpolator)
start()
}
}
but it doesn't compile ... But I think that ObjectAnimaton is receiver and it get everything what I will call in {} bracket. Can anybody explain the real meaning and provide a basic example - at least more basic than this? :D

The idea is the same as with keyword in Pascal.
Anyway, here are three samples with identical semantic:
with(x) {
bar()
foo()
}
with(x) {
this.bar()
this.foo()
}
x.bar()
x.foo()

I think that I understood what "with" do. Look at code:
class Dummy {
var TAG = "Dummy"
fun someFunciton(value: Int): Unit {
Log.d(TAG, "someFunciton" + value)
}
}
fun callingWith(): Unit {
var dummy = Dummy()
with(dummy, {
someFunciton(20)
})
with(dummy) {
someFunciton(30)
}
}
If I run this code I get one calling of someFunciton with 20 and then with 30 param.
So the code above can be tranfer to this :
fun View.animateTranslationX(translationX: Int, interpolator: Interpolator) {
var obj = ObjectAnimator()
with(obj) {
ofFloat(this, "translationX", translationX.toFloat())
setDuration(context.resources.getInteger(R.integer.config_mediumAnimTime).toLong())
setInterpolator(interpolator)
start()
}
}
and I should work - so we have to have var.

Related

What are the differences between kotlin variable functions and common fun functions?

There is a fun method0:
private fun method0() {
println("method0 fun")
}
And a var method0 :
var method0 = {
println("method0")
}
It seems they are used the same:
method0()
I found that both occur at the same time, and the fun function has a higher priority when the code calls.
Other than that, is there any difference between them?
The var way of doing it results in a functional object. The lambda content is wrapped as a functional object so it can be passed around like any other instance of a class. It can directly be used as a function parameter, for instance.
var method0 = {
println("method0")
}
fun doSomethingTwice(action: ()->Unit) {
repeat(2) { action() }
}
fun main() {
doSomethingTwice(method0)
}
And since it's marked as a var you can swap it out for a different function:
fun main() {
method0 = { println("hello, world!") }
doSomethingTwice(method0)
}
Note that this way of specifying a function is a little bit heavier since it is wrapping the function in another class instance.
And you can still wrap any "regular" function into a functional object at any time by using :: to avoid doing it until it's necessary.
fun method0() {
println("method0")
}
fun main() {
doSomethingTwice(::method0)
}

How to return a value from a coroutine from a method that is non suspending?

What I've tried so far
fun getCPByID(ids: List<Int>): List<CheckingPointVo> {
var list : List<CheckingPointVo> = emptyList()
coroutineScope.launch {
list = someMethod()
}
return list
}
here I tried to use async and await but that cannot be run from a non suspend function. Is there a way to do this ?
Not really with the current structure, you're basically trying to combine synchronous code with async.
You have 3 possible options though to make it async:
Use a callback:
fun getCPByID(ids: List<Int>, listCallback: (List<CheckingPointVo>) -> Unit) {
coroutineScope.launch {
listCallback(someMethod())
}
}
Note: If you're using it from Java, this should work with either Java lambdas or Function. But you may create an interface for this, like :
Interface ListCallback {
fun onListReceived(list: List<CheckingPointVo>)
}
fun getCPByID(ids: List<Int>, listCallback: ListCallback) {
.... // Same implementation
}
// Call it from Java
getCPByID(ids, new ListCallback() {
void onListReceived(List<CheckingPointVo> list) {
...
}
});
Use either an observable pattern, use a Flow or LiveData. A possible example:
fun getCPByID(ids: List<Int>) = coroutineScope.launch {
flow {
emit(someMethod())
}
}
}
Make your function a suspend function and use coroutineScope.launch from the caller

Android KTX: how to override Kotlin added property extension

I am trying to override View.setRotation() method in Kotlin.
Since AndroidKTX already provided property extension "rotation", caller's can simply call
viewObject.rotation = 90.0f
to rotate the view.
However, I want to add some additional operation when user change the rotation, like
override fun setRotation(newRotation: Float) {
if (rotation == newRotation)
return
rotation = newRotation
doSomethingElse()
}
This will crash because of StackOverflow error.
So, I have to add some additional code to achieve the goal:
private var _rotation: Float = 0.0f
override fun setRotation(newRotation: Float) {
if (_rotation == newRotation) {
return
}
_rotation = newRotation
updateRotationInternally()
}
private fun updateRotationInternally() {
super.setRotation(_rotation)
doSomethingElse()
}
This works, but I wonder if there is some other more elegant way of doing this, like "override the property extension setter"?
I disagree with one aspect of your implementation: your return shortcut. You are assuming that calling setRotation() with the existing rotation value has no effect. It would not surprise me if that is true in the official Google version of View, but for all we know, that is not a safe assumption on some Oppo device running their modified version of Android 8.0. Try not to assume the behavior of stuff that you didn't write. If you want to skip doSomethingElse() when the old and new rotations are equal, that's fine.
I am guessing that your setRotation() functions are in some subclass of View. If so, and taking my above complaint into account, here's the simplest that I could come up with:
class Bar : View() {
override fun setRotation(f: Float) {
val needSomething = getRotation() != f
super.setRotation(f)
if (needSomething) doSomethingElse()
}
fun doSomethingElse() {
println("got here!")
}
}
My overall test code was done in a Kotlin scratchpad (outside of Android), so I tested with a fake View implementation and fake rotation extension property:
open class View {
private var r: Float = 0.0f
open fun setRotation(f: Float) {
r = f
}
fun getRotation() = r
}
var View.rotation: Float
get() = getRotation()
set(value) = setRotation(value)
class Bar : View() {
override fun setRotation(f: Float) {
val needSomething = getRotation() != f
super.setRotation(f)
if (needSomething) doSomethingElse()
}
fun doSomethingElse() {
println("got here!")
}
}
fun main() {
val bar = Bar()
bar.rotation = 15.0f
bar.rotation = 15.0f
}
If you run this in that scratchpad, you will see got here printed on the console once, showing that while we successfully updated the extension property twice, we skipped getSomethingElse() on the second call, as the old and new rotation were the same.

Kotlin: pass a reference / pointer to a function

For Android development I am using Kotlin. There are various buttons (buttonA, buttonB) to call the same function. The only difference is that the same function is called with different parameters (REQUEST_A, REQUEST_B). Following Code is running fine:
fun standardizedFunction(requestCode: Int){
....}
buttonA.setOnClickListener { standardizedFunction(REQUEST_A) }
buttonB.setOnClickListener { standardizedFunction(REQUEST_B) }
Now the question: Is there a way to make it more elegant? like
fun standardizedFunction(Object: Pointer, requestCode: Int){
Object.setOnClickListener{
....
}
}
standardizedFunction(buttonA,REQUEST_A)
standardizedFunction(buttonB,REQUEST_B)
You can make it "nicer" by using an extension method to View to setup the listener:
fun View.standardizedFunction(requestCode: Int) = setOnClickListener {
...
}

Queue multiple async calls with callbacks

I need to fetch some images from the gallery, process them (resize, compress...) and save them to a certain path. However, i need to queue the calls because older devices won't be able to process multiple images at the same time.
I am using Glide, this is the code used for processing one image:
fun processImage(context: Context, sourcePath: String, destinationPath: String, quality: Int, width: Int, height: Int, deleteOriginal: Boolean, callback: ((success: Boolean) -> Unit)) {
val sourceFile = File(sourcePath)
val destinationFile = File(destinationPath)
GlideApp.with(context)
.asBitmap()
.load(sourceFile)
.into(object : SimpleTarget<Bitmap>(width, height) {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
try {
destinationFile.writeBytes(ImageUtilities.imageToByteArray(resource, quality, Bitmap.CompressFormat.JPEG, false))
if (deleteOriginal) {
val originalFile = File(sourcePath)
originalFile.delete()
}
callback.invoke(true)
} catch (ex: Exception) {
callback.invoke(false)
}
}
})
}
Now i am queuing the calls manually by calling processNextImage which calls itself recursively until all the images are processed:
private fun processImages(sourceImagePaths: List<String>) {
processNextImage(sourceImagePaths, 0)
}
private fun processNextImage(sourceImagePaths: List<String>, index: Int) {
val imagePath = sourceImagePaths[index]
val destination = FileUtilities.generateImagePath()
processImage(this, imagePath, destination, 90, 1000, 1000, false) {
processedImagePaths.add(destination)
if (index + 1 < sourceImagePaths.count())
processImage(sourceImagePaths, index + 1)
else
success()
}
}
However I don't think this is the best way to do it and I tried to look into Kotlin coroutines but all I found were examples when the queued code is already blocking, which doesn't fit my case because Glide already handles the resizing asynchronously and returns the result in a callback onResourceReady
Any ideas for a clean way to do this?
As described in the official documentation, there is a simple pattern to follow if you want to turn a callback-based API into one based on suspendable functions. I'll paraphrase that description here.
Your key tool is the function from the standard library called suspendCoroutine(). Assume that you have someLongComputation function with a callback that receives a Result object:
fun someLongComputation(params: Params, callback: (Result) -> Unit)
You can convert it into a suspending function with the following straightforward code:
suspend fun someLongComputation(params: Params): Result =
suspendCoroutine { cont ->
someLongComputation(params) { cont.resume(it) }
}
Note how the type of the object passed to the original callback became simply the return value of the suspendable function.
With this you can see the magic of coroutines happen right in front of you: even though it looks exactly like a blocking call, it isn't. The coroutine will get suspended behind the scenes and resume when the return value is ready — and how it will resume is totally under your control.
I was able to solve the issue using suspendCoroutine as suggested in Marko's comment, here is my code:
private fun processImages(sourceImagePaths: List<String>) {
async(UI) {
sourceImagePaths.forEach { path ->
processNextImage(path)?.let {
processedImagePaths.add(it)
}
}
if (processedImagePaths.isEmpty()) finishWithFailure() else finishWithSuccess()
}
}
private suspend fun processNextImage(sourceImagePath: String): String? = suspendCoroutine { cont ->
val destination = FileUtilities.generateImagePath()
processImage(this, sourceImagePath, destination, 90, 1000, 1000, false) { success ->
if (success)
cont.resume(destination)
else
cont.resume(null)
}
}
The method processImages iterates over the list of paths, and calls processNextImage for each path. Since processNextImage contains a suspendCoroutine, it will block the thread until cont.resume is called, which guarantees that the next image will not be processed before the current one is done.

Categories

Resources