In Kotlin, how does a constructor with anonymous class work? - android

Consider a functionality F, which depends on Android lifecycle methods. I have implemented this functionality in an Activity A. Any other activity which wants to implement this functionality can simply extend A. The results are sent back to the child activity via an interface. Example:
// interface
interface ACallbacks {
fun onResult(string: String)
}
// Activity A
open class AActivity
(private val aCallbacks: ACallbacks): AppCompatActivity() {
// functionality F, which depends on Android lifecycle methods
}
// Activity B
class BActivity: AActivity(object: ACallbacks {
override fun onResult(string: String) {
// Q: how to use string in BActivity?
}
}) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_b)
}
}
How to access the result from Activity A (i.e., string) in Activity B. For example, how do I set it in a textView present in Activity B?

If BActivity inherits from AActivity - inside BActivity you can get what you want:
override fun someFun(): String {
val parentResult = super.someFun()

Related

UninitializedPropertyAccessException: lateinit property binding has not been initialized

I am getting a random crash "lateinit property binding has not been initialized". Most of the time it's working fine but a few time randomly we are getting this crash on crashlytics.
Please let me know what's wrong here
I have a BaseActivity with following code
abstract class BaseActivity<D : ViewDataBinding> : AppComptActivity() {
abstract val layoutId: Int
lateinit val binding: D
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState:Bundle)
binding = DataBindingUtil.setContentView(this, layoutId)
....
}
}
I have a HomeActivity which override BaseActivity with following code
class HomeActivity : BaseActivity<ActivityHomeBinding>() {
override val layoutId: Int get() = R.layout.activity_home
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState:Bundle)
....
}
}
I am using bottomNavigation menu and one of the fragment is HomeFragment
class HomeFragment : BaseFragment<FragmenntHomeBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState:Bundle)
(activity as HomeActivity).binding.appBarHome.visible(false)
//HERE I AM GETTING lateinit property binding has not been initialized crash
}
}
I don't want to use isInitialized property of lateinit as this will not solve my issue
As mentioned in the comment, I'd suggest instead of calling parent container (Activity) objects directly, register a listener to a navigation change like this in HomeActivity:
navController.addOnDestinationChangedListener { controller, destination, arguments ->
if(destination.id = R.id.homeFragment) {
// TODO hide/show your view here
}
}
In that case, you are sure that the view gets hidden/shown when it should be without relying on the HomeFragment being only in HomeActivity as this can change in the future and your app will start crashing
If you have an orientation change or other config change, or the OS process is killed while in the background and the user returns to the app, Android will recreate the Activity and the Fragments.
Unfortunately, it creates the Fragments first, before creating the Activity. So you cannot rely on the existence of the Activity until the Fragment has been attached to the Activity. You should move code that relies on the existence of the Activity to
onActivityCreated().
Note: I also agree with the comment about not doing it this way. Your Fragment should not make assumptions like this (that it is hosted by HomeActivity), but instead should make some callback to the hosting Activity and let the hosting Activity set the visibility of the app bar (or whatever else it wants to do).

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 can I decrease a method count in my activity in MVP pattern?

I build my app in MVP architecture and I have a trouble with many functions in my activity and presenter. How Can I decrease a method count?
I have already heard about some solutions:
Split a big presenter into smaller ones but then I would have to create another methods in my activity for presenters connection.
Create a new class and create it instance in my activity which would implement the View interface and will require all of the views needed to manage the presenters. But I am not convinced to this solution. I think it may add another mess to my architecture.
Do you have other ideas or advantages/disadvantages about one described above?
There is more than a way to reduce methods from your Activity/Fragment
One is called inheritance, where you can extend abstract methods into your main Activity/Fragment class and manage the lifecycle from there.
For example, using BaseActivity or BaseFragment you can have more than one method inside of it and just extend that into your main Activity or Fragment
BaseActivity.kt
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
super.onCreate(savedInstanceState)
setContentView(getLayout())
}
#LayoutRes
abstract fun getLayout(): Int
fun Context.toast(message: String?, toastDuration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, toastDuration).show()
}
override fun onDestroy() {
super.onDestroy()
//Do here what you want
}
override fun onStop() {
super.onStop()
//Do here what you want
}
override fun onStart(){
super.onStart()
//Do here what you want
}
override fun onPause() {
super.onPause()
//Do here what you want
}
override fun onRestart() {
super.onRestart()
//Do here what you want
}
}
This BaseActivity extends AppCompatActivity(), that means that you can manage the lifecycle of your Activity in this class, and then, just extend it in your main Activity, when you do this, all the functionality inside your BaseActivity will be applied to your MainActivity class, if you want to change or override something, just call the methods from that abstract class.
MainActivity.kt
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//You dont need setContentView since we do all the configuration in the BaseActivity
toast("This is a message with a toast since we implemented thi into the BaseActivity we do not need to do toasts all over again")
}
override fun getLayout(): Int {
return R.layout.activity_login_view
}
//For example, if you want to override the functionallity from a method inside your BaseActivity you can implement it like always
override fun onRestart() {
super.onRestart()
//Replace what BaseActivity onRestart() does
}
Doing this, you can have more than 1 method of your Activity inside your BaseActivity, this will reduce the methods inside your class that inherits from BaseActivity, also, if you need this to work with Fragments, just extend Fragment instead of AppCompatActivity an make a class called BaseFragment
Also, adding an interface for view operations and presenter operations is a great way to organize our apps, you can take a look at an example I'm making for login on Github

Reuse methods in Kotlin, Android

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

Categories

Resources