Kotlin filter Search EditText in RecyclerView - android

In my android app Api 28 with Kotlin, I create an Interface "listOfCountry.xml", an editText for search and a recycler view that contains all the list of country as the following code:
<EditText
android:id="#+id/search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:padding="5dp"
android:layout_weight="1"
android:background="#android:color/transparent"
android:fontFamily="#font/cairo"
android:hint="Search..."
android:imeOptions="actionSearch"
android:maxLines="1"
android:singleLine="true"
android:textSize="14sp"/>
<TextView
android:id="#+id/cancelSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/cairo"
android:layout_marginTop="15dp"
android:layout_marginStart="20dp"
android:text="#string/cancel_msg"
android:textSize="14sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewGovernmentOrSectionList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/lyt_search" />
I want to get a country from the list with the search filter, the following code is the description of my adapter :
class CountryItemAdapter(var countries: Array<AddressesData>, var mListener: OnItemClickListener) : RecyclerView.Adapter<CountryItemAdapter.ViewHolder>()
, Filterable {
private var selectedPos = -1
var searchableList: MutableList<AddressesData> = arrayListOf()
private var onNothingFound: (() -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_country, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = countries.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item: AddressesData = countries[position]
holder.tickImage.visibility = (if (selectedPos == position) View.VISIBLE else View.GONE)
holder.country.text = item.englishName.toString()
holder.itemView.setOnClickListener {
selectedPos = position
mListener.onItemClick(holder.itemView, item)
notifyDataSetChanged()
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val country: TextView = itemView.counrty
val tickImage: ImageView = itemView.tickGovernment
}
interface OnItemClickListener {
fun onItemClick(view: View, viewModel: AddressesData)
}
override fun getFilter(): Filter {
return object : Filter() {
private val filterResults = FilterResults()
override fun performFiltering(constraint: CharSequence?): FilterResults {
searchableList.clear()
if (constraint.isNullOrBlank()) {
searchableList.addAll(countries)
} else {
val filterPattern = constraint.toString().toLowerCase().trim { it <= ' ' }
for (item in 0..countries.size) {
if (countries[item].englishName!!.toLowerCase().contains(filterPattern)) {
searchableList.add(countries[item])
}
}}
return filterResults.also {
it.values = searchableList
}
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
if (searchableList.isNullOrEmpty())
onNothingFound?.invoke()
notifyDataSetChanged()
}
and I add the following code in my activity :
search.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
adapter.filter.filter(charSequence.toString())
}
override fun afterTextChanged(editable: Editable) {}
})
The filter didn't work, I would like to know where's the problem in my code and How can I correct it to make the filter work ?

I think you lost getItemCount method in your adapter, it must return actual size of items in adapter.

Its late but may it it will help someone,
You are filtering data in searchableList list but you have applied countries list to adapter.
Change accordingly it will work.

Related

android spinner in recyclerview

RecyclerView is using a spinner.
For example, if you have three items,
The second item is made of TEMP through a spinner. (The default is test.)
EX)
test
temp
test
When the delete button of the first position is pressed,
I want the contents I want to be displayed as below.
test
test
But in reality,
test
temp
I deleted the temp, but why does the temp keep being exposed?
What should I do?
RecycleItemadapter.kt
#SuppressLint("MissingPermission") class RecycleItemAdapter(val calculateWeight: (MutableList<RecycleDropDownData>, Int) -> Unit,
private val itemList: MutableList<RecycleDropDownData>) : RecyclerView.Adapter<RecycleItemAdapter.DevicesAdapterViewHolder>() {
private var recycleItemList = mutableListOf<RecycleDropDownData>()
#SuppressLint("NewApi")
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DevicesAdapterViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_recycler, parent, false)
return DevicesAdapterViewHolder(view)
}
#SuppressLint("RecyclerView", "NotifyDataSetChanged")
override fun onBindViewHolder(holder: DevicesAdapterViewHolder, pos: Int) {
holder.recycleItem.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val weight = recycleItemList[pos].weight
recycleItemList[pos] = itemList[position]
recycleItemList[pos].weight = weight
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
holder.recycleWeight.text = recycleItemList[pos].weight
// TODO
holder.recycleDelete.setOnClickListener {
recycleItemList.removeAt(pos)
notifyDataSetChanged()
}
}
fun setData(recyclerList: MutableList<RecycleDropDownData>) {
this.recycleItemList = recyclerList
}
fun getData(): MutableList<RecycleDropDownData> {
return recycleItemList
}
override fun getItemCount(): Int {
return recycleItemList.size
}
inner class DevicesAdapterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recycleWeight: TextView
private val recycleCalculate: Button
val recycleDelete: Button
val recycleItem: Spinner
init {
recycleItem = itemView.findViewById(R.id.recycle_item_spinner)
recycleItem.adapter = ArrayAdapter(itemView.context, R.layout.item_drop_down, itemList)
recycleWeight = itemView.findViewById(R.id.recycler_item_weight)
recycleCalculate = itemView.findViewById(R.id.recycler_item_weight_calculate)
recycleDelete = itemView.findViewById(R.id.recycler_item_delete)
}
}
}
item_recycler
<Spinner
android:id="#+id/recycle_item_spinner"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/item_drop_down" />
<TextView
android:id="#+id/recycler_item_weight"
android:layout_width="100dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="#id/recycle_item_spinner"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="#+id/recycler_item_weight_calculate"
style="#style/station_detail_item_passenger_button"
android:background="#color/color_mint_code_75f8ff"
android:text="무게"
app:layout_constraintLeft_toRightOf="#id/recycler_item_weight"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="#+id/recycler_item_delete"
style="#style/station_detail_item_passenger_button"
android:text="삭제"
app:layout_constraintLeft_toRightOf="#id/recycler_item_weight"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/recycler_item_weight_calculate" />

