How to initialize array with user input? - android

I am trying to initialize my array upon user input. Say if the user enters an item into the text field and then they press the add button, I want the string from the text field to go into the array.
class CustomList : AppCompatActivity() {
lateinit var thingsList: MutableList<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_custom_list)
addItemBtn.setOnClickListener {
if(item_text_field.text.toString() == "") {
Toast.makeText(this, "Must enter text first", Toast.LENGTH_SHORT).show()
} else {
val item = item_text_field.text.toString()
thingsList = item
}
}
}
}

There's no need of lateinit here, and unless you initialize the variable you cannot add items to it. You can instead use lazy to initialize it when needed. And the MutableList.add() is used to add items to a list.
// thingsList will be initialized whenever accessed for the first time
val thingsList by lazy { mutableListOf<String>() }
addItemBtn.setOnClickListener {
if(item_text_field.text.toString() == "") {
Toast.makeText(this, "Must enter text first", Toast.LENGTH_SHORT).show()
} else {
// use add on the MutableList to add times into it
thingsList.add(item_text_field.text.toString())
}
}

Related

Summary doesn't persists on dynamically load data in ListPreference

In my SettingActivity I'm loading some data to be shown in ListPreference from Room, once the items are selected all works correctly, the value is saved to `SharedPreferences and the summary is shown correctly, but once I return to SettingsActivity the summary value is reset to null.
Here is what is happening:
My code is pretty simple, onViewCreated() I start observing LiveData to be shown in ListPreference and then I set the values for entries and entryValues
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.shops.observe(viewLifecycleOwner) {
setShopsPreferences(it)
}
}
private fun setShopsPreferences(shops: List<Shop>) {
val shopsPreference = preferenceManager.findPreference<ListPreference>("defaultShop")
if (shops.isEmpty()) {
shopsPreference?.isEnabled = false
return
} else {
shopsPreference?.isEnabled = true
}
val entries: ArrayList<String> = ArrayList()
val entryValues: ArrayList<String> = ArrayList()
shops.forEach {
entries.add(it.description)
entryValues.add(it.id)
}
shopsPreference?.entryValues = entryValues.toArray(arrayOfNulls<CharSequence>(entryValues.size))
shopsPreference?.entries = entries.toArray(arrayOfNulls<CharSequence>(entries.size))
}
ViewModel:
#HiltViewModel
class ShopsViewModel #Inject constructor(repository: ShopsRepository) : ViewModel() {
private val _shops = MutableLiveData<List<Shop>>()
val shops: LiveData<List<Shop>> = _shops
init {
repository.getAllShops().observeForever {
_shops.value = it
}
}
}
Repository:
fun getAllShops(): LiveData<List<Shop>> {
return shops.select()
}
DAO:
#Dao
interface ShopsDAO {
#Query("SELECT * FROM shops")
fun select(): LiveData<List<Shop>>
}
You are populating list entries dynamically after the preference hierarchy is already created by inflating the xml. But during that time there was no entry, hence the value was null. The data was then retrieved asynchronously but the change will not be reflected. So you have to set the summary manually.
Another approach I'm not sure about is to call recreate on the activity after populating the data inside the observer listener.
To resolve the issue with dynamic data in a ListPreference I've made some changes to my code, first of call in onCreatePreferences() I've added a preference listener to my ListPreference like this:
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
val shopsPreference = preferenceManager.findPreference<ListPreference>("defaultShop")
shopsPreference?.setOnPreferenceChangeListener { preference, newValue ->
val description = viewModel.shops.value?.find { it.id == newValue }
preference.summary = description?.description
true
}
}
So in that way on each new selection, I'm looking for the matching value for that preference in my ViewModel and getting the description for it which then is set as the summary.
While to set the summary every time the list has been changed or the SettingsActivity is opened I've added the following code to my setShopsPreferences() function:
private fun setShopsPreferences(shops: List<Shop>?) {
if (shops == null) {
return
}
val shopsPreference = preferenceManager.findPreference<ListPreference>("defaultShop")
// The preference is enabled only if there are shows inside the list
shopsPreference?.isEnabled = shops.isNotEmpty()
// Getting the defaultShop preference value, which will be used to find the actual shop description
val defaultShop = sharedPreferences.getString("defaultShop", "0")
defaultShop?.let { shopId ->
// Setting the summary based on defaultShop saved preference
shopsPreference?.summary = shops.find { it.id == shopId }?.description
}
val entries: ArrayList<String> = ArrayList()
val entryValues: ArrayList<String> = ArrayList()
shops.forEach {
entries.add(it.description)
entryValues.add(it.id)
}
shopsPreference?.entryValues =
entryValues.toArray(arrayOfNulls<CharSequence>(entryValues.size))
shopsPreference?.entries = entries.toArray(arrayOfNulls<CharSequence>(entries.size))
}

ArrayList automatically duplicates items in it

