I know the concept of "Interfaces" but I've hard time to understand how to use them in android development.
Let's say I created an interface to decide if to show progress bar or not -
interface ProgressBarInterface {
fun showProgressBar()
fun hideProgressBar()
}
And I implement this inside BaseActivity/MainActivity in single Activity app:
class BaseActivity : AppCompatActivity() , ProgressBarInterface {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun showProgressBar() {
}
override fun hideProgressBar() {
}
}
And inside my other activity I've a button, that when I click on it, I want to trigger showProgressBar in the base activity:
button.setOnClickListener {
//Show progress bar
}
How can I interact with the interface to trigger the function inside base activity?
Since you already are implementing the interface in your BaseActivity, you can then just add what you need to do inside the interface methods, and then call them up in any point in your activity, if what you are looking for is to extend this BaseActiviy into more activities you will need to make this BaseActivity abstract then you can extend in each activity this BaseClass and just use the interface methods
abstract class BaseActivity : AppCompatActivity() , ProgressBarInterface {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun showProgressBar() {
progressBar.visibility = View.VISIBLE
}
override fun hideProgressBar() {
progressBar.visibility = View.GONE
}
}
and then in your Activities you can extend from BaseActivity() and just use your interface methods as you have defined in that BaseActivity() to prevent coding them again, you can do
class FirstActivity : BaseActivity() {
...
button.setOnClickListener {
showProgressBar()
}
An easier way to show and hide the views? Use extension functions
fun View.show() {
this.visibility = View.VISIBLE
}
fun View.hide() {
this.visibility = View.GONE
}
you can define that extensions in any class, for example ViewUtils.kt and then just call
button.setOnClickListener {
progressBar.show()
}
or
button.setOnClickListener {
progressBar.hide()
}
Related
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navigate = Intent(this,Activity2::class.java)
startActivity(navigate)
}
}
}
fun switchActivity(){
val navigate = Intent(this,Activity2::class.java)
startActivity(navigate)
}
I want to start an activity from a function and not from the main activity..
I can start an activity from main class but in function, the code doesnt work..
Please help.. I'm new to kotlin android programming..
You can also try like this
class MainActivity : AppCompatActivity() {
#SuppressLint("MissingInflatedId")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
nextActivity(this, MainActivity2())
}
private fun nextActivity(context: Context, activity: Activity) {
startActivity(Intent(context, activity::class.java))
}
}
Just use it
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
switchActivity()
}
}
fun switchActivity(){
val navigate = Intent(this#MainActivity, Activity2::class.java)
startActivity(navigate)
}
Actually i don't get your question clearly. But i'll try to answer your question based on my understanding of yours.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
switchActivity()
// if you want to pass the class through the paramater,
// can try this
// TODO: Another Way
// switchActivityCustom(Activity2::class.java)
}
}
fun switchActivity(){
val navigate = Intent(this, Activity2::class.java)
startActivity(navigate)
}
// TODO: Another Function
// fun switchActivityCustom(destination: Class<*>,){
// val navigate = Intent(this, destination)
// startActivity(navigate)
// }
}
And the last one is, put your function inside the class (in this case: MainActivity Class)
If your function is declared at the top level, without a reference to this, you'd need to pass an instance of Activity to your function:
fun switchActivity(activity: Activity) {
val navigate = Intent(activity, Activity2::class.java)
activity.startActivity(navigate)
}
On a button click i have to get some value from API call and then launch one screen. I have two options:
Call the observer each time when user will click on button.
Call the observer on fragment onActivityCreated() and store the value in variable and act accordingly on button click.
So which approach I should follow?
Actually it's up to you. But i always prefer to call it in Activity's onCreate() function, so activity only has 1 observer. If you call it in button click, it will give you multiple observers as much as button clicking
Here is some example :
class HomeProfileActivity: BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initObserver()
initView()
}
private fun initObserver() {
viewModel.profileWorkProccess.observe(this, {
swipeRefreshLayout.isRefreshing = it
})
viewModel.isLoadingJobs.observe(this, {
layoutProgressBarJobs.visibility = View.VISIBLE
recyclerViewJobs.visibility = View.GONE
dotsJobs.visibility = View.GONE
})
//other viewmodel observing ......
}
private fun initView() {
imageProfile.loadUrl(user.image, R.drawable.ic_user)
textName.text = identity.user?.fullName
textAddress.text = identity.user?.city
buttonGetData.setOnClickListener { viewModel.getData(this) }
}
}
If the button is placed on the Activity, and data is displayed in the Fragment, you need to store variable in Activity ViewModel and observe it in Fragment
You only need to call observe one time when fragment is created.
For example:
class MyActivity : AppCompatActivity() {
val viewModel: MyActViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myButton.setOnClickListener { view ->
viewModel.getData()
}
}
}
class MyActViewModel: ViewModel {
val data: LiveData<String> = MutableLiveData()
fun getData() {}
}
class MyFragment: Fragment {
val actViewModel: MyActViewModel by activityViewModels()
override fun onActivityCreated(...) {
....
actViewModel.data.observe(viewLifecycleOwner, Observer { data ->
...
}
}
}
I have an Activity that extend base Activity like so:
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getInfo("Pojo")
}
}
My base activity does some API access and returns the answer like so:
abstract class BaseActivity : AppCompatActivity() {
companion object {
#JvmStatic var compositeDisposable: CompositeDisposable?=null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
compositeDisposable = CompositeDisposable()
}
override fun onDestroy() {
super.onDestroy()
compositeDisposable?.clear()
}
fun getTvShows(query: String) {
compositeDisposable?.add(
ApiClient.getClient.getTV(Params.getParamsSearch(1, query))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponseTvShows)
)
}
private fun handleResponseTvShows(result: ObjectsSearchTVShows) {
//Need to send result back to MainActivity or any activity that extends BaseActivity
}
}
I need to send the result back to MainActivity or any activity that extends BaseActivity, how I can achive this?
Make handleResponseTvShows abstract and protected:
protected abstract fun handleResponseTvShows(result: ObjectsSearchTVShows)
and implement it in MainActivity:
override protected fun handleResponseTvShows(result: ObjectsSearchTVShows) {
// process the result here
}
I have FragmentA and FragmentB.
I am trying to update the values of FragmentA from FragmentB
I have an interface:
interface FragmentCallback {
fun onDataSent(sendUpdatedData: String, position: Int?)
}
In FragmentA, I override the interface function. I also instantiate FragmentB and call setFragmentCallback function
class FragmentA: Fragment(), FragmentCallback {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
FragmentB().setFragmentCallback(this)
}
//This function is not being called from FragmentB...
override fun onDataSent(sendUpdatedData: String, position: Int?) {
updateRecyclerView(sendUpdatedData, position!!)
}
In FragmentB (which is on top of FragmentA) I instantiate the FragmentCallback interface. When I am done editing and pop the fragment(backstack), I call the overridden function from FragmentA(onDataSent).
class FragmentB: Fragment(){
private var fragmentCallback: FragmentCallback? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
btn_save.setOnClickListener{
fragmentCallback?.onDataSent(et_new_text.text.toString(), position )
fragmentManager?.popBackStack()
}
}
fun setFragmentCallback(callback: FragmentCallback?) {
fragmentCallback = callback
}
}
For some reason, when I pop FragmentB and call onDataSent, through the line:
fragmentCallback?.onDataSent(et_new_text.text.toString(), position)
onDataSent will actually NOT be called.
Use a FragmentResultListener in FragmentA:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setFragmentResultListener("requestKey") { key, bundle ->
val sendUpdatedData = bundle.getString("sendUpdatedData")
// Do something with the result...
}
}
Then in FragmentB set the result:
btn_save.setOnClickListener{
val result = et_new_text.text.toString()
setFragmentResult("requestKey", bundleOf("sendUpdatedData" to result))
...
}
Have a look at the official documentation.
The old-fashioned way uses the Activity to communicate between two fragments. You could also use a shared ViewModel to share data.
You can try this too.
Let your activity implements your interface.
class YourActivity : FragmentCallback{
override fun onDataSent(sendUpdatedData: String, position: Int?) {
// Get Fragment A
val fraga: FragmentA? =
supportFragmentManager.findFragmentById(R.id.fragment_a) as FragmentA?
fraga.updateRecyclerView(sendUpdatedData,position)
}
}
In your fragmentB, send the data like this.
class FragmentB : Fragment() {
var mCallback: FragmentCallback? = null
override fun onAttach(activity: Activity) {
super.onAttach(activity)
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
mCallback = try {
activity as FragmentCallback
} catch (e: ClassCastException) {
throw ClassCastException(
activity.toString()
+ " must implement TextClicked"
)
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mCallback.onDataSent(et_new_text.text.toString(), position)
fragmentManager?.popBackStack()
}
override fun onDetach() {
mCallback = null // => avoid leaking, thanks #Deepscorn
super.onDetach()
}
}
In your FragmentA.
class FragmentA : Fragment() {
fun updateRecyclerView(sendUpdatedData: String, position: Int?) {
// Here you'll have it
}
}
I am using one activity and three fragments for my application
Each fragment has an interface that is used to communicate with the logic
the activity implements this interface and calls the logic object (a persistent fragment) with the same arguments, so it looks something like this:
class Child : Fragment() {
private fun userInteraction() {
(activity as? ChildInteraction)?.askStuff()
}
interface ChildInteraction {
fun askStuff():Unit
}
}
class ParentActivity : AppCompatActivity(), ChildInteraction {
override fun askStuff() {
(supportFragmentManager.findFragmentByTag("LOGIC") as? ChildInteraction).askStuff()
}
}
class LogicFragment : Fragment(), ChildInteraction {
override fun askStuff() {
//do some work here
}
}
the thing is, that each interaction has 5-10 methods in it, and all ParentActivity does is pass on the message, is there a way to simplify the passing along?
I know you can't do this in Java but I was hoping there is a way for Kotlin to do it
Take a look on setTargetFragment and getTargetFragment. Here is an example of Fragment-to-Fragment communication:
interface ChildInteraction {
companion object
fun askStuff()
}
fun ChildInteraction.Companion.wrap(f: Fragment) = (f.targetFragment as? ChildInteraction)
class Child1 : Fragment() {
private fun userInteraction() {
ChildInteraction.wrap(this)?.askStuff()
}
}
class Child2 : Fragment() {
private fun userInteraction() {
ChildInteraction.wrap(this)?.askStuff()
}
}
class LogicFragment : Fragment(), ChildInteraction {
override fun askStuff() {
//do some work here
}
}
class ParentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val logic ...
val child1 ...
child1.setTargetFragment(logic, 0)
val child2 ...
child2.setTargetFragment(logic, 0)
}
}
Now I know that this is probably what works for me, so I'll be still accepting the other answer, and because it taught me something I didn't know.
But after some work the way that I handled it was this:
The fragment that attaches looks like this:
class Child : Fragment() {
private var parent: ChildInteraction? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
//this should throw an exception if it is not implemented correctly
parent = (context as LogicProvider).logic!! as Child.ChildInteraction
}
private fun userInteraction() {
parent!!.askStuff()
}
interface ChildInteraction {
fun askStuff():Unit
}
}
Then I have the LogicProvider interface like this:
interface LogicProvider {
val logic: Any?
}
and then the parent will implement logic providers that will pass on the arguments
class ParentActivity : AppCompatActivity(), LogicProvider {
override var logic: Logic? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var frag: Fragment? = supportFragmentManager.findFragmentByTag("parent_logic")
if (frag == null) {
frag = Logic()
supportFragmentManager.beginTransaction().add(frag, "parent_logic").commitNow()
}
logic = frag as Logic
}
override fun onPause() {
super.onPause()
if (isFinishing)
supportFragmentManager.beginTransaction().remove(supportFragmentManager.findFragmentByTag("parent_logic")).commitNow()
}
}
that way the logic fragment is the only one that has to implement interfaces
class Logic : Fragment(), Child.ChildInteraction, Child2.ChildInteraction2 {
override fun askStuff() {
//do stuff here
}
override fun askStuff2() {
//do other stuff here
}
}