room android title is not filled in the list on StartFragment

I get the table and complete the title in the list on the start page, but the text not updated for some reason, what could be the problem?
I have a table:
#Entity(tableName = "Note_table")
class NoteModel (
#PrimaryKey(autoGenerate = true)
var id: Int = 0,
#ColumnInfo
var title: String = "",
#ColumnInfo
var description: String = ""
) : Serializable
I have RecyclerView:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardElevation="5dp"
app:cardCornerRadius="3dp"
android:layout_margin="1dp">
<TextView
android:id="#+id/item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:text="title"
android:textColor="#color/blue"
android:textSize="20sp"
android:textStyle="bold" />
</androidx.cardview.widget.CardView>
and in the class NoteAdapte, i complete in the title (here - binding.itemTitle.text = listNote[position].title), but in the activity I get itemTitle.text = title:
class NoteAdapter: RecyclerView.Adapter<NoteAdapter.NoteViewHolder>() {
lateinit var binding: ItemLayoutBinding
var listNote = emptyList<NoteModel>()
class NoteViewHolder(view: View): RecyclerView.ViewHolder(view) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
binding = ItemLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return NoteViewHolder(view)
}
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
binding.itemTitle.text = listNote[position].title
}
override fun getItemCount(): Int {
return listNote.size
}
#SuppressLint("NotifyDataSetChanged")
fun setList(list: List<NoteModel>){
listNote = list
notifyDataSetChanged()
}
override fun onViewAttachedToWindow(holder: NoteViewHolder) {
super.onViewAttachedToWindow(holder)
holder.itemView.setOnClickListener{
StartFragment.clickNote(listNote[holder.adapterPosition])
}
}
override fun onViewDetachedFromWindow(holder: NoteViewHolder) {
holder.itemView.setOnClickListener(null)
}
}
link to Git: https://github.com/Avdors/RoomLesson.git
enter image description here
You are suppose to have a ItemLayoutBinding per item in your Adapter.
Here you are not.
First change the declaration of the NoteViewHolder class.
class NoteViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding: ItemLayoutBinding
init {
binding = ItemLayoutBinding.bind(view)
}
}
The onCreateViewHolder method become :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return NoteViewHolder(view)
}
And the onBindViewHolder become:
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
holder.binding.itemTitle.text = listNote[position].title
}

Get multiple / dynamic EditText value in RecyclerView