I have created a variable private var deals=ArrayList<Deals>() in a fragment and I have set a click listener in the onCerateView() like the following.
binding.tvAllDeals.setOnClickListener {
viewAllDeals()
}
So that it will trigger the following method
private fun viewAllDeals(){
val intent = Intent(context,ViewAllDealsActivity::class.java)
intent.putExtra("details",deals)
Log.d("Tag2", "Size is ${deals.size}")
startActivity(intent)
}
I have the following function to get the data from the firestore and then I save the result in the variable 'deals'. However, whenever I click the 'tvAllDeals' it shows many images, when I check the size of the 'deals' using Log.d 'Tag1' always shows the correct size, which is 3, whereas 'Tag2' show some random numbers like 6, 9, 24. I try to find out why this is happening but I didn't get any idea. The variable 'deals' is not used anywhere else other than declaring and initializing, to assign the value and to pass it in the 'viewAllDeals()'
private fun getDeals() {
FirestoreClass().getDeals(
onSuccess = { list ->
Result.success(list)
successDeals(list) ///// THIS FUNCTION WILL SHOW THE IMAGES IN A VIEWPAGER
deals.clear()
deals=list
Log.d("Tag1", "Size is ${deals.size}")
},
onFailure = {
}
)
}
Edit:
NOTE: 'Tag3' also shows correct array size like 'Tag1'. However,
private fun successDeals(list: ArrayList<Deals>) {
Log.d("Tag3", "Size is ${deals.size}")
if (list.size > 0) {
binding.vpDeals.visibility = View.VISIBLE
val adapter = DealsAdapter(binding.vpDeals,requireContext(), list)
binding.vpDeals.adapter = adapter
binding.vpDeals.orientation = ViewPager2.ORIENTATION_HORIZONTAL
sliderHandle= Handler()
sliderRun= Runnable {
binding.vpDeals.currentItem=binding.vpDeals.currentItem+1
}
binding.vpDeals.registerOnPageChangeCallback(
object :ViewPager2.OnPageChangeCallback(){
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
sliderHandle.removeCallbacks(sliderRun)
sliderHandle.postDelayed(sliderRun,4000)
}
}
)
} else {
binding.vpDeals.visibility = View.GONE
}
}

Kotlin get ids of selected options

I have multiple option select and I need to get array of selected options but all I get is latest option selected.
Code
class PublishActivity : AppCompatActivity() {
var selectedTags: List<String>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_publish)
pTags.setOnClickListener {
var tagIds = ArrayList<String>()
val tagOptions = ArrayList<String>()
for (i in tags) {
tagOptions.add(i.title)
tagIds.add(i.id)
}
var checkedItems = ArrayList<Int>()
checkedItems.forEach{ index -> tagIds[index + 1] }
MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.project_tags))
.setMultiChoiceItems(tagOptions.toTypedArray(), null) { dialog, which, checked ->
if (checked) {
checkedItems.add(which)
} else if (checkedItems.contains(which)) {
checkedItems.remove(Integer.valueOf(which))
}
// Respond to item chosen
pTags.setText("${checkedItems.size} tags selected")
}
.setPositiveButton(resources.getString(R.string.ok)) { dialog, which ->
for (i in checkedItems) {
Log.d("eeee1", tagOptions[i])
selectedTags = listOf(tagOptions[i])
}
}
.setNeutralButton(resources.getString(R.string.clear)) { dialog, which ->
pTags.text = null
pTags.hint = "0 tag selected"
if (checkedItems.size > 0) {
checkedItems.clear()
}
}
.show()
}
}
}
Log.d("eeee1", tagOptions[i]) returns such data in logcat
D/eeee1: 3D Printing
D/eeee1: 3D Architecture
D/eeee1: .NET/Mono
D/eeee1: ActionScript
but in my selectedTags I get only D/eeer1: [ActionScript]
It supposed to give me something like this D/eeer1: ["3D Printing", "3D Architecture", ".NET/Mono", "ActionScript"]
PS: what I'm actually look to achieve here is to get id of those selected items instead of their names that's why I have var tagIds = ArrayList<String>() but if that's not possible to achieve as long as it just return array of all names (like sample above) it's fine by me as well.
Any idea?
The following code sets your variable to a list with a single item. So you just overwrite your variable over and over again
selectedTags = listOf(tagOptions[i])
you need:
//Declaration
var selectedTags: MutableList<String> = mutableListOf()
...
// In loop
selectedTags.add(tagOptions[i])
You could also do it with a more functional approach:
//Declaration
var selectedTags: List<String>? = listOf()
...
// Skip the loop and use the map function
.setPositiveButton(resources.getString(R.string.ok)) { dialog, which ->
selectedTags = checkedItems.map{ tagOptions[it] }
}
To get the Id's instead of the titles you should just be able to use your tagIds instead of tagOptions. Just make sure that you get your typing right. The selectedTags list needs to be of the same type as tag.id.
You are getting only last inserted value because you are creating fresh list when ok button is clicked and assigning it to selectedTags. Problem at selectedTags = listOf(tagOptions[i]) line of your code.
Solution:
Declare a single list and put selected values into it. Like :
val selectedTags = arrayListOf<String>()
then use below code inside ok button click:
.setPositiveButton("Ok") { dialog, which ->
for (i in checkedItems) {
//selectedTags = listOf(tagOptions[i])
selectedTags.add(tagOptions[i])
}
}

