I'm new here and this is my first post!
I try to learn Android app development and I'm stuck with this problem :
How can I update a textview inside a function? I code a small app which generates all the permutation with repetitions and write it in a textview.
It works, but the textview updates only at the end of all the permutations... Don't understand why...
Sorry if my English is bad, I'm French ;)
I try to use Thread, the app doesn't crash, it seems to work but the app goes directly in the background...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
permutation2("abcdefgh", 8)
}
private fun permutation1(text: String, prefix: String, n: Int, k: Int): String {
if (k == 0) {
} else
for (i in 0..n) {
val newprefix = prefix + text[i]
if (newprefix.length >= text.length) {
zoneTexte.text = newprefix
}
permutation1(text, newprefix, n, k - 1)
}
return "Erreur"
}
private fun permutation2(text: String, k: Int) {
permutation1(text, "", text.length - 1, k)
}
}
Functions for permutations work well but the textview update only at the end (with the last permutation "hhhhhhhh") and I would like to update it for each permutation.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
permutation2("abcdefgh", 8)
}
private fun permutation2(text: String, k: Int) {
MyCal().execute(text)
}
inner class MyCal : AsyncTask<String ,String, String>(){
override fun onProgressUpdate(vararg values: String?) {
super.onProgressUpdate(*values)
zoneTexte.text = values[0]
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
zoneTexte.text = result
}
override fun doInBackground(vararg p0: String?): String {
return permutation1(p0[0]!!, "", p0[0]!!.length?.minus(1), 8)
}
fun permutation1(text: String, prefix: String, n: Int, k: Int): String {
if (k == 0) {
} else
for (i in 0..n) {
val newprefix = prefix + text[i]
if (newprefix.length >= text.length) {
onProgressUpdate(newprefix)
return newprefix
}
permutation1(text, newprefix, n, k - 1)
}
return "Erreur"
}
}
}
onCreate is executed on the ui-thread, as is the case for permutation1() and permutation2(). The ui won't actually refresh until onCreate completes and ui can then refresh/redraw the screen, so that's why you don't see any incremental text updates until the end.
If you would like to see it update in real time, you may want to look into AsyncTask. In your particular example, you aren't really performing a long running task, so I'm not sure if you'll be able to see the incremental additions to your TextView even if you use AsyncTask.
After the help from Mark and Kishan I find the solution ! Thank you guys !
Here is the code :
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
permutation2("abcdefgh", 8)
}
private fun permutation2(text: String, k: Int) {
MyCal().execute(text)
}
inner class MyCal : AsyncTask<String ,String, String>(){
override fun onProgressUpdate(vararg values: String?) {
super.onProgressUpdate(*values)
runOnUiThread(Runnable { zoneTexte.text = values[0] })
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
runOnUiThread(Runnable { zoneTexte.text = result })
}
override fun doInBackground(vararg p0: String?): String {
return permutation1(p0[0]!!, "", p0[0]!!.length?.minus(1), 8)
}
fun permutation1(text: String, prefix: String, n: Int, k: Int): String {
if (k == 0) {
} else
for (i in 0..n) {
val newprefix = prefix + text[i]
if (newprefix.length >= text.length) {
onProgressUpdate(newprefix)
return newprefix
}
permutation1(text, newprefix, n, k - 1)
}
return "Erreur"
}
}
}
Related
I'm trying to integrate a VerticalGridSupportFragment() inside a BrowseSupportFragment() based on some of the Leanback examples.
On running the code below (partial snippet provided below) I get the exception java.lang.IllegalArgumentException: Fragment must implement MainFragmentAdapterProvider
I'm struggling to understand how to implement the interface methods defined by MainFragmentAdapterProvider [i.e getMainFragmentAdapter()]. Could anyone provide insight surrounding this issue, or could you provide an example? I've tried to return the mRowsAdapter, however it was not of the correct type.
Any help would really be appreciated, thanks!
class MainFragmentByGroupFragment : BrowseSupportFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
super.onActivityCreated(savedInstanceState)
prepareBackgroundManager()
mainFragmentRegistry.registerFragment(
PageRow::class.java,
PageRowFragmentFactory(mBackgroundManager)
)
setupUIElements()
createRows()
setupEventListeners()
}
private class PageRowFragmentFactory(backgroundManager: BackgroundManager) : BrowseSupportFragment.FragmentFactory<androidx.fragment.app.Fragment>() {
override fun createFragment(rowObj: Any?): androidx.fragment.app.Fragment {
val row = rowObj as Row
return ChannelFragment()
}
}
}
class ChannelFragment : VerticalGridSupportFragment(), OnItemViewClickedListener, BrowseSupportFragment.MainFragmentAdapterProvider {
private var mRowsAdapter: ArrayObjectAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mRowsAdapter = ArrayObjectAdapter(CardPresenter())
setAdapter(mRowsAdapter)
setOnItemViewClickedListener(this)
createRows()
}
private fun createRows() {
val gridPresenter = VerticalGridPresenter()
gridPresenter.numberOfColumns = NUM_COLUMNS
setGridPresenter(gridPresenter)
setOnItemViewClickedListener(this)
val list = MovieList.list
for (i in 0 until 100) {
mRowsAdapter!!.add(list[i % 5])
}
}
override fun onItemClicked(
itemViewHolder: Presenter.ViewHolder?, item: Any,
rowViewHolder: RowPresenter.ViewHolder?, row: Row?
) {
}
override fun getMainFragmentAdapter(): BrowseSupportFragment.MainFragmentAdapter<*> {
TODO("Not yet implemented")
}
companion object {
private const val NUM_COLUMNS = 5
}
}
I think you should implement getMainFragmentAdapter.
Add the following to ChannelFragment:
private val mMainFragmentAdapter: BrowseSupportFragment.MainFragmentAdapter<*> =
object : BrowseSupportFragment.MainFragmentAdapter<Fragment?>(this) {
override fun setEntranceTransitionState(state: Boolean) {
}
}
And return mMainFragmentAdapter in getMainFragmentAdapter:
override fun getMainFragmentAdapter(): BrowseSupportFragment.MainFragmentAdapter<*> {
return mMainFragmentAdapter
}
I'm working on a training project as a student, but I can't figure out how to transfer my business logic from NewItemActivity to the NewItemViewModel. It turned out that I have all the logic for validating and creating the ItemModel inside the fragment. It's not good to do this, these are all parts of the business logic that need to be given to the viewModel. How can I transfer business logic to the ViewModel, but at the same time keep the application working correctly? Otherwise I tried and everything broke for me.
NewItemActivity.kt
class NewItemActivity : AppCompatActivity() {
private val calendar: Calendar = Calendar.getInstance()
private val viewModel: NewItemViewModel by viewModels(factoryProducer = {
NewItemViewModel.Factory()
})
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_item)
val saveButton = findViewById<Button>(R.id.saveButton)
val editDate = findViewById<EditText>(R.id.editDate)
val date = SimpleDateFormat("dd.MM.yyyy")
val dateDefault = date.format(calendar.timeInMillis)
editDate.setText(dateDefault)
editDate.setOnClickListener {
showDatePickerDialog()
}
saveButton.setOnClickListener {
checkStateDescriptionLayout()
if (checkStateTitleLayout()) return#setOnClickListener
if (checkStateDescriptionLayout()) return#setOnClickListener
val newItem = ItemModel(
title = editTitle.text.toString(),
description = editDescription.text.toString(),
date = Date(),
isFavorite = false
)
viewModel.saveNewItem(newItem)
Toast.makeText(this, "New item added", Toast.LENGTH_SHORT).show()
finish()
}
textChangedListener()
}
private fun showDatePickerDialog() {
val datePickerDialog = DatePickerDialog(
this#NewItemActivity,
{ _, year, monthOfYear, dayOfMonth ->
val selectedDate: String =
dayOfMonth.toString() + "." + (monthOfYear + 1) + "." + year
editDate?.setText(selectedDate)
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
)
datePickerDialog.datePicker.maxDate = calendar.timeInMillis
datePickerDialog.show()
}
private fun checkStateTitleLayout(): Boolean {
val titleLayout = findViewById<TextInputLayout>(R.id.editTitleLayout)
val checkTitleLayoutState = titleLayout.editText?.text?.toString()
val fieldIsRequired = getString(R.string.fieldIsRequired)
val error: Boolean = checkTitleLayoutState!!.isEmpty()
if (error) titleLayout.error = fieldIsRequired
return error
}
private fun checkStateDescriptionLayout(): Boolean {
val descriptionLayout = findViewById<TextInputLayout>(R.id.editDescriptionLayout)
val checkDescriptionLayoutState = descriptionLayout.editText?.text?.toString()
val fieldIsRequired = getString(R.string.fieldIsRequired)
val error: Boolean = checkDescriptionLayoutState!!.isEmpty()
if (error) descriptionLayout.error = fieldIsRequired
return error
}
private fun textChangedListener() {
val titleLayout = findViewById<TextInputLayout>(R.id.editTitleLayout)
val descriptionLayout = findViewById<TextInputLayout>(R.id.editDescriptionLayout)
titleLayout.editText?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
titleLayout.error = null
titleLayout.isErrorEnabled = false
}
})
descriptionLayout.editText?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
descriptionLayout.error = null
descriptionLayout.isErrorEnabled = false
}
})
}
}
NewItemViewModel.kt
class NewItemViewModel(
private val repository: MyItemsRepository
) : ViewModel() {
fun saveNewItem(item: ItemModel) = repository.saveNewItem(item)
class Factory : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return NewItemViewModel(MyItemsRepositoryImpl.getInstance()) as T
}
}
}
It seems like your main business logic is contained in onCreate, and that's not a big problem when you have little business logic, plus your logic is basically one event (saving the ItemModel object).
If you still want to move more logic to the ViewModel, move the creation of ItemModel to the ViewModel and provide functions to change each parameter of ItemModel there. It should look like this:
class NewItemViewModel(
private val repository: MyItemsRepository
) : ViewModel() {
private var title = ""
private var description = ""
private var date = Date()
private var isFavorite = false
fun setTitle(s: String) { title = s }
fun setDescription(s: String) { description = s }
fun setDate(d: Date) { date = d }
fun setIsFavorite(b: Boolean) { isFavorite = b }
fun saveNewItem() = repository.saveNewItem(Item(title, description, date, isFavorite))
class Factory : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return NewItemViewModel(MyItemsRepositoryImpl.getInstance()) as T
}
}
}
You might think it's more trouble than it's worth, and you will be right. However, there's no harm to preparing for more complicated logic ;)
P.S. I have not tested this code, but it should be basically correct.
I am trying to do an app where I want to update a ProgressBar in a for loop using AsyncTask. The best is to show you a pseudo code of what I am trying to do
button.setOnClickListener{
for(int i=0; i<5000; i++)
{
doSomeHeavyStuff();
UpdateProgressBarAsyncTask(i).execute()
}
}
This is what I have so far
MainActivity:
class MainActivity : AppCompatActivity() {
var progress:ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
progress = findViewById(R.id.progress)
val buttonStart = findViewById<TextView>(R.id.button)
var maxNumber = 5000
buttonStart.setOnClickListener{
for(i in 0 until maxNumber)
{
HeavyStuff()
ProgressTask(i,progress!!,maxNumber,this).execute()
}
}
}
internal class ProgressTask (var actual:Int, var progress: ProgressBar, var max: Int, var context: Activity): AsyncTask <Void, Int, Int>()
{
override fun onPreExecute() {
super.onPreExecute()
progress.visibility = View.VISIBLE
progress.max = max
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
progress.setProgress(values[0]!!)
}
override fun doInBackground(vararg params: Void?): Int? {
publishProgress(actual)
return null
}
override fun onPostExecute(result: Int?) {
super.onPostExecute(result)
progress.visibility = View.INVISIBLE
Toast.makeText(context, "Finished!", Toast.LENGTH_SHORT).show()
}
}
XML:
<ProgressBar
android:id="#+id/progress"
style="#android:style/Widget.ProgressBar.Horizontal"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="24dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Do I miss something here? Right now it shows ProgressBar after the HeavyStuff() is finished and thus it is not shown during the for loop. What am I missing here?
Can you guys please help me with this?
Thanks
Actually, I think that both the heavy stuff and the For Loop need to be present inside of the doBackground function (The call of heavy stuff in the main thread will freeze the UI and cause an ANR), see the code below :
const val max = 50000
class MainActivity : AppCompatActivity() {
var progress: ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
progress = findViewById(R.id.progress)
buttonStart.setOnClickListener {
ProgressTask(progress!!, max, this).execute()
}
}
internal class ProgressTask(
var progress: ProgressBar,
var max: Int,
var context: Activity
) : AsyncTask<Void, Int, Int>() {
override fun onPreExecute() {
super.onPreExecute()
progress.visibility = View.VISIBLE
progress.max = max
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
progress.setProgress(values[0]!!)
}
override fun doInBackground(vararg params: Void?): Int? {
for (i in 0 until max) {
doHeavyStuff()
publishProgress(i)
}
return null
}
override fun onPostExecute(result: Int?) {
super.onPostExecute(result)
progress.visibility = View.INVISIBLE
Toast.makeText(context, "Finished!", Toast.LENGTH_SHORT).show()
}
}
}
I would go with this approach to avoid memory leaks:
private lateinit var mTextView: WeakReference<TextView>
private lateinit var mProgressBar: WeakReference<ProgressBar>
private const val MAX = 50000
class ProgressTask(
pb: ProgressBar,
var max: Int
) : AsyncTask<Void, Int, Int>() {
init {
mProgressBar = WeakReference(pb)
}
override fun onPreExecute() {
super.onPreExecute()
mProgressBar.get()?.visibility = View.VISIBLE
mProgressBar.get()?.max = MAX
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
mProgressBar.get()?.progress = values[0]!!
}
override fun doInBackground(vararg p0: Void?): Int {
for (i in 0 until MAX) {
doHeavyStuff()
publishProgress(i)
}
return 1
}
override fun onPostExecute(result: Int?) {
super.onPostExecute(result)
mProgressBar.get()?.visibility = View.GONE
}
}
I have an activity that calls a class and runs a function in that class like so:
for (i in 0 until questions) {
droll.droolCalls(sign)
}
This can sometimes run forever as it has to generate a bunch of random numbers, so I want it to be able to run in the background. I wrote an AsyncTask that looks like this:
class MyAsync(
private val droll: CreateLayout,
private val questions: Int, private val sign:Int,
var progressBar: ProgressBar,
var layoutProgress: LinearLayout,
var layoutMain: LinearLayout
) :
AsyncTask<String, Int, CreateLayout>() {
var progress = 0
override fun doInBackground(vararg params: String?): CreateLayout {
for (i in 0 until questions) {
droll.droolCalls(sign)
}
return droll
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
progressBar.incrementProgressBy(progress * 10)
}
override fun onPostExecute(result: CreateLayout?) {
super.onPostExecute(result)
layoutProgress.visibility = View.GONE
layoutMain.visibility = View.VISIBLE
}
}
However, when i call the drollclass
MyAsync(droll, totalQuestions,sign, progressBar, Loading, mainLayout).execute("hello")
i get an error from other functions that require drool to run.
this one for example insert_point.addView(droll.makeTextView(numberOfQuestions - 1)) gives me a java.lang.IndexOutOfBoundsException: Index: 2, Size: 0 error because insert_point not getting the data from droll because the Async didn't run? however if i take it out of the Async the for loop our of the Async it works fine.
the whole structure looks something like this
class mainclass{
MyAsync(droll, totalQuestions,sign, progressBar, Loading, mainLayout).execute("hello")
insert_point.addView(droll.makeTextView(numberOfQuestions - 1))
class MyAsync(
private val droll: CreateLayout,
private val questions: Int, private val sign:Int,
var progressBar: ProgressBar,
var layoutProgress: LinearLayout,
var layoutMain: LinearLayout
) :
AsyncTask<String, Int, CreateLayout>() {
var progress = 0
override fun doInBackground(vararg params: String?): CreateLayout {
for (i in 0 until questions) {
droll.droolCalls(sign)
}
return droll
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
progressBar.incrementProgressBy(progress * 10)
}
override fun onPostExecute(result: CreateLayout?) {
super.onPostExecute(result)
layoutProgress.visibility = View.GONE
layoutMain.visibility = View.VISIBLE
}
}
}
package www.binexmining.co.`in`.binexmining.binexmining.uam.view.activity
import android.app.Activity
import android.os.AsyncTask
import android.os.Bundle
import android.support.annotation.MainThread
import kotlinx.android.synthetic.main.row_referraluser.view.*
import www.binexmining.co.`in`.binexmining.R
class AsynTaskKotlinMain : Activity()
{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_landing)
}
override fun onResume() {
super.onResume()
MyAssyn().execute("Pre-Executing Param Put here...","Param For Doinbackgroun")
}
}
class MyAssyn : AsyncTask<Any, Any, Any>()
{
override fun onPreExecute() {
super.onPreExecute()
}
override fun doInBackground(vararg params: Any?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onPostExecute(result: Any?) {
super.onPostExecute(result)
}
}
This question already has answers here:
Pass ArrayList<? implements Parcelable> to Activity
(6 answers)
Closed 5 years ago.
I have a custom class filled with information of a User. I use an ArrayList to hold all the User class data. I would like to pass the array to another activity where the it can be read and modified. Since the array could be large, I need this process to be done as efficiently and fast as possible, which is why I choose to use Parcelable instead of Serializable.
I am having trouble doing this using Kotlin. There are lots of Java tutorials but none are in Kotlin, which is what makes this question different then all the others. Can someone please provide and explain the code in Kotlin on how this can be achieved?
This is what I have so far:
constructor(`in`:Parcel) {
CollegeName = `in`.readString()
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel?, flags: Int) {
}
private fun readFromParcel(`in`:Parcel) {
CollegeName = `in`.readString()
}
companion object {
#JvmField final val CREATOR: Parcelable.Creator<College> = object: Parcelable.Creator<College> {
override fun createFromParcel(source: Parcel): College {
return College(source)
}
override fun newArray(size: Int): Array<College?> {
return arrayOfNulls<College>(size)
}
}
}
Use something like this...
class Series() : Parcelable {
private var name: String? = null
private var numOfSeason: Int = 0
private var numOfEpisode: Int = 0
constructor(parcel: Parcel) : this() {
val data = arrayOfNulls<String>(3)
parcel.readStringArray(data)
this.name = data[0]
this.numOfSeason = Integer.parseInt(data[1])
this.numOfEpisode = Integer.parseInt(data[2])
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name);
parcel.writeInt(numOfSeason);
parcel.writeInt(numOfEpisode);
}
private fun readFromParcel(parcel: Parcel){
name = parcel.readString()
numOfSeason = parcel.readInt()
numOfEpisode = parcel.readInt()
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Creator<Series> {
override fun createFromParcel(parcel: Parcel): Series {
return Series(parcel)
}
override fun newArray(size: Int): Array<Series?> {
return arrayOfNulls(size)
}
}
}
and then in your Activity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
val i = Intent(this, SecondActivity::class.java)
val testing = ArrayList<testparcel>()
i.putParcelableArrayListExtra("extraextra", testing)
startActivity(i)
}
class SecondActivity :Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.secondActivty_activity)
val testing = this.intent.getParcelableArrayListExtra<Parcelable>("extraextra")
}
}