I'm updating the font size of all the texts on the app, what I want to achieve is, when I select the font size, i should be able to update the font sizes of all the texts on that activity.
My only problem is i can't find the size property on the Spinner Object.
This is what I did for Text Views, is it possible to apply a code similar to this one for Spinners ?
const val HEADER_TEXT = 24
const val NORMAL_TEXT = 14
private fun updateAssetSize(textView: TextView, additionalSize: Int, type: Int) {
val size = additionalSize + type
textView.setTextSize(COMPLEX_UNIT_SP, size.toFloat());
}
//calling the method:
updateAssetSize(screenText, additionalFontSize, HEADER_TEXT)
Note: This should be done from code, since this will be updated on run time.
Based on #Zain Suggestion, I resolved this by using an adapterlist object. Instead of using String I created a custom class with fontSize and text properties in it.
class SpinnerItem(
val text: String,
var fontSize: Int
) {
// this is necessary, in order for the text to display the texts in the dropdown list
override fun toString(): String {
return text
}
}
Here's the AdapterList that I created:
class SpinnerItemListAdapter(
context: Context,
val resourceId: Int,
var list: ArrayList<SpinnerItem>
) : ArrayAdapter<SpinnerItem>(context, resourceId, list) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val text = this.list[position].text
val size = this.list[position].fontSize
val inflater = LayoutInflater.from(context)
val convertView = inflater.inflate(resourceId, parent, false)
val simpleTextView = convertView.findViewById(R.id.simpleTextView) as TextView
simpleTextView.text = text
simpleTextView.setTextSize(size.toFloat())
return convertView
}
// We'll call this whenever there's an update in the fontSize
fun swapList(list: ArrayList<SpinnerItem>) {
clear()
addAll(list)
notifyDataSetChanged()
}
}
Here's the custom XML File spinner_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/simpleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:padding="12dp"
android:textSize="16sp" />
The Spinner to be updated:
var fontSizes = arrayListOf(
SpinnerItem("Small", NORMAL_TEXT, "Default"),
SpinnerItem("Normal", NORMAL_TEXT, "Default"),
SpinnerItem("Large", NORMAL_TEXT, "Default"),
SpinnerItem("Largest", NORMAL_TEXT, "Default")
)
var fontSizeAdapterItem = SpinnerItemListAdapter(
this,
R.layout.spinner_item,
toSpinnerItemList(fontSizes, newSize)
)
Here's What will happen when we update it:
private fun updateSpinnerSize(additional: Int) {
val newSize = additional + NORMAL_TEXT
fontSizes = toSpinnerItemList(fontSizes, newSize)
fontSizeAdapterItem?.let {
it.swapList(fontSizes)
}
}
private fun toSpinnerItemList(
list: ArrayList<SpinnerItem>,
newSize: Int
): ArrayList<SpinnerItem> {
val itemList = ArrayList<SpinnerItem>()
for (item in list) {
item.fontSize = newSize
itemList.add(item)
}
return itemList
}
Related
I have a button (id: readyButtonIntro) inside a layout (introscreen.xml) that i need to enable. To do that, i have another button inside the RecyclerView.ViewHolder.
This is my Layout to need access
introscreen.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorWhite"
tools:context=".IntroScreenVC">
<LinearLayout
android:id="#+id/indicatorContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="40dp"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent" />
<Button
android:id="#+id/readyButtonIntro"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:background="#color/colorWhite"
android:textColor="#color/colorTerciary"
android:alpha="0"
android:enabled="false"
android:text="Ready"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
And the another button is inside into the ViewHolder
slide_item_container.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="15dp"
>
<Button
android:id="#+id/addData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Agregar Datos"
android:background="#drawable/button_rounded2"
/>
</LinearLayout>
How can I enable from inside the class that listener the button?
class IntroSlideViewHolder(view: View) : RecyclerView.ViewHolder(view) {
init {
addData.setOnClickListener(View.OnClickListener {
//NEED TO ENABLE THE BUTTON
// val introScreen = IntroScreenVC()
// introScreen.readyButton()
}
}
I have a fun into IntroScreenVC but always have a error that its null, if a pass the context or view, do nothing.
fun readyButton(){
readyButtonIntro.isEnabled = true
}
Could you help me with this? I would really appreciate it.
Thank you very much!
Regards.
Edit:
I put the adapter and the ViewHolder for more information.
I ignored that because I didn't want to create confusion. Sorry for that..
Class Constructor
data class IntroSlide(val title: String, val description: String, val icon: Int, val firstButton: Boolean, val secondButton: Boolean, val thirdButton: Boolean)
IntroScreenVC.kt
class IntroScreenVC: AppCompatActivity() {
private val introSliderAdapter = IntroScreenAdapter(
listOf(
IntroSlide(
"title1",
"description1",
R.drawable.logo,
false,
false,
false
),
IntroSlide(
"title2",
"description2",
R.drawable.doggrooming,
true,
false,
false
),
IntroSlide(
"title3",
"description3",
R.drawable.introscreen3,
false,
true,
false
)
)
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.introscreen)
introSliderViewPager.adapter = introSliderAdapter
}
}
IntroScreenAdapter.kt
class IntroScreenAdapter(private val introSlides: List<IntroSlide>) : RecyclerView.Adapter<IntroSlideViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IntroSlideViewHolder {
val layoutInflater = LayoutInflater.from(parent?.context)
val cellForRow = layoutInflater.inflate(R.layout.slide_item_container,parent,false)
return IntroSlideViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return introSlides.size
}
override fun onBindViewHolder(holder: IntroSlideViewHolder, position: Int) {
holder.bind(introSlides[position])
}
}
class IntroSlideViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val textTitle = view.findViewById<TextView>(R.id.textTitle)
private val textDescription = view.findViewById<TextView>(R.id.textDescription)
private val imageIcon = view.findViewById<ImageView>(R.id.imageSlideIcon)
private val addData = view.findViewById<Button>(R.id.addData)
private val addPet = view.findViewById<Button>(R.id.agregarMascota)
val contexto = itemView.context;
fun bind(introSlide: IntroSlide) {
textTitle.text = introSlide.title
textDescription.text = introSlide.description
imageIcon.setImageResource(introSlide.icon)
addData.isEnabled = introSlide.firstButton
addPet.isEnabled = introSlide.thirdButton
}
}
init {
addData.setOnClickListener(View.OnClickListener {
//ADD A ALERTDIALOG AND WHEN PRESS OK NEED TO ENABLE THAT BUTTON
val mDialogView = LayoutInflater.from(contexto).inflate(R.layout.alertdialog_add_data,null)
val builder = AlertDialog.Builder(contexto)
builder.setView(mDialogView)
val dialog: AlertDialog = builder.create()
dialog.show()
dialog.getWindow()?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT));
mDialogView.agregarDatosOK.setOnClickListener {
//HERE I NEED TO ENABLE THE BUTTON
//readyButtonIntro(introscreen.xml)
}
}
}
Edit2:
This is what I do with sharedPreferences.
IntroScreenAdapter.kt
class IntroSlideViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val textTitle = view.findViewById<TextView>(R.id.textTitle)
private val textDescription = view.findViewById<TextView>(R.id.textDescription)
private val imageIcon = view.findViewById<ImageView>(R.id.imageSlideIcon)
private val addData = view.findViewById<Button>(R.id.addData)
private val addPet = view.findViewById<Button>(R.id.agregarMascota)
//INIT sharedPreferences
private val prefs: SharedPreferences = view.context.getSharedPreferences(getString(R.string.prefs_file), Context.MODE_PRIVATE)
val contexto = itemView.context;
fun bind(introSlide: IntroSlide) {
textTitle.text = introSlide.title
textDescription.text = introSlide.description
imageIcon.setImageResource(introSlide.icon)
addData.isEnabled = introSlide.firstButton
addPet.isEnabled = introSlide.thirdButton
}
}
init {
addData.setOnClickListener(View.OnClickListener {
val mDialogView = LayoutInflater.from(contexto).inflate(R.layout.alertdialog_add_data,null)
val builder = AlertDialog.Builder(contexto)
builder.setView(mDialogView)
val dialog: AlertDialog = builder.create()
dialog.show()
dialog.getWindow()?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT));
mDialogView.agregarDatosOK.setOnClickListener {
//HERE EDIT THE sharedPreferences
with (prefs.edit()) {
putBoolean("ready_button_enabled", true)
apply()
}
dialog.dismiss()
}
}
}
IntroScreenVC.kt
class IntroScreenVC: AppCompatActivity() {
private val introSliderAdapter = IntroScreenAdapter(
listOf(
IntroSlide(
"title1",
"description1",
R.drawable.logo,
false,
false,
false
),
IntroSlide(
"title2",
"description2",
R.drawable.doggrooming,
true,
false,
false
),
IntroSlide(
"title3",
"description3",
R.drawable.introscreen3,
false,
true,
false
)
)
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.introscreen)
introSliderViewPager.adapter = introSliderAdapter
}
//HERE PUT THE RESUME TO EXPECT THE SHOW AND ENABLE THE BUTTON
override fun onResume() {
super.onResume()
val prefs = getSharedPreferences(getString(R.string.prefs_file), Context.MODE_PRIVATE)
val buttonEnabled = prefs.getBoolean("ready_button_enabled", false)
readyButtonIntro.isEnabled = buttonEnabled
if (buttonEnabled) {
readyButtonIntro.alpha = 1f
}else {
readyButtonIntro.alpha = 0f
}
}
}
SOLUTION:
Into the Activity (IntroScreenVC)
class IntroScreenVC: AppCompatActivity(), IntroScreenAdapter.AdapterOnClick {
private val introSliderAdapter =
listOf(
...
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.introscreen)
introSliderViewPager.adapter = IntroScreenAdapter(introSliderAdapter, this)
}
...
override fun onClick() {
//HERE ENABLE AND SHOW THE BUTTON
readyButtonIntro.isEnabled = true
readyButtonIntro.alpha = 1f
}
And the into the Adapter and RecyclerView
class IntroScreenAdapter(private val introSlides: List<IntroSlide>, val adapterOnClick: AdapterOnClick) : RecyclerView.Adapter<IntroScreenAdapter.IntroSliderViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IntroScreenAdapter.IntroSliderViewHolder {
val layoutInflater = LayoutInflater.from(parent?.context)
val cellForRow = layoutInflater.inflate(R.layout.slide_item_container,parent,false)
return IntroSliderViewHolder(cellForRow)
}
...
inner class IntroSliderViewHolder(view: View) : RecyclerView.ViewHolder(view) {
...
init {
addData.setOnClickListener(View.OnClickListener {
val mDialogView = LayoutInflater.from(contexto).inflate(R.layout.alertdialog_add_data,null)
val builder = AlertDialog.Builder(contexto)
builder.setView(mDialogView)
val dialog: AlertDialog = builder.create()
dialog.show()
dialog.getWindow()?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT));
mDialogView.agregarDatosOK.setOnClickListener {
//FINALLY HERE CHANGE THE BUTTON TO ENABLE :)
adapterOnClick.onClick()
}
}
}
}
As I understand your problem you have a class A that is trying to communicate (change something) in class B.
There are several options for solving this kind of problem, depending on your exact needs.
From the code you have provided the relation of your Layouts and classes is not clear enough to me to give a more precise answer.
First of all, I understand you are using a recycler view.
A recycler view can have many items, and I assume you want to be able to enable that button from each item.
In order to let your IntroScreen class communicate with your viewholder, you have to pass a reference to the ViewHolder constructor.
For this purpose you could implement a simple "callback pattern".
Here is an example for defining an interface (e.g. for a function that enables the button) and implementing the callback.
Have a read here to see a well-explained example in Java. In Kotlin you could do it the same way.
Here a summary of the implementation steps:
define interface EnableButtonCallback that implements an abstract method enableButton
let your InfoScreen class implement that interface (in which you enable the button)
pass your InfoScreen class to your RecyclerView adapter and then from your adapter to your ViewHolder
in your ViewHolder onClickListener call the interface method enableButton
Update 2020/08/11
I try to give suggestions based on your updated code.
In the intro screen you set your viewPager adapter, but it is still not clear where this property is coming from and where exactly it is displayed. I guess maybe you just cut out the parameter definition. However, I just assume you have your views set up properly and this is not a problem here. For using recycler view with viewPager I found some related information here.
I can not yet see your use case clearly yet. Are you adding data persistently? Then should your button in the IntroScreen be permanently enabled?
In this case probably SharedPreferences are a good choice for persisting this kind of information. Even when it doesn't need to be persisted. Reading one shared preference file is lightweight and quick enough to be done on the main thread.
I will give you an example implementation here:
Get a shared preferences object
val sharedPref = activity?.getSharedPreferences(
"intro_button_settings_file", Context.MODE_PRIVATE) // String with the key should be in your string resource file
Pass your sharedPref to your adapter and your viewHolder and write to it:
with (sharedPref.edit()) {
putBoolean("ready_button_enabled", true) // String with the key should be in your string resource file
commit()
}
in your IntroScreen check the setting
val readyButtonShouldBeEnabled = sharedPref.getBoolean("ready_button_enabled",
false) // defaults to false
If, after clicking your enable button (that sets the setting to true), you need to return to your IntroScreen activity: then you could enable your button in your activities onResume method
A different solution would be:
You check the setting in your IntroScreen onClick method.
Then you don't need to disable the button.
You just set:
// in your IntroScreen readyButtonIntro onClick method
val buttonEnabled = sharedPref.getBoolean("ready_button_enabled",
false)
if (!buttonEnabled) {
// optional: write a Toast to notify the user why the button is doing nothing (yet)
Toast.makeText(yourIntroScreenContext, "First agregar datos", Toast.LENGTH_SHORT).show()
return // onClick returns, so nothing else will happen when clicked
}
... // your code when the button **should** be enabled
If your button should be disabled again, simply save false to the setting.
Since I do not know more about your use case, this seems like an easy and quick solution to me. This way you do not need to bother with implementing an interface. Anyways, when clicking your button in your viewHolder there is no immediate action taking place in your IntroScreen activity. You still want the user to return to the IntroScreen and click the enabled button.
Then checking if your button was enabled just when clicking on it appears sufficient to me.
I'm trying to make my own DayView/TimelineView/AgendaView it has many names, but essentially - something like this:
I couldn't find a single library that works + supports API version 21 + allows the customization that I need (ex. custom time formatting, event item layout, etc). Therefore, I decided to make my own. I'm curious if it's possible to create this using simple ItemDecoration as the time, where the events will be what actually populates the RecyclerView.
I have created something similar using ItemDecoration hope it helps.
in your Activity/Fragment call setAdapter()
#RequiresApi(Build.VERSION_CODES.O)
private fun setAdapter() {
val data = arrayListOf<EventPOJO>(
EventPOJO(
"Entry 1 \n Lorem Ipsum is simply dummy text of the printing and typesetting industry. ",
"12:00 AM"
),
EventPOJO("Entry 2 ", "01:00 AM")
)
val mAdapter = Adapter<EventPOJO>(data)
recyclerDay.run {
adapter = mAdapter
layoutManager = LinearLayoutManager(context)
addItemDecoration(ScheduleTimeHeadersDecoration(data))
}
}
your normal RecyclerView.Adapter
class Adapter<T>(
private val eventList: ArrayList<T>
) : RecyclerView.Adapter<Adapter.EventListViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventListViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view: View = layoutInflater.inflate(R.layout.item_day, parent, false)
return EventListViewHolder(view)
}
override fun getItemCount() = eventList.size
override fun onBindViewHolder(holder: EventListViewHolder, position: Int) {
holder.bind(eventList[position])
}
class EventListViewHolder(var view: View) :
RecyclerView.ViewHolder(view) {
private var name: TextView? = null
init {
name = view.findViewById(R.id.name)
name?.creatRandomColor()
}
fun <T> bind(bindObj: T) {
if (bindObj is EventPOJO) {
name?.text = bindObj.name
}
}
private fun TextView.creatRandomColor() {
val rnd = Random()
val color = Color.argb(
255,
rnd.nextInt(256),
rnd.nextInt(256),
rnd.nextInt(256)
)
this.setBackgroundColor(color)
}
}}
the data Object
data class EventPOJO(
val name: String,
val time: String
)
your item_day.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:minHeight="50dp"
android:padding="5dp"
android:text="Entry 1"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="#+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="100dp" />
</android.support.constraint.ConstraintLayout>
and finally your itemDecoration
/**
* A [RecyclerView.ItemDecoration] which draws sticky headers for a given list of
sessions.
*/
#RequiresApi(Build.VERSION_CODES.O)
class ScheduleTimeHeadersDecoration(
sessions: ArrayList<EventPOJO>
) : RecyclerView.ItemDecoration() {
private val paint: TextPaint = TextPaint(ANTI_ALIAS_FLAG).apply {
color = Color.BLUE
textSize = 50f
}
private val width: Int = 350
// Get the sessions index:start time and create header layouts for each
#RequiresApi(Build.VERSION_CODES.O)
private val timeSlots: Map<Int, StaticLayout> =
sessions
.mapIndexed { index, session ->
index to session.time
}.map {
it.first to createHeader(it.second)
}.toMap()
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val parentPadding = parent.paddingTop
var earliestPosition = Int.MAX_VALUE
var previousHeaderPosition = -1
var previousHasHeader = false
var earliestChild: View? = null
for (i in parent.childCount - 1 downTo 0) {
val child = parent.getChildAt(i)
if (child.y > parent.height || (child.y + child.height) < 0) {
// Can't see this child
continue
}
val position = parent.getChildAdapterPosition(child)
if (position < 0) {
continue
}
if (position < earliestPosition) {
earliestPosition = position
earliestChild = child
}
val header = timeSlots[position]
if (header != null) {
drawHeader(c, child, parentPadding, header, child.alpha, previousHasHeader)
previousHeaderPosition = position
previousHasHeader = true
} else {
previousHasHeader = false
}
}
if (earliestChild != null && earliestPosition != previousHeaderPosition) {
// This child needs a sicky header
findHeaderBeforePosition(earliestPosition)?.let { stickyHeader ->
previousHasHeader = previousHeaderPosition - earliestPosition == 1
drawHeader(c, earliestChild, parentPadding, stickyHeader, 1f, previousHasHeader)
}
}
}
private fun findHeaderBeforePosition(position: Int): StaticLayout? {
for (headerPos in timeSlots.keys.reversed()) {
if (headerPos < position) {
return timeSlots[headerPos]
}
}
return null
}
private fun drawHeader(
canvas: Canvas,
child: View,
parentPadding: Int,
header: StaticLayout,
headerAlpha: Float,
previousHasHeader: Boolean
) {
val childTop = child.y.toInt()
val childBottom = childTop + child.height
var top = (childTop).coerceAtLeast(parentPadding)
if (previousHasHeader) {
top = top.coerceAtMost(childBottom - header.height - 25)
}
paint.alpha = (headerAlpha * 255).toInt()
canvas.withTranslation(y = top.toFloat()) {
header.draw(canvas)
}
}
/**
* Create a header layout for the given [startTime].
*/
private fun createHeader(startTime: String): StaticLayout {
val text = SpannableStringBuilder().apply {
append(startTime.split(" ")[0])
append(System.lineSeparator())
append(startTime.split(" ")[1])
}
return newStaticLayout(text, paint, width, ALIGN_CENTER, 1f, 0f, false)
}
fun newStaticLayout(
source: CharSequence,
paint: TextPaint,
width: Int,
alignment: Layout.Alignment,
spacingmult: Float,
spacingadd: Float,
includepad: Boolean
): StaticLayout {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StaticLayout.Builder.obtain(source, 0, source.length, paint, width).apply {
setAlignment(alignment)
setLineSpacing(spacingadd, spacingmult)
setIncludePad(includepad)
}.build()
} else {
#Suppress("DEPRECATION")
StaticLayout(source, paint, width, alignment, spacingmult, spacingadd, includepad)
}
}
private inline fun Canvas.withTranslation(
x: Float = 0.0f,
y: Float = 0.0f,
block: Canvas.() -> Unit
) {
val checkpoint = save()
translate(x, y)
try {
block()
} finally {
restoreToCount(checkpoint)
}
}
}
reference Google I/O 2019 Android App
So I want to show a suggestion in a searchView which is now inside a toolbar. So I created this adapter and it doesn't seem to work and the app is also crashing with this error StringIndexOutOfBoundsException
Adapter
class SearchHitchAdapter(context: Context, cursor: Cursor) : CursorAdapter(context, cursor, false) {
private val dataSet = arrayListOf<String>(*context.resources.getStringArray(R.array.city_states))
override fun newView(context: Context?, cursor: Cursor?, parent: ViewGroup?): View {
val inflater = context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
return inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false)
}
override fun bindView(view: View?, context: Context?, cursor: Cursor?) {
val position = cursor!!.position
val textView = view!!.findViewById(android.R.id.text1) as TextView
textView.text = dataSet[position]
}
}
This function is being called inside onQueryTextChange
private fun setUpSearchSuggestions(query: String) {
val dataSet = getCityList()
val columns = arrayOf("_id", "text")
val temp = arrayOf(0, "default")
val cursor = MatrixCursor(columns)
for (i in 0 until dataSet.size) {
val city = dataSet[i]
if (city.toLowerCase(Locale.US).contains(query.toLowerCase(Locale.US))) {
temp[0] = i
temp[1] = city[i]
cursor.addRow(temp)
}
}
searchVIew.suggestionsAdapter = SearchAdapter(context!!, cursor)
}
This is not working can somebody help me or suggest me something.
This line in your code looks suspicious:
temp[1] = city[i]
This is the same as writing temp[i] = city.get(i): you are trying to get the character from city at position i.
Since i is the loop variable, and you're looping over dataset, this is very likely a mistake. There's no guarantee that every string in the data set is as long as the data set itself. Imagine that you have a list of a thousand cities; chances are very good that each city's name is less than one thousand characters long.
The exception "StringIndexOutOfBoundsException" says that you are accessing the data which doesn't exists. Check if your dataset has the right list.
I am trying to return a list from inside firestore function based on if a condition is true.I want to return different lists when different categories are selected.
I tried:
putting the return statement out of firestore function which did not work and returned empty list due to firestore async behaviour.
creating my own callback to wait for Firestore to return the data using interface as I saw in some other questions but in that case how am i supposed to access it as my function has a Int value(i.e.private fun getRandomPeople(num: Int): List<String>)?
What could be the way of returning different lists for different categories based on firestore conditions?
My code(Non Activity class):
class Board// Create new game
(private val context: Context, private val board: GridLayout) {
fun newBoard(size: Int) {
val squares = size * size
val people = getRandomPeople(squares)
createBoard(context, board, size, people)
}
fun createBoard(context: Context, board: GridLayout, size: Int, people: List<String>) {
destroyBoard()
board.columnCount = size
board.rowCount = size
var iterator = 0
for(col in 1..size) {
for (row in 1..size) {
cell = RelativeLayout(context)
val cellSpec = { GridLayout.spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f) }
val params = GridLayout.LayoutParams(cellSpec(), cellSpec())
params.width = 0
cell.layoutParams = params
cell.setBackgroundResource(R.drawable.bordered_rectangle)
cell.gravity = Gravity.CENTER
cell.setPadding(5, 0, 5, 0)
text = TextView(context)
text.text = people[iterator++]
words.add(text.text as String)
text.maxLines = 5
text.setSingleLine(false)
text.gravity = Gravity.CENTER
text.setTextColor(0xFF000000.toInt())
cell.addView(text)
board.addView(cell)
cells.add(GameCell(cell, text, false, row, col) { })
}
}
}
private fun getRandomPeople(num: Int): List<String> {
val mFirestore: FirebaseFirestore=FirebaseFirestore.getInstance()
val mAuth: FirebaseAuth=FirebaseAuth.getInstance()
val currentUser: FirebaseUser=mAuth.currentUser!!
var validIndexes :MutableList<Int>
var chosenIndexes = mutableListOf<Int>()
var randomPeople = mutableListOf<String>()
mFirestore.collection("Names").document(gName).get().addOnSuccessListener(OnSuccessListener<DocumentSnapshot>(){ queryDocumentSnapshot->
var categorySelected:String=""
if (queryDocumentSnapshot.exists()) {
categorySelected= queryDocumentSnapshot.getString("selectedCategory")!!
print("categoryselected is:$categorySelected")
Toast.makeText(context, "Got sel category from gameroom:$categorySelected", Toast.LENGTH_LONG).show()
when(categorySelected){
"CardWords"->{
for (i in 1..num) {
validIndexes=(0..CardWords.squares.lastIndex).toMutableList()
val validIndexIndex = (0..validIndexes.lastIndex).random()
val peopleIndex = validIndexes[validIndexIndex]
chosenIndexes.add(peopleIndex)
val person = CardWords.squares[peopleIndex]
randomPeople.add(person)
validIndexes.remove(peopleIndex)
peopleIndexes = chosenIndexes.toList()
}
}
else->{}
}
}
else {
Toast.makeText(context, "Sel category does not exist", Toast.LENGTH_LONG).show()
}
}).addOnFailureListener(OnFailureListener { e->
val error=e.message
Toast.makeText(context,"Error:"+error, Toast.LENGTH_LONG).show()
})
return randomPeople.toList()
}
}
Activity A:
Board(this, gridLay!!)
I have a listView that has a custom layout that contains a fragment. The problem I am having is when I add the fragment to the listView row I get a never ending loop that keeps going through the getView code.
I have only been doing Android coding for a couple months so any help would be great. Please let me know if there is any more of my code I need to post
This is my adapter code:
class AdapterReply(
private val context: Context,
private val ph: Phan,
private val replies: ArrayList<Reply> ) : BaseAdapter() {
override fun getCount(): Int {
return replies.size
}
override fun getItem(position: Int): Reply {
return replies[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup?): View? {
val rowMain: View?
val rowHolder: ListRowHolder
val contacts = ph.contacts
val reply = replies[position]
Log.d("AdapterReply", "Binding reply: Position $position ${reply.id} Detail: ${reply.detail}")
if (convertView == null) {
val layoutInflater = LayoutInflater.from(viewGroup!!.context)
rowMain = layoutInflater.inflate(R.layout.reply_item_row, viewGroup, false)
rowHolder = ListRowHolder(rowMain)
rowMain.tag = rowHolder
Log.d("AdapterReply", "New Item")
} else {
rowMain = convertView
rowHolder = rowMain.tag as ListRowHolder
Log.d("AdapterReply", "Old item from memory")
}
rowHolder.itemDetail.text = Helpers.anonymizeContent(reply.detail, ph.anonymousMetadata, ph.isViewerMember())
rowHolder.itemLastActive.text = Helpers.getFormattedDate(reply.lastActive())
if(reply.attachments.size > 0){
val imageAttachment = reply.attachments.filter { attachment: Attachment -> attachment.type == 0 }[0]
var imageUrl = Constants.BASE_URL + imageAttachment.url
if(imageAttachment.anonymous()){
imageUrl = Constants.BASE_URL + imageAttachment.anonUrl
}
Picasso.with(rowMain!!.context).load(imageUrl).into(rowHolder.itemImage)
}
var poster: Contact? = reply.contact
if(contacts.size > 0) {
val posterList = contacts.filter { contact: Contact -> contact.id == reply.rlContactID }
if (posterList.isNotEmpty()) {
poster = posterList[0]
}
}
if(poster != null) {
if(poster.anonymizedName.isNotEmpty()) {
rowHolder.itemPoster.text = poster.anonymizedName
} else {
val posterName = "${poster.firstName} ${poster.lastName}"
rowHolder.itemPoster.text = posterName
}
//Code the causes the problem
val manager = (rowMain!!.context as AppCompatActivity).supportFragmentManager
val posterAvatarFragment =
AvatarFragment.newInstance(poster)
manager.beginTransaction()
.add(R.id.reply_avatar_fragment, posterAvatarFragment, "ReplyAvatarFragment")
.commit()
//End Code the causes the problem
}
bindReplies(rowMain, ph, reply.replies)
return rowMain
}
internal class ListRowHolder(row: View?) {
var itemDetail : TextView = row!!.reply_view_detail
var itemImage : ImageView = row!!.reply_view_image
var itemPoster : TextView = row!!.reply_view_posterName
var itemLastActive : TextView= row!!.reply_view_lastActive
var itemReplyReplies: ListView = row!!.reply_view_replyList
}
private fun bindReplies(viewRow: View?, ph: Phan, replies : ArrayList<Reply>){
if(replies.isNotEmpty()) {
val myObject = AdapterReply(context, ph, replies)
val replyListView = viewRow!!.reply_view_replyList
Log.d("AdapterReply", "Binding Replies: ${ph.encodedId} ${replies.size}")
replyListView.adapter = myObject
}
}
}
manager.beginTransaction()
.add(R.id.reply_avatar_fragment, posterAvatarFragment, "ReplyAvatarFragment")
.commit()
Man, I'm not sure do you know, what function performs adapter class in listview. Adapter class is used to fill listview by rows passed in array inside class constructor. getView method is called for every row in array, so for example, if you have an array with 10 rows, this code will fire ten times. If you do automatically change fragment to another, and during filling an old view you will change layout to a another layout with the same data, your code will make an infinity loop, because you will repeat all time the same cases. You should avoid actions, which will dynamically change layout during load him. In my sugestion, if you want to use a multiple layouts inside one adapter, there are two special methods to set layout for row, based on some conditions: getItemViewType and getViewTypeCount. In first one you can use your conditions to check, which layout should be used for row. Second one is to set number of layouts, which will be used in your adapter class. Let search some examples of usage.