How to iterate for variable names in Kotlin?

I'm doing a project which has a lot of buttons. I would like to iterate over those buttons by its name. They are all named 'levelXbutton', where X can be a big number.
I'm now doing it with a lot of lines of code. But I'm sure there's a way to do it in a loop, specially in Kotlin.
For example, this is one of the operations I'd like to do:
if(FacadeData.getLastUnlockedLevel()<2){ binding.lvl2Button.setTextColor(Color.WHITE)}
if(FacadeData.getLastUnlockedLevel()<3){ binding.lvl3Button.setTextColor(Color.WHITE)}
if(FacadeData.getLastUnlockedLevel()<4){ binding.lvl4Button.setTextColor(Color.WHITE)}
if(FacadeData.getLastUnlockedLevel()<5){ binding.lvl5Button.setTextColor(Color.WHITE)}
You can make a loop to go through the buttons numbers and then load their ids with a string like so:
for (i in 0..20) {
val layoutID = context.resources.getIdentifier("lvl${i}Button", "id", context.packageName)
val button = findViewById(layoutID) as Button
...
}
Maybe you should try something like this:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
applyChangeRecursively(findViewById<ViewGroup>(android.R.id.content)) {
it.setBackgroundColor(Color.WHITE)
}
}
fun getResourceName(view:View):String? {
if (view.id > 0 && view is Button) {
return view.resources.getResourceName(view.id)
}
return null
}
fun matchesLabel(resourceName:String?):Boolean = resourceName?.matches(Regex(".*lvl\\d+Button")) ?: false
fun applyChange(v:View, u:(View)->Unit) = v.run(u)
fun applyChangeRecursively(parent:View, fun1:(View)->Unit){
when (parent) {
is ViewGroup -> parent.children.forEach{ applyChangeRecursively(it, fun1) }
else -> if(matchesLabel(getResourceName(parent))) { applyChange(parent, fun1) }
}
}
}

Selecting which button pressed in recyclerview row kotlin

I have had a look at various stackoverflow questions and answers to this problem and still struggling to get the right solution. If someone has a link to my problem with a solution I will mark it as duplicate but I have not found one yet.
Note: Code has been upgraded to Kotlin so mindful of some code may not be optimized for Kotlin best practices yet.
I have captured the button pressed in my RecyclerAdapter and now want it passed back to my Activity that has the recyclerView.addOnItemTouchListener.
My RecyclerAdapter code is as follows:
override fun onBindViewHolder(holder: NotesPicsRecyclerAdapter.RecyclerViewHolder, position: Int) {
val data_provider = arrayList[position]
holder.notePicShareButton.setOnClickListener {
Toast.makeText(context, "Share Button", Toast.LENGTH_SHORT).show()
}
holder.notePicChangePicButton.setOnClickListener {
Toast.makeText(context, "Edit Button", Toast.LENGTH_SHORT).show()
}
holder.notePicDeleteButton.setOnClickListener {
Toast.makeText(context, "Delete Button", Toast.LENGTH_SHORT).show()
}
try {
holder.imageText.text = data_provider.notes_text
val imageFile = data_provider.image_path
val inputStream = context.assets.open(imageFile)
val d = Drawable.createFromStream(inputStream, null)
holder.imagePath.setImageDrawable(d)
} catch (e: IOException) {
e.printStackTrace()
}
}
class RecyclerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
internal var imagePath: ImageView
internal var imageText: TextView
internal var notePicShareButton: Button
internal var notePicChangePicButton: Button
internal var notePicDeleteButton: Button
init {
imagePath = view.findViewById<View>(R.id.single_note_pic_image) as ImageView
imageText = view.findViewById<View>(R.id.single_note_pic_text) as TextView
notePicShareButton = view.findViewById<View>(R.id.single_note_pic_share_button) as Button
notePicChangePicButton = view.findViewById<View>(R.id.single_note_pic_change_pic_button) as Button
notePicDeleteButton = view.findViewById<View>(R.id.single_note_pic_delete_button) as Button
}
}
My onCreate in my Activity code snippet is as follows:
recyclerView.addOnItemTouchListener(
MyRecyclerViewClickListener(this, object : MyRecyclerViewClickListener.OnItemClickListener {
override fun onItemClick(view: View, position: Int) {
val thePos = arrayList[position].list_id
Toast.makeText(applicationContext, "This row: $thePos", Toast.LENGTH_SHORT).show()
}
})
)
All Toast messages in my RecyclerAdapter and Activity work so I have captured which button is being pressed as well as the row being selected, but I need the button information to be passed back to my Activity so I can use it with the row that was pressed. Not sure how to proceed and still digging but any info would be appreciated.
I think you can do this.
Create Variable and setter of that variable in your activity.
private YourObject localVariable;
public YourObject setLocalVariable(YourObject localVariable){
this.localVariable= localVariable;
}
And on Clicking on recycle View List Listener.
Typecast your context into your activity and set your Local variable over there.
((YourActivity)context).setLocalVariable(arrayList[position])
You can use the EventBus library it will achieve what you want easy
https://github.com/greenrobot/EventBus

Categories

Resources