Pass parameter to function in Kotlin - android

Here is my activity:
class PlayerDetails : AppCompatActivity(), View.OnClickListener {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player_details)
val intent = getIntent()
numOfPlayers = intent.getIntExtra("number_of_players", 1)
next_details.setOnClickListener(this)
}
override fun onClick(v: View?) {
for (i in 1..numOfPlayers) {
...
nextUser()
} else {
blankFields()
}
}
}
private fun nextUser() {
}
}
How do I pass the value of i in my for loop to nextUser()?
I tried nextUser(num: Int = i) but it doesn't work.
Any idea?

Change your method to accept a parameter
private fun nextUser(num: Int) { }
And call the function like this
for (i in 1..numOfPlayers) { ... nextUser(i) } else { blankFields() } }

Your nextUser should look like as follow:
private fun nextUser(i: Int = DEFAULT_VALUE) {
}
DEFAULT_VALUE is optional, if you choose you can delete = DEFAULT_VALUE from you function definition. In that case, you always need to pass value when you make a call to nextUser.
Then, from your for loop you can pass i value to nextUser as follow.
for (i in 1..numOfPlayers) {
...
nextUser(i)
} else {
blankFields()
}

The NextUser() function must accept one input param.
Now the Code is like
for(i until numOfPlayers) {
nextuser(i)
}
else {
blankFields()
}
Now the nextUser looks like.
nextUser(value : Int) {
TODO("perform the action")
}

Related

when i click imagebutton the image change. Can i save this change when i return again in this activity?

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

How to pass activity's function to compose

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

Is there a better and simpler way to optimize helper callback and handler code in kotlin?

I am using helper classes with retry logic which is used throughout my app. I was wondering if there is a way I can combine the handler and callback classes into one instead of having 2 different classes one for callback and one for handler. Here's my code as follows:
Retryhandler:
abstract class RetryHandler(private val totalRetries: Int = 0, private val delayMillis : Long = 0) : Handler() {
private var retryCount: Int = 0
fun retry(): Boolean {
return if (retryCount++ < totalRetries) {
if (delayMillis > 0) {
postDelayed({ onRetry(retryCount) }, delayMillis)
} else {
onRetry(retryCount)
true
}
} else false
}
abstract fun onRetry(retryCount: Int)
}
Retrycallback:
abstract class RetryableCallback(totalRetries: Int = 0, delayMillis : Long = 0)
: RetryHandler(totalRetries, delayMillis), MyCallback {
override fun handleTransactionCompleted() {
if (!onCompleted()) {
if (!retry()) {
onFailed(null)
}
}
}
override fun handleTransactionFailed(e: MyException?) {
if (!retry()) {
onFailed(e)
}
}
abstract fun onCompleted(): Boolean
abstract fun onFailed(e: MyException? = null)
}
Here's how I am using them in my code:
private val newCallback = object: RetryableCallback(5, 5000) {
override fun onRetry(retryCount: Int) {
....}
override fun onCompleted(): Boolean {
}
}
Any ideas ?
Well, as long as I don't fully understand the purpose, let's say like this:
abstract class RetriableCallbackHandler(private val totalRetries: Int = 0, private val delayMillis : Long = 0) : Handler(), MyCallback {
private var retryCount: Int = 0
fun retry(): Boolean {
return if (retryCount++ < totalRetries) {
if (delayMillis > 0) {
postDelayed({ onRetry(retryCount) }, delayMillis)
} else {
onRetry(retryCount)
true
}
} else false
}
abstract fun onRetry(retryCount: Int)
override fun handleTransactionCompleted() {
if (!onCompleted()) {
if (!retry()) {
onFailed(null)
}
}
}
override fun handleTransactionFailed(e: MyException?) {
if (!retry()) {
onFailed(e)
}
}
abstract fun onCompleted(): Boolean
abstract fun onFailed(e: MyException? = null)
}

Initialization of a fragment causes the animation lag

