I use this code to add extension for Log class android
fun Log.i2(msg:String):Unit{
Log.i("Test",msg)
}
when using in the activity
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i2("activity_main")
}
}
Log.i2 not found. What's wrong?
To achieve extension function in static class, you need to write extension of the companion object(refer this)
fun Log.Companion.i2(msg:String) {
...
}
You have created Extension function of Class Log.
Which is suppose to call by Instance of Class Log. You are trying to treat extension function as static and calling it by Class name. Which is not correct in the case
Currently, static extension methods in Kotlin is not supported without the companion object, because android.util.Log is a java class, so there is no companion object.
Instead, I recommend you to use a static function (package-level function, simply declared outside a class in a source code file):
fun logI2(msg: String) {
Log.i("Test", msg)
}
And just use it like this:
logI2("activity_main")
Related
I have a Kotlin class that is becoming very large (a couple of hundreds of lines). It's mainly because this class is the listener of several interfaces. Usually, I split my class functions with extensions (and place them in separate files). However, when I try that with override functions, Android Studio gives me this error:
"Modifier 'override' is not applicable to 'top level function'"
So, is there a workaround? How would you split a large file with many override functions? (In Swift, this is done using extensions or Partials in C#). Here is an image for reference in Android Studio and in Xcode. In Swift, we simply add "extension" and that allows us to write code as if we were writing right within the class:
Instead of having your Activity implement listener interfaces, make them into anonymous object members. I think this is usually cleaner anyway.
class MyActivity: AppCompatActivity() {
private lateinit var binding: MyActivityBinding
private val someButtonListener = OnClickListener {
binding.text = "Button clicked!"
}
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = MyActivityBinding.inflate(layoutInflater)
binding.button.onClickListener = someButtonListener
}
}
Then, you could break these out into another file by making functions that create them.
class MyActivity: AppCompatActivity() {
private lateinit var binding: MyActivityBinding
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = MyActivityBinding.inflate(layoutInflater)
binding.button.onClickListener = createSomeButtonListener(binding)
}
}
fun createSomeButtonListener(binding: MyActivityBinding) = OnClickListener {
binding.text = "Button clicked!"
}
If you need to call functions in the Activity from these listeners, you'll have to make your Activity a parameter of the function and expose those functions as public, unfortunately. It's not really proper encapsulation, but you typically don't reference Activities from other classes ever, so it's probably not a big deal for it to have some public functions.
This is my code inspired by the answer:
interface MyInterface {
fun itHappened()
}
class MyClass {
lateinit var listener: MyInterface
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myObj = MyClass()
myObj.listener = anonymousListener()
myObj.listener.itHappened()
}
}
fun MainActivity.anonymousListener() = object : MyInterface {
override fun itHappened() {
Log.d("MyTag", "Clicked")
}
}
I want to make playvideo using Exoplayer, but I have a little bit problem with how to access my extension function.
import com.google.android.exoplayer2.ui.PlayerView
class playerViewadapter {
companion object{
fun PlayerView.loadView(){
}
}
}
but loadView extension from my AppCompatActivity not show
class Test:AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.test)
playerViewadapter.loadView()
}
}
How to call loadView from my AppCompatActivity
You have to be in context of the object in order to invoke something of it.
with(playerViewadapter) { } or playerViewadapter.apply { } should do it:
Example:
with(playerViewadapter) {
playerView.loadView()
}
Try it yourself
Why You define extension function in a companion object of another class? Isn't it better to define it in the file Where You have PlayerView or if it is not Your class, create a new file for utils functions?
class PlayerView
{
}
fun PlayerView.loadView()
{
}
And then You can call this function:
val playerView = PlayerView()
playerView.loadView()
In my application i want use Dagger2 and i want show just one image from server and for show image i used Picasso.
I write below codes, but after run application not show me any image into imageview!
For android i use Kotlin language.
Application class :
class App : Application() {
var component: AppComponent? = null
override fun onCreate() {
super.onCreate()
//Init dagger component
component = DaggerAppComponent.builder().modulePicasso(ModulePicasso(this)).build()
}
fun getAppComponent(): AppComponent? {
return component
}
}
Activity class :
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
App().getAppComponent()?.getPicasso()?.load("https://cdn01.zoomit.ir/2017/6/5f0e97c1-9eb7-4176-aa02-35252489ede8.jpg")
?.into(imageView)
}
}
How can i fix it?
I think it's because you are creating a new instance of you application class, eg. App() and then calling getAppComponent() which for sure returns null, as you should not construct the application instance yourself, but instead access a static property referencing it.
To fix it, you need to add a static property (instance) to the App class and get the AppComponent using that property.
I have a few cases where I want to add static functions or values in a base class so that I can use them in all subclasses that inherits from it.
One such case is when i want to create generic tags for each class to use in data mapping as a key, like when i want to find fragments or pass data between activities.
For example:
open class BaseClass(){
companionObject{
val TAG: String = this.javaClass.simpleName
}
}
class ChildClass: BaseClass()
class Main: Activity(){
fun startActivity(){
val intent = Intent(this, ChildClass::class.java)
intent.putExtra(ChildClass.TAG, data)
startActivity(intent)
finish()
}
}
Can this be done or am I forced to create an companion object for each class?
I don't know a solution with companions. But you could use a global reified inline function for the specific use case, you mentioned in your question:
open class BaseClass()
class ChildClass: BaseClass()
inline fun <reified T> tagOf() = T::class.java.simpleName
fun main(args: Array<String>) {
println(tagOf<BaseClass>())
println(tagOf<ChildClass>())
}
Hm... I think, you can't do it. As mentioned in this article: https://proandroiddev.com/a-true-companion-exploring-kotlins-companion-objects-dbd864c0f7f5
companion object is really a public static final class in your BaseClass. So, I think, you can't do this.
Let's say I have an interface Base and that we implement that interface in class Base1 : Base.
I would expect an extension function in the form
fun ArrayList<Base>.myFun()
to also work on arrayListOf(Base1(), Base1()).myFun(), but it doesn't. It requires the list to be of type Base instead of Base1.
Isn't this really strange? Or am I just missing something?
And, what are my options to write a function available on all subclasses of an interface?
Thanks!
You need to allow extension function to accept child implementation
interface Base
class Base1: Base
fun ArrayList<out Base>.myFun() = println(toString())
fun main(args: Array<String>) {
arrayListOf(Base1(), Base1()).myFun()
}
Lets say that you have:
interface Base
class Base1: Base
class Base2: Base
To have a fun for each class that extends your interface, you should create a class with a generic extension:
fun <T : Base> ArrayList<T>.myFun() = arrayListOf<T>()
In this case what you are doing is do an extension on any ArrayList which container class would be any class that extends from your Base interface. In the same way it will return an arraylist of that type.
In code this will look like:
fun myCode(){
val array1 : ArrayList<Base1> = arrayListOf(Base1()).myFun()
val array2 : ArrayList<Base2> = arrayListOf(Base2()).myFun()
}
You can read more about it in the official documentation, it has a lot of generic examples too: https://kotlinlang.org/docs/reference/extensions.html
This:
fun ArrayList<Base>.myFun()
is an extension only on ArrayList<Base>.
You can make the function generic to support any implementers of Base:
fun <T : Base> ArrayList<T>.myFunc() {}
Usage:
ArrayList<Base1>().myFunc()
This should work with array list of any type:
fun <T> ArrayList<T>.myFun()