I'm new with these case and I want to try to get the value of dynamic EditText(s) from RecyclerView.
And in the layout :
<?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_marginTop="#dimen/_10sdp"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_question"
style="#style/textH4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="bottom"
android:text="Ini adalah tempat pertanyaan pertama" />
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/et_answer"
android:layout_width="match_parent"
android:layout_height="#dimen/_120sdp"
android:hint="Tulis jawaban anda"
style="#style/EdittextPrimary"
android:gravity="top"
android:background="#drawable/bg_outline_grey"
android:inputType="textMultiLine"
android:layout_marginTop="#dimen/_10sdp"/>
</LinearLayout>
The question is.. how do I get the EditText value and put it in an ArrayList ?
Newest update codes :
I add my Recycler Adapter, and tried these :
class RequestJoinAdapter(
val onTextChanged: (text:String,position:Int)->Unit
) :
RecyclerView.Adapter<RequestJoinAdapter.ViewHolder>() {
var listData: MutableList<KeamananModel> = ArrayList()
private var textValue = ""
fun insertAll(data: List<KeamananModel>) {
data.forEach {
listData.add(it)
notifyItemInserted(listData.size - 1)
}
}
fun clear() {
if (listData.isNotEmpty()) {
listData.clear()
notifyDataSetChanged()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemRequestJoinBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = listData[position]
holder.bindTo(item)
holder.binding.etAnswer.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable?) {
onTextChanged.invoke(s.toString(), position)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textValue = s.toString().toLowerCase()
}
})
}
override fun getItemCount() = listData.size
inner class ViewHolder(val binding: ItemRequestJoinBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindTo(item: KeamananModel) {
val context = binding.root.context
binding.tvQuestion.text = item.qt
}
}
private fun isLog(msg: String) {
Log.e("join grup:", msg)
}
}
and in my current Activity, I used to add some codes like
RequestJoinAdapter { text, position ->
val values = ArrayList<Jawaban>()
val hashSet = HashSet<Jawaban>()
values.add(Jawaban(pertanyaan_no = position+1, jawaban = text))
hashSet.addAll(values)
values.clear()
values.addAll(hashSet)
val valuest = ArrayList<JawabanKeamanan>()
val hashSets = HashSet<JawabanKeamanan>()
valuest.add(JawabanKeamanan(values))
hashSets.addAll(valuest)
valuest.clear()
valuest.addAll(hashSets)
isLog("currentResult: $valuest")
}
How do I set my latest valuest into my var listJawaban: MutableList<JawabanKeamanan> = ArrayList() without any duplicate datas inside it ? What I want is like JawabanKeamanan(jawaban_keamanan=[Jawaban(pertanyaan_no=1, jawaban=t)], [Jawaban(pertanyaan_no=2, jawaban=u)]). Thanks..
The simplest answer is to implement a TextWatcher to your EditText and have it return your data when your text is changed.
Now you may ask how can I get such data in my activty? Well it's pretty simple.
Create an interface in your adapter to communicate with your activity.
Pass your interface as a constructor parameter so when you initialize your adapter in the activity your methods are implemented.
Another solution for you is to add a button in each item of your list and call your interface method in the button's OnClickListener.
EDIT:
In the snippet below I have used a lambda function for when the text changes.
class RequestJoinAdapter(
private val onClickedItem: (ArrayList<String>)->Unit,
val onTextChanged: (text:String,position:Int)->Unit
):
RecyclerView.Adapter<RequestJoinAdapter.ViewHolder>() {
var listData: MutableList<KeamananModel> = ArrayList()
var listJawaban: MutableList<Jawaban> = ArrayList()
private var textValue = ""
fun insertAll(data: List<KeamananModel>) {
data.forEach {
listData.add(it)
notifyItemInserted(listData.size - 1)
}
}
fun clear() {
if (listData.isNotEmpty()) {
listData.clear()
notifyDataSetChanged()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemRequestJoinBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = listData[position]
holder.bindTo(item)
}
override fun getItemCount() = listData.size
inner class ViewHolder(val binding: ItemRequestJoinBinding):
RecyclerView.ViewHolder(binding.root) {
fun bindTo(item: KeamananModel) {
val context = binding.root.context
binding.tvQuestion.text = item.qt
binding.etAnswer.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable?) {
if (textValue.isNotEmpty()) {
isLog(textValue)
onTextChanged.invoke(s,absoluteAdapterPosition)
} else {
}
listJawaban.add(Jawaban(pertanyaan_no = adapterPosition + 1, jawaban = textValue))
isLog(listJawaban.toString())
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textValue = s.toString().toLowerCase()
}
})
}
}
private fun isLog(msg: String) {
Log.e("join grup:", msg)
}
}
Pay attention to the onTextChanged.invoke() method. This lambda function can be used like an interface to communicate between your adapter and your view. It will be triggered every time your text is changed.
Finally instantiate your adapter like below:
val adapter = RequestJoinAdapter({
}, { text, position ->
//onTextChanged
})
The position argument is there to help you know which TextView was changed

Can't show items by sections correctly in recyclerview in kotlin

