when i try to change orientation of the display
it will show this error "lateinit property fragmentDispatchingAndroidInjector has not been initialized"
MainActivity.kt
class MainActivity : BaseActivity(), HasSupportFragmentInjector {
#Inject
internal lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
internal lateinit var mainMenuPagerAdapter: MainMenuPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainMenuPagerAdapter = MainMenuPagerAdapter(supportFragmentManager)
setUpMainMenuPagerAdapter()
}
override fun onFragmentAttached() {
}
override fun onFragmentDetached(tag: String) {
}
override fun supportFragmentInjector(): AndroidInjector<Fragment>? {
return fragmentDispatchingAndroidInjector
}
private fun setUpMainMenuPagerAdapter() {
mainMenuPagerAdapter.count = 1
mainMenuViewPager.adapter = mainMenuPagerAdapter
tabLayout.addTab(tabLayout.newTab().setText(R.string.my_account).setIcon(R.drawable.ic_person_white_24dp))
mainMenuViewPager.offscreenPageLimit = tabLayout.tabCount;
mainMenuViewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
mainMenuViewPager.currentItem = tab.position
}
override fun onTabUnselected(tab: TabLayout.Tab) {}
override fun onTabReselected(tab: TabLayout.Tab) {}
})
}
}
I had same crash when rotating screen, I was calling AndroidInjection.inject(this) after super.onCreate(savedInstanceState).
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
...
}
After this no crash. It may help.
abstract class BaseActivity : AppCompatActivity(), MVPView, BaseFragment.CallBack {
private var progressDialog: ProgressDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
performDI()
}
override fun hideProgress() {
progressDialog?.let { if (it.isShowing) it.cancel() }
}
override fun showProgress() {
hideProgress()
progressDialog = CommonUtil.showLoadingDialog(this)
}
private fun performDI() = AndroidInjection.inject(this)
}
this is my BaseActivity.ky
Related
class suspect : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_suspect)
imagebutton.setOnClickListener {
imagebutton.setImageResource(R.drawable.picture2)
}
}
}
You could save an image identifier to Shared Preferences and then retrieve an image number from Shared Preferences when you open your activity. Then you use that number to set the ImageButton image.
I haven't ran this code, but something along the lines of this should work:
private val picture1Id = 1
private val picture2Id = 2
private val IMAGE_KEY = "IMAGE_KEY"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_suspect)
imagebutton.setOnClickListener {
saveImageIdentifier(picture2Id)
imagebutton.setImageResource(R.drawable.picture2)
}
setupImageButtonFromPreferences()
}
private fun saveImageIdentifier(id: Int) {
val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) ?: return
with (sharedPref.edit()) {
putInt(IMAGE_KEY, id)
apply()
}
}
private fun getImageIdentifier(): Int {
val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) ?: return 1
return sharedPref.getInt(IMAGE_KEY, 1)
}
private fun setupImageButtonFromPreferences() {
when (getImageIdentifier()) {
picture1Id -> imagebutton.setImageResource(R.drawable.picture1)
picture2Id -> imagebutton.setImageResource(R.drawable.picture2)
else -> return
}
}
var a = 0
class suspect : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_suspect)
if (a == 1){imagebutton.setImageResource(R.drawable.picture2)}
imagebutton.setOnClickListener {
imagebutton.setImageResource(R.drawable.picture2)
a = 1
}
imagebutton.setOnLongClickListener {
imagebutton.setImageResource(R.drawable.picture1)
a = 0
true
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("image", a)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
a = savedInstanceState.getInt("image")
}
}
Following tutorial to change set view based on button click.
When I set the variable in onCreate, somehow, I cannot refer to them in functions.
abstract class MainActivity : AppCompatActivity() {
var count = 0
fun reset(view: View) {
count = 0
textView.setText(count.toString())
}
fun plusOne(view: View) {
count++
textView.setText(count.toString())
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var textView = findViewById<TextView>(R.id.textView)
textView.text = "Hello Fam"
}
}
Try with the following code.
abstract class MainActivity : AppCompatActivity() {
private lateinit var textView:TextView
var count = 0
fun reset(view: View) {
count = 0
textView.setText(count.toString())
}
fun plusOne(view: View) {
count++
textView.setText(count.toString())
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById<TextView>(R.id.textView)
textView.text = "Hello Fam"
}
}
Is there a way to pass a activity's function with parameter to a compose function?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyTheme {
Main(
activityFunWithParam = activityFunWithParam // not work
)
}
}
}
fun activityFunWithParam(param: Param) {
...
}
}
#Composable
fun Main(
activityFunWithPara: (Param) -> Unit
) {
...
}
You can pass it as a method reference
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Main(activityFunWithParam = ::activityFunWithParam)
}
}
fun activityFunWithParam(param: Int) {
Toast.makeText(this, param.toString(), Toast.LENGTH_SHORT).show()
}
}
#Composable
fun Main(
activityFunWithParam: (Int) -> Unit
) {
activityFunWithParam(5)
Text(text = "Test")
}
In your example, method name itself is not a function. See https://kotlinlang.org/docs/reference/lambdas.html#instantiating-a-function-type
Hi I have LoginActivity and LoginViewModel and some more classes. I have showLoading and hideLoading in the BaseActivity so it can be accessible from each activity.
I am able to call LoginActivity method from the LoginViewModel like mNavigator?.startForgotPasswordActivity()
I want to call it from the LoginViewModel then what the way to do it using MVVM ? or I am going with wrong approach. Please suggest what is the correct way to do this ?
BaseActivity.kt
abstract class BaseActivity : AppCompatActivity(), AnkoLogger {
private val progressBar: ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
protected fun getToolbar(): Toolbar {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
return toolbar
}
protected fun performDependencyInjection() {
AndroidInjection.inject(this);
}
#TargetApi(Build.VERSION_CODES.M)
fun requestPermissionsSafely(permissions: Array<String>, requestCode: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions, requestCode)
}
}
#TargetApi(Build.VERSION_CODES.M)
fun hasPermission(permission: String): Boolean {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
}
fun isNetworkConnected(): Boolean {
return NetworkUtils.isNetworkConnected(applicationContext)
}
fun showLoading() {
hideLoading()
// show progress bar
}
fun hideLoading() {
// hide progress bar
}
}
LoginActivity.kt
class LoginActivity : BaseActivity(), LoginNavigator {
#Inject
lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
performDependencyInjection()
super.onCreate(savedInstanceState)
val activityLoginBinding: ActivityLoginBinding = DataBindingUtil.setContentView<ActivityLoginBinding>(this, R.layout.activity_login)
activityLoginBinding.loginViewModel = loginViewModel
loginViewModel.mNavigator = this
}
override fun startHomeActivity() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun startRegistrationActivity() {
startActivity(Intent(this, RegistrationActivity::class.java))
}
override fun startForgotPasswordActivity() {
startActivity(Intent(this, ForgotPasswordActivity::class.java))
}
override fun handleError(throwable: Throwable) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
LoginViewModel.kt
class LoginViewModel : BaseViewModel<LoginNavigator>(), AnkoLogger {
val emailField = ObservableField<String>()
private val email: String
get() = emailField.get()
val passwordField = ObservableField<String>()
private val password: String
get() = passwordField.get()
#Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
fun login(view: View) {
if (isEmailAndPasswordValid(email, password)) {
ApiHelperImpl().doServerLoginApiCall(email, password)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : CallbackWrapper<LoginResponse>() {
override fun onSuccess(loginResponse: LoginResponse) {
info { loginResponse }
}
})
}
}
/**
* Validate email and password. It checks email and password is empty or not
* and validate email address is correct or not
* #param email email address for login
* #param password password for login
* #return true if email and password pass all conditions else false
*/
private fun isEmailAndPasswordValid(email: String, password: String): Boolean {
if (email.isEmpty()) return false
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) return false
if (password.isEmpty()) return false
return true
}
}
BaseViewModel.kt
abstract class BaseViewModel<N> {
var mNavigator: N? = null
}
There can be 2 approach for same
1) Use all functions i.e related to UI update or UI event listener from a view (Activity or Fragment) according to mvp and from viewmodel only try to manage data like api's and other logic
class LoginActivity : BaseActivity(), LoginNavigator {
#Inject
lateinit var mLoginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
mLoginViewModel.mNavigator = this
callApi()
}
private fun callApi() {
showLoading()
mLoginViewModel.callApi()
}
override fun openHomeScreen(model: Model) {
hideLoading()
showSnackBar(constraint_root, model.Domain)
}
}
class LoginViewModel(sessionManager: SessionManager, requestInterface: RequestInterface) : BaseViewModel<LoginNavigator>(sessionManager, requestInterface) {
fun callApi() {
requestInterface.getServiceIP()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleResponse, this::handleError)
}
private fun handleResponse(model: Model) {
if (model.Domain == null) {
mNavigator!!.openHomeScreen(model)
} else {
}
}
private fun handleError(error: Throwable) {
error.printStackTrace()
}
}
2)
In Login interface add a function
interface LoginNavigator {
fun openHomeScreen()
fun getActivity(): BaseActivity
}
In LoginActivity override the function and return
override fun getActivity(): BaseActivity = this
Now using navigator you can access base activity & call show/hide loader function
mNavigator!!.getActivity().showLoading()
I have an AsyncTask as an inner class inside my Activity written in Kotlin. Now, I tried to access the Activity from my AsyncTask's onPostExecute using this#MyActivity but Android Studio reports it as Unresolved Reference error. But this is the most common method suggested online for referencing the OuterClass from an InnerClass. The code is as follows:
class MyActivity : AbstractAppPauseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onResume() {
super.onResume()
}
class MyTask(private var mContext: Context?, val pbMigrating: ProgressBar) :AsyncTask<Void, Int, Void>() {
private var size: Long = 0
override fun onPreExecute() {
super.onPreExecute()
...
}
override fun doInBackground(vararg params: Void?): Void? {
...
return null
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
pbMigrating.progress = values[0] as Int
}
override fun onPostExecute(result: Void?) {
super.onPostExecute(result)
this#MyActivity //<- Cannot Resolve error here
}
}
}
You have to make class as inner
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onResume() {
super.onResume()
} inner class MyTask(private var mContext: Context?, val pbMigrating: ProgressBar) : AsyncTask<Void, Int, Void>() {
private var size: Long = 0
override fun onPreExecute() {
super.onPreExecute()
}
override fun doInBackground(vararg params: Void?): Void? {
return null
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
pbMigrating.progress = values[0] as Int
}
override fun onPostExecute(result: Void?) {
super.onPostExecute(result)
this#MyActivity //<- Cannot Resolve error here
}
}
}
The class MyTask must be defined as an inner class:
inner class MyTask(
private var mContext: Context?,
val pbMigrating: ProgressBar
) : AsyncTask<Void, Int, Void>()
{
...
}