Using interface in Fragment - android kotlin - android

I create a Interface like bellow:
interface LevelsViewListener {
fun isVisible()
}
and in my activity is:
class MainActivity : AppCompatActivity(){
var levelsViewListener: LevelsViewListener? = null
fun initTEST(listener: LevelsViewListener){
this.levelsViewListener = listener
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(context as MainActivity).levelsViewListener
btnClick.....{
levelsViewListener!!.isVisible()
}
}
}
And in my fragment is:
class LessonsFragment : Fragment(), LevelsViewListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activity as MainActivity?)?.initTEST(this#LessonsFragment)
}
}
But get me bellow error in LessonsFragment :

If a class implements two interfaces (or, let's say, implements one interface and extends one class, as in your case), and each interface defines a method that has an identical signature, then in fact there is only one method, and they are not distinguishable. If, say, two methods have conflicting return types, then it will be a compilation error. This is what happens in your fragment class.
The problem is that the Fragment class also has an isVisible function, but it returns boolean. And your isVisible function in LevelsViewListener returns Unit.
There are two solutions to this problem.
You should return boolean in your isVisible function in LevelsViewListener interface.
You can change the name of the function in LevelsViewListener interface

The problem is that isVisible() function is already defined as part of the Fragment class. As reported in the error message the signature is:
final public boolean isVisible()
and its return value is boolean. Conversely, your interface LevelsViewListener defines a method with the same name but without return value. This scenario is not allowed by Java/Kotlin.

Related

How to pass a button click from Fragment that is in a BottomSheet Toolbar to the Main Activity?

I have implemented a TabLayout (which uses fragments) in my bottom sheet toolbar that has buttons which should affect the Main Activity. How do I pass the button clicks from the fragments in my TabLayout to the Main Activity?
I'm stuck and I don't know where to start.
There are multiple methods to do communication between fragments and its activity . I'll explain the ones which are used widely.
Using an interface.
Using a SharedViewModel for all your fragments and its activity . ( this can be used if you are implementing MVVM architecture )
check this out link
EDIT :
This is a simple step by step implementation on how to pass data from a fragment to activity . I am just using dummy class names and method parameters .
Create a folder called 'listeners' inside your app module , this is where you should have all your interface classes. ( This is just for a clean approach , if that is not your priority then you can save the interface class anywhere ). for Eg I am making TabLayoutFragmentClickListner.
interface TabLayoutFragmentClickListener {
}
Add a method to this interface . This is the method which would be called when we click a button inside the fragment. add the required parameters which needs to be passed from fragment to the activity. In this case I am just using a String.
interface TabLayoutFragmentClickListener {
fun onLayoutFragmentClick(value : String)
}
Implement this interface in the activity in which you want the data to be received.
class MainActivity : AppCompatActivity() , TabLayoutFragmentClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
This would make you override the interface method inside that activity.
class MainActivity : AppCompatActivity() , TabLayoutFragmentClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onLayoutFragmentClick(value: String) {
Log.d("testing" , value)
}
}
This overiden method is the definition for your interface method in this activity. Hence when you access the interface method from your fragment, the overidden method inside the activity would be called. Try understanding how interface works in java or kotlin.
Now initialise the instance of the listener in your fragment's onAttach method. like this
class TestingFragment : Fragment() {
lateinit var listener: TabLayoutFragmentClickListener
override fun onAttach(context: Context) {
super.onAttach(context)
listener = context as TabLayoutFragmentClickListener
}
}
now call the interface method from your fragment with the required parameter. This would hence trigger the interface method definition in your activity hence passing data from the fragment to the activity.
class TestingFragment : Fragment() {
lateinit var listener: TabLayoutFragmentClickListener
override fun onAttach(context: Context) {
super.onAttach(context)
listener = context as TabLayoutFragmentClickListener
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
test_btn.setOnClickListener {
listener.onLayoutFragmentClick("testing string")
}
}
Hope this helps.
I think You need to create a function in MainActivity And call that function in the fragment. You can easily access that function bcoz it is your parent activity so you can use it on the button.
References:
I know this is in java but I think it's helpful for you link

How to bind external library class with callback in Hilt?

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.

How to keep Kotlin property syntax when defining an Interface?

CLARIFICATION: property syntax == val s = supportFragmentManager as opposed to val s = getSupportFragmentManager()
If I'm writing an interface for an Activity class and want to expose a property while retaining the property syntax, how do I do it?
Create a class inheriting AppCompatActivity and implements the following interface.
interface MyInterface {
fun getSupportFragmentManager: FragmentManager // Option 1 Boo!
val supportFragmentManager: FragmentManager // Option 2 Yey!
}
Option 1 will work just fine.
MyActivity already contains a function called getSupportFragmentManager() so I don't have to implement it.
Option 2 will prompt me to implement the missing property, and doing so I will result in the following
class MyActivity: AppCompatActivity(), MyInterface {
override val supportFragmentManager: FragmentManager
get() = TODO("Not yet implemented")
}
This will give me an error since there already is a function with the same signature in the class.
Accidental override: The following declarations have the same JVM signature (getSupportFragmentManager()Landroidx/fragment/app/FragmentManager;):
fun <get-supportFragmentManager>(): FragmentManager defined in com.my.project.MyActivity
fun getSupportFragmentManager(): FragmentManager defined in com.my.project.MyActivity
Any ides of how to keep Kotlins property syntax throughout interfaces?
I just checked the compiler warnings, and the Accidental override exception you're getting is because of defining both the get function and the original variable itself.
You could do it like this:
interface MyInterface {
val supportFragmentManager: FragmentManager
}
class MyActivity: AppCompatActivity(), MyInterface {
override val supportFragmentManager: FragmentManager = FragmentManager(...)
get() = ... //logic (getter is optional, by default kotlin generates getter itself)
}
PS: In getter you can use field to access the variable aka supportFragmentManager like
get() = field.apply { updateSomthing() }
this calls MyActivity.updateSomthing() and return field itself.

Android&Kotlin lifecycle events in interface

Is there any possibility to trigger some default methods in Kotlin interfaces with lifecycle events of, for example, an Activity that implements that interface?
So, I have such interface, that called in Swift - protocol:
interface MyInterface {
fun showToast() {
this as MyActivity
Toast.show(this, "Welcome", Toast.LENGTH_SHORT).show()
}
}
And Activity class:
class MyActivity : AppCompatActivity(), MyInterface {
fun onResume() {
super.onResume()
showToast() //I want this method be called automatically, if possible
}
}
As you can see I should call showToast() method directly. But is there any possibility to call it automatically with, for example, LifeCycleObserver events or somehow else?
You can extend LifecycleObserver interface and use appropriate annotations, for example:
interface LifecycleInterface : LifecycleObserver{
#OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onLifeResume(){
(this as? Context).let{Toast.makeText(it, "Resumed", Toast.LENGTH_LONG).show()}
}
#OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onLifePause(){
(this as? Context).let{Toast.makeText(it, "Paused", Toast.LENGTH_LONG).show()}
}
}
Then register activity itself (or any custom object for that matter) as listener for lifecycle events:
class MainActivity : AppCompatActivity(), LifecycleInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(this) // add this to trigger lifecycle methods from interface
setContentView(R.layout.activity_main)
// rest of your onCreate...
}
}
Edit:
After showing bytecode and decompiling back to java, I end up with those two methods injected into activity:
#OnLifecycleEvent(Event.ON_RESUME)
public void onLifeResume() {
DefaultImpls.onLifeResume(this);
}
#OnLifecycleEvent(Event.ON_PAUSE)
public void onLifePause() {
DefaultImpls.onLifePause(this);
}
For anyone still facing issue due to compiler adding a parameter in default function of the interface, this is how you fix it:
Instead of using kapt to process the annotations:
kapt "androidx.lifecycle:lifecycle-compiler:$archLifecycleVersion"
Use annotationProcessor for lifecycler compiler:
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$archLifecycleVersion"
This works with kotlin code.

add extension on Log in android (Kotlin)

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")

Categories

Resources