I am relatively new in kotlin and I would like to know how I can display items by sections, like this: showing data in sections . I have two services, one returns the list of section names and the other returns the items for each section.
So far I did the following, the view is like this:
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:id="#+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="header" />
<androidx.appcompat.widget.SwitchCompat
android:id="#+id/header_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list_productos_seccion"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="#+id/header_switch"
app:layout_constraintStart_toStartOf="#+id/header_title"
app:layout_constraintTop_toBottomOf="#+id/header_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
and in my outer adapter I did the following, where I pass a list of sections to the adapter and for each one I consult the service to obtain items and display it:
class SeccionesAdapter(
var seccionClickAdapter: SeccionClickAdapter,
private val context: Context,
var fragment: Fragment,
var productoViewModel: ProductoViewModel,
var vm: ClientActivityViewModel
): RecyclerView.Adapter<SeccionesAdapter.MyViewHolder>() {
interface SeccionClickAdapter{
fun onSeccionClick(producto: ProductoQryDTO)
}
var dataList = emptyList<SeccionQryDTO>()
var productosLista = ArrayList<ProductoQryDTO>()
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun setData(seccionDTO: SeccionQryDTO) {
itemView.header_title.text = seccionDTO.nombre
itemView.header_switch.isChecked = seccionDTO.isSelected?: true
itemView.list_productos_seccion.layoutManager = LinearLayoutManager(context)
if (itemView.header_switch.isChecked) {
itemView.list_productos_seccion.visibility = View.VISIBLE
dataFromServer(seccionDTO.id, itemView)
}
itemView.header_switch.setOnCheckedChangeListener { _, isChecked -> visible(isChecked, seccionDTO.id) }
}
fun visible(isChecked: Boolean, idSeccionDTO: Int) {
if (isChecked) {
itemView.list_productos_seccion.visibility = View.VISIBLE
dataFromServer(idSeccionDTO, itemView)
} else {
itemView.list_productos_seccion.visibility = View.GONE
}
}
}
fun dataFromServer(idSeccionDTO: Int, itemView: View) {
productosLista.clear()
productoViewModel.getItems(
idSeccionDTO,
{
fragment.activity?.runOnUiThread {
productosLista.clear()
productosLista.addAll(it.data!!)
itemView.list_productos_seccion.adapter = ProductoxSeccionAdapter(context, productosLista, seccionClickAdapter)
}
}, {
Log.e("Error", "", it)
}
)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_header_model, parent, false))
}
override fun getItemCount(): Int {
return dataList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.setData(dataList[position]!!)
}
fun setSecciones(seccionesList : List<SeccionQryDTO>) {
this.dataList = seccionesList
notifyDataSetChanged()
}
}
The problem is that when the application is loaded, sometimes the amount of items that should be for each section is not shown, and when I scroll it begins to make calls to the service, also the elements disappear and it remains blank as shown in this image
blank space in sections.
Could you help me please? I don't know how I could handle this situation or if I should implement it in some other way
EDIT
This is my inner adapter: ProductoxSeccionAdapter
class ProductoxSeccionAdapter (
private val context: Context,
var listProductos: List<ProductoQryDTO>?,
var seccionClickAdapter: SeccionesAdapter.SeccionClickAdapter
) : RecyclerView.Adapter<ProductoxSeccionAdapter.MyViewHolder>() {
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun setData(producto: ProductoQryDTO) {
itemView.nombreProductoLista.text = producto.nombre
itemView.descProductoLista.text = producto.detalle
itemView.precioProductoLista.text = producto.simboloMoneda + producto.precio
itemView.setOnClickListener { seccionClickAdapter.onSeccionClick(producto) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_producto_cliente_lista, parent, false))
}
override fun getItemCount(): Int {
return listProductos!!.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.setData(listProductos!![position])
}
}

How to stop a view in RecyclerView from updating its value when it has focus?

