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()
Related
I'm a beginner in Hilt. I have a library which takes in an interface. The library does some operation and invokes the interface callback. I have an activity which invokes this library by passing the interface implementation. I'd like to know how to inject this using Hilt.
Interface in library
interface InterfaceInLibrary() {
fun callback1()
fun callback2(/*params */)
}
Activity
class MyActivity: InterfaceInLibrary() {
override fun onCreate(savedInstanceState: Bundle?) {
//library initialization
val myLibraryClass = MyLibraryClass.getInstance(this) //passing the InterfaceInLibrary implementation
}
override fun callback1() {
Toast.makeText(this, "callback1", Toast.LENGTH_LONG).show()
}
override fun callback2() {
Toast.makeText(this, "callback2", Toast.LENGTH_LONG).show()
}
}
I would like to know how to inject MyLibraryClass in MyActivity using Hilt.
The only possible way I know (or at least how I am handling this use-case in my projects) is to field inject the concrete class that invokes the interface and then let the activity implement the concrete class and inherit from the callback. Since your interface and your concrete class look kinda weird, I will provide a full implementation here. Let's assume we have the following interface:
Interface
interface IMyCallbackInterface {
fun callbackWithoutParameters()
fun callbackWithParameters(value: String)
}
Then, you need some class to invoke this callback. In my case, this was always a recylerview.adapter, but we will use somethin easier:
Invoking class
class MyInvokingClass #Inject constructor() {
// this interface will be initialized by our activity
private lateinit var callbackListener: IMyCallbackInterface
// This function invokes the first callback
fun someFunctionThatInvokesCallbackWithoutParameters() {
// do some stuff
callbackListener.callbackWithoutParameters()
}
// This function invokes the second callback
fun someFunctionThatInvokesCallbackWithParameters() {
// do some stuff
callbackListener.callbackWithParameters(value = "Hello")
}
// This will be called from our activity to initialize the callback
fun initializeCallback(callbackOwner: IMyCallbackInterface) {
this.callbackListener = callbackOwner
}
}
Then, you need to field inject the class and inherit from the callback inside your activity
Activity or Fragment
class MyActivity : IMyCallbackInterface {
#Inject lateinit var invokingClass: MyInvokingClass
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate()
setContentView(...)
// Since MyActivity implements the interface
// it is an instance of it. So you can simply
// say, that the "owner" of the callback is the activity
invokingClass.initializeCallback(this#MyActivity)
}
override fun callbackWithoutParameters() {
// do some stuff
}
override fun callbackWithParameters(value: String) {
// do some stuff with string
}
}
Because our Activity inherits from the callback and we said in onCreate() that the interfaceOwner of MyInvokingClass is the activity, every time the callback gets invoked, the interface functions inside the activity will be invoked as well.
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 need to open one activity from several different points in the app. Let's say from Settings fragment, Main Activity and Navigation drawer (fragment). I don't want to copy/paste the same method and the method is very specific, it should be exactly the same (because it registeres Firebase events). How to structure the code in effective way? Where to put this method? One idea is to have a global ActivityUtils.kt file with just methods and it would be used to store these methods. I'm interested in the alternatives and what are pros and cons of each.
I would create a companion object in the Activity you need to open:
class YourActivity : AppCompatActivity() {
companion object {
fun start(ctx: Context) {
// put your logic here (registering of Firebase events)
val i = Intent(ctx, YourActivity::class.java)
ctx.startActivity(i)
}
}
}
And call it from another activity:
YourActivity.start(this)
or from another fragment:
YourActivity.start(context)
Use an extension method:
fun Activity.doMyStuff() {}
That can be called from any class extending Activity:
doMyStuff()
Extension functions like this shouldn't go inside a class, but rather inside a file. So if you were to make an ActivityUtils.kt file, don't have any sort of class ActivityUtils {} stuff in it. The function(s) should just go directly into the file.
Why not to use MVP?
Like,
interface IView {
val context: Context
}
interface IPresenter {
fun launchActivity(view: IView)
}
class MyActivityModel
{
var key = "key"
/*some other data*/
fun getParcelableObject(): Parcelable
{
return /*some parcelable from model data*/
}
}
class MyActivity : AppCompatActivity(), IView
{
override val context: Context
get() = this
}
class MyActivityPresenter() : IPresenter
{
private var model: MyActivityModel = MyActivityModel()
override fun launchActivity(view: IView)
{
val intent = Intent(view.context, MyActivity::class.java)
intent.putExtra(model.key, model.getParcelableObject())
view.context.startActivity(intent)
}
fun setSomeDataToModel(someData: Any) {
}
}
/*Everyone who wants to use presenter, must be a Context and implement IView*/
fun use()//in some fragment, or activity implementing IView
{
MyActivityPresenter().launchActivity(this)
//or
val presenter = MyActivityPresenter()
presenter.setSomeDataToModel("some data")
presenter.launchActivity(this)
}
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")
The error occurs when passing this to onResume.
Somehow it doesn't recognize that this implements ActivityLifecycleType, Am I missing something?
open class BaseActivity<ViewModelType: ActivityViewModel<*>>: RxAppCompatActivity(), ActivityLifecycleType {
protected var viewModel: ViewModelType? = null
#CallSuper
override fun onResume() {
super.onResume()
viewModel?.onResume(this) ==> Error Required Nothing, Find BaseActivity<ViewModelType>
}
}
open class ActivityViewModel<in ViewType: ActivityLifecycleType> {
fun onResume(view: ViewType) {
// Do something
}
}
interface ActivityLifecycleType {
fun lifecycle(): Observable<ActivityEvent>
}
Kotlin's generics' more strict that you have you write use the code below:
open class BaseActivity<ViewModelType : ActivityViewModel<ActivityLifecycleType>> : ActivityLifecycleType, RxAppCompatActivity() {
protected var viewModel: ViewModelType? = null
#CallSuper
override fun onResume() {
super.onResume()
viewModel?.onResume(this#BaseActivity) // ==> Error Required Nothing, Find BaseActivity<ViewModelType>
}
}
open class ActivityViewModel<in ViewType : ActivityLifecycleType> {
fun onResume(view: ViewType) {
// Do something
}
}
interface ActivityLifecycleType {
fun lifecycle(): Observable<ActivityEvent>
}
What I've done is to change the declaration in the first line.
Java is too weak to check the generic type but Kotlin do.
Mention there're two things you have to do next:
implement lifecycle in BaseActivity or make it abstract.
it's recommended to use lateinit var viewModel instead of nullable types