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 {
...
}
Related
I'm developing a huge section of my Android app in Jetpack Compose with the MVVM pattern.
I have a ViewModel father that is extended by all the other ViewModels. There, I have defined an open function which contains the initialization logic of each ViewModel that I need to call every time I enter in a new screen and to call again when something went wrong and the user clicks on the "try again" button.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
fun onTryAgainClick() {
myInitMethod()
}
}
class MyScreen1ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
class MyScreen2ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
Is there a way I can call this method in the init function of MyFatherViewModel instead of doing it in all the children ViewModels? If I try to do that, it gives me the "Calling non-final function in constructor" warning and, of course, it doesn't work.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
init {
myInitMethod()
}
fun onTryAgainClick() {
myInitMethod()
}
}
Is it possible to call a non-final function in constructor?
Technically yes, but you shouldn't. Kotlin is trying to protect you from problems here. If you call an open function from a constructor, it means you are running code from the child class before the parent class is completely initialized, and before the child class even started initializing. If the child implementation of the open function tries to access properties from the child class, unexpected things may happen. For instance, non-nullable properties could yield null (because not initialized yet), or primitive values could yield their type's default instead of the default value from their initializer:
fun main() {
Child()
}
open class Parent {
init {
initialize()
}
val id = 42
open fun initialize() = println("parent init")
}
class Child : Parent() {
val name = "Bob"
override fun initialize() = println("initializing $name, parent id=$id")
}
This prints the following output:
initializing null, parent id=0
I guess you can see why this is dangerous.
Maybe you should reconsider what you're trying to do with this try-again feature. Maybe a new view model should be instantiated instead (if try-again is to handle crashes, the state of the current view model may actually be bad enough to want to re-create it from scratch anyway).
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)
}
I have hit a wall with this one and I can't find any question with a solution for this here in SO.
I am using a PagingAdapter method, from Google's Paging library, that receives an inline function as a listener:
fun addLoadStateListener(listener: (CombinedLoadStates) -> Unit) {
differ.addLoadStateListener(listener)
}
And then to remove the listener they provide the following method
fun removeLoadStateListener(listener: (CombinedLoadStates) -> Unit) {
differ.removeLoadStateListener(listener)
}
And I am using it like this
myPagingAdapter.addLoadStateListener { it: CombinedLoadStates ->
myPagingAdapter.removeLoadStateListener(this)
}
I know the above does not work, but it worked when the file was written in java since it had a correct reference to itself inside its own function. However, in Kotlin I cannot find a way to do this at all. I tried turning into an anonymous function, but it still won't pass the correct context
myPagingAdapter.addLoadStateListener { fun(it: CombinedLoadStates) ->
myPagingAdapter.removeLoadStateListener(this)
}
At this point I have no idea how I can remove an inline function that can't reference itself, and I cannot find any documentation with a solution for this anywhere.
How can I remove in kotlin an inline function by referencing itself?
If I understand correctly, you need a reference of inline function which was passed in addLoadStateListener so you can pass in removeLoadStateListener.
You can try this
myPagingAdapter.addLoadStateListener(object : (String) -> Unit {
override fun invoke(p1: String) {
myPagingAdapter.removeLoadStateListener(this)
}
})
You can create a local function to reference itself:
fun myFun(CombinedLoadStates): Unit {
myPagingAdapter.removeLoadStateListener(::myFun)
}
myPagingAdapter.addLoadStateListener(::myFun)
I'm trying to call recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView,null,0) only after mDiffer.submitlist(list) is finished "diffing" and animating the list updates/changes.
Is there an AsyncListDiffer feature for onAfterSubmitList(Callback callback) that I could use to achieve this?
If not, is there anyway to find out when does submitList() finish its task so I could put my scrollToPosition(0) in there?
Update July 2020:
Google added a callback for this! See: https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer#submitList(java.util.List%3CT%3E,%20java.lang.Runnable)
Previous answer, from the bad old days:
First of all, I can't believe Google didn't provide a callback for this.
I dived into the source code of AsyncListDiffer, and I found that it's possible to receive a callback when all RecyclerView updates have been done - but the way to receive this callback is just bizarre.
You need to create a sub-class of BatchingListUpdateCallback, and then wrap your ListUpdateCallback (or AdapterListUpdateCallback as in most cases) inside it.
Then, you should override dispatchLastEvent. AsyncListDiffer will call this after all but one of the updates have been dispatched. In your dispatchLastEvent, you'll want to call the super implementation, so you don't break anything. You'll also want to invoke a custom callback, which is your way in.
In Kotlin that looks like:
class OnDiffDoneListUpdateCallback(
listUpdateCallback: ListUpdateCallback,
private val onDiffDoneCallback: () -> Unit
) : BatchingListUpdateCallback(listUpdateCallback) {
override fun dispatchLastEvent() {
super.dispatchLastEvent()
onDiffDoneCallback()
}
}
The last step is to then provide your custom OnDiffDoneListUpdateCallback to the AsyncListDiffer. To do this, you need to initialise the AsyncListDiffer for yourself - if you're using ListAdapter or something similar, you'll need to refactor so that you are in control of the AsyncListDiffer.
In Kotlin, that looks like:
private val asyncListDiffer = AsyncListDiffer<ItemType>(
OnDiffDoneListUpdateCallback(AdapterListUpdateCallback(adapter)) {
// here's your callback
doTheStuffAfterDiffIsDone()
},
AsyncDifferConfig.Builder<ItemType>(diffCallback).build()
)
Edit:
I forgot about the edge-cases, of course!
Sometimes, dispatchLastEvent isn't called, because AsyncListDiffer considers the update trivial. Here's the checks it does:
if (newList == mList) {
...
return;
}
// fast simple remove all
if (newList == null) {
...
mUpdateCallback.onRemoved(0, countRemoved);
return;
}
// fast simple first insert
if (mList == null) {
...
mUpdateCallback.onInserted(0, newList.size());
return;
}
I recommend doing these checks for yourself, just before you call asyncListDiffer.submitList(list). But of course, it couldn't be that easy! mList is private, and getCurrentList will return an empty list if mList is null, so is useless for this check. Instead you'll have to use reflection to access it:
val listField = AsyncListDiffer::class.java.getDeclaredField("mList")
listField.isAccessible = true
val asyncDifferList = listField.get(asyncListDiffer) as List<ItemType>?
asyncListDiffer.submitList(newList)
if(asyncDifferList == null) {
onDiffDone()
}
if(newList == null) {
onDiffDone()
}
if(newList == asyncDifferList) {
onDiffDone()
}
Edit 2: Now, I know what you're saying - surely there's an easier, less hacky way to do this? And the answer is... yes! Simply copy the entire AsyncListDiffer class into your project, and just add the callback yourself!
You can get advantage from registerAdapterDataObserver methods by listening to them (or what your needs need) i.e:
listAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
//
}
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
}
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
}
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
}
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
}
})
You can clear the register of your adapterObserver once needed using unregisterAdapterDataObserver.
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.