While writing an Android app, I encountered a problem with a stuttering animation. I use AHBottomNavigation for navigation, FragNav is for swapping fragments and FlexibleAdapter for RecyclerView.
The application is built from one activity and five fragments. When I try to switch to the first fragment in the application, the BottomNavigation animation freez for a moment. It looks very unsightly. The second time I choose the same fragment, everything works smoothly. It seems to me that it is the fault to initialize the views in the fragment, but I have no idea how to do it differently.
AHBottomNavigation https://github.com/aurelhubert/ahbottomnavigation
FragNav https://github.com/ncapdevi/FragNav
FlexibleAdapter https://github.com/davideas/FlexibleAdapter
Fragment
class GradeFragment : BaseFragment(), GradeView {
#Inject
lateinit var presenter: GradePresenter
private val gradeAdapter = FlexibleAdapter<AbstractFlexibleItem<*>>(null, null, true)
companion object {
fun newInstance() = GradeFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_grade, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.run {
attachView(this#GradeFragment)
loadData()
}
}
override fun initView() {
gradeAdapter.run {
isAutoCollapseOnExpand = true
isAutoScrollOnExpand = true
setOnUpdateListener { presenter.onUpdateDataList(it) }
setOnItemClickListener { position ->
getItem(position).let {
if (it is GradeItem) {
GradeDialog.newInstance(it.grade).show(fragmentManager, it.toString())
}
}
}
}
gradeRecycler.run {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = gradeAdapter
}
gradeSwipe.setOnRefreshListener { presenter.loadData(forceRefresh = true) }
}
override fun updateData(data: List<GradeHeader>) {
gradeAdapter.updateDataSet(data, true)
}
override fun isViewEmpty(): Boolean = gradeAdapter.isEmpty
override fun showEmptyView(show: Boolean) {
gradeEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showProgress(show: Boolean) {
gradeProgress.visibility = if (show) VISIBLE else GONE
}
override fun setRefresh(show: Boolean) {
gradeSwipe.isRefreshing = show
}
Presenter
class GradePresenter #Inject constructor(
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersManager,
private val gradeRepository: GradeRepository,
private val sessionRepository: SessionRepository) : BasePresenter<GradeView>(errorHandler) {
override fun attachView(view: GradeView) {
super.attachView(view)
view.initView()
}
fun loadData(forceRefresh: Boolean = false) {
disposable.add(sessionRepository.getSemesters()
.map { it.single { semester -> semester.current } }
.flatMap { gradeRepository.getGrades(it, forceRefresh) }
.map { it.groupBy { grade -> grade.subject } }
.map { createGradeItems(it) }
.subscribeOn(schedulers.backgroundThread())
.observeOn(schedulers.mainThread())
.doFinally { view?.setRefresh(false) }
.doOnSuccess { if (it.isEmpty()) view?.showEmptyView(true) }
.doOnError { view?.run { if (isViewEmpty()) showEmptyView(true) } }
.subscribe({ view?.updateData(it) }) { errorHandler.proceed(it) })
}
private fun createGradeItems(items: Map<String, List<Grade>>): List<GradeHeader> {
return items.map {
val gradesAverage = calcAverage(it.value)
GradeHeader().apply {
subject = it.key
average = view?.run {
if (gradesAverage == 0f) emptyAverageString()
else averageString().format(gradesAverage)
}.orEmpty()
number = view?.gradeNumberString(it.value.size).orEmpty()
subItems = (it.value.map { item ->
GradeItem().apply {
grade = item
weightString = view?.weightString().orEmpty()
valueColor = getValueColor(item.value)
}
})
}
}
}
fun onUpdateDataList(size: Int) {
if (size != 0) view?.showProgress(false)
}
After a few days, I managed to solve the problem by updating the SDK to version 28. RecyclerView no longer causes animation jams when inflating

How to call baseactivity function from viewmodel in android

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

Categories

Resources