I have a RecyclerView with a set of CardView that contain an EditText in each card. The RecyclerView adapter receives a list of numbers every second and it binds the data to each CardView's EditText.
However, when the user click on one of the EditView I want that EditView to stop changing every second while the EditView has the focus, so the user can enter their own data.
I don't really know what to do if (editView.hasFocus)
Adapter
class ConverterRecyclerViewAdapter(val ratesList: ArrayList<Currency>,
val context: Context?,
val onRateListener: OnRateListener)
: RecyclerView.Adapter<ConverterRecyclerViewAdapter.ViewHolder>() {
class ViewHolder(itemView: View, val onRateListener: OnRateListener) : RecyclerView.ViewHolder(itemView), View.OnClickListener{
var editText: EditText
init{
itemView.setOnClickListener(this)
editText = itemView.findViewById(R.id.value_conv_cv)
}
fun bindItem(currency : Currency){
itemView.currency_name_conv_cv.text = currency.currencyName
itemView.country_currency_name_conv_cv.text = currency.countryCurrencyName
itemView.value_conv_cv.text = SpannableStringBuilder(currency.value.toString())
itemView.flag_iv_conv_cv.setImageResource(currency.flag)
itemView.tag = currency.currencyName
}
override fun onClick(v: View?) {
onRateListener.onRateClick(adapterPosition, itemView.currency_name_conv_cv.text.toString())
}
}
interface OnRateListener {
fun onRateClick(position : Int, currency : String)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.converter_card_layout, parent, false)
return ViewHolder(view, onRateListener)
}
override fun getItemCount(): Int {
return ratesList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItem(ratesList[position])
}
}
CardView.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardElevation="0dp"
app:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:id="#+id/flag_iv_conv_cv"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#mipmap/ic_launcher_round"/>
<TextView
android:id="#+id/currency_name_conv_cv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CURRENCY"
android:textStyle="bold"
android:textSize="16sp"
android:paddingTop="#dimen/list_item_tv_padding"
android:paddingStart="#dimen/list_item_tv_padding"
android:layout_toRightOf="#id/flag_iv_conv_cv"
android:layout_toEndOf="#+id/flag_iv_conv_cv"
android:textColor="#android:color/black"/>
<TextView
android:id="#+id/country_currency_name_conv_cv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/flag_iv_conv_cv"
android:layout_toEndOf="#+id/flag_iv_conv_cv"
android:layout_below="#+id/currency_name_conv_cv"
android:paddingStart="#dimen/list_item_tv_padding"
android:text="Country Currency Name"
android:textSize="12sp"
android:textColor="#android:color/darker_gray"/>
<EditText
android:id="#+id/value_conv_cv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignEnd="#id/country_currency_name_conv_cv"
android:gravity="right"
android:text="100"
android:textSize="24sp" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
Fragment
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this).get(ConverterViewModel::class.java)
viewModel.initialisePositions()
viewModel.setupTimer(viewModel.ratesPositions[0])
val ratesLiveData = viewModel.rates
ratesLiveData.observe(viewLifecycleOwner, Observer { data ->
refreshData(data.rates)
viewModel.sortListByPositionsArray(ratesArray)
val firstElementInRecyclerView = viewModel.ratesPositions[0]
val view = recyclerView.findViewWithTag<View>(firstElementInRecyclerView)
lateinit var editText : EditText
if (converterAdapter == null) setUpRecyclerView()
else {
converterAdapter!!.notifyDataSetChanged()
}
})
}
First of all, you want to update your item when you don't have the focus on it, so indeed you have to use if (!editView.hasFocus()) {}
Then if you have to check if your list ratesArray is holding the same items or not, because if you do ratesArray.clear() at some point, you're clearing the objects then the RecyclerView will create a new ViewHolder for your item.
If you are clearing and creating new objects continuously, you have to "pair" your Adapter with the items.
For example, if you always clear the items and create them again respecting the position, add this method to your Adapter:
override fun getItemId(position: Int): Long {
return position.toLong()
}
you can relate the "rate" in any other ways (a String inside the object, for example). If you do it with the position, you're pairing each item with their order.
Now, to finish "respecting that order", you have to tell the adapter that it has stable ids. That's done with Adapter.setHasStableIds(true)
Maybe like this:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if(!holder.editText.hasFocus()){
holder.bindItem(ratesList[position])
}
}
UPDATE
remove the focusable attribute for edittext and add this to its xml
android:focusableInTouchMode="false"
in the adapter:
//decalare clicked item as member of the class and set it to -1
var clickedItem = -1;
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if(clickedItem != position){
holder.bindItem(ratesList[position])
}
//on click
holder.editText.setOnClickListener {
clickedItem = position;
notifyDataSetChanged();
}
}
Your EditText needs special binding so remove this line from fun bindItem():
itemView.value_conv_cv.text = SpannableStringBuilder(currency.value.toString())
And on onBindViewHolder, bind this view only when it's not focused, which requires listening. Try this:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val current = ratesList[position]
holder.bindItem(current)
holder.value_conv_cv.setOnFocusChangeListener { view, hasFocus ->
if (hasFocus) {
// do nothing
} else {
holder.value_conv_cv.text = SpannableStringBuilder(current.value.toString())
}
}
}

Categories

Resources