How can I use setOnItemClickListner in each item in my ListView?
my xml :
<ListView
android:id="#+id/tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
this my adapter class
inner class mo3d1Adapter : BaseAdapter {
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return listOfmo3d.size
}
var listOfMkabala = ArrayList<MeetingDetails>()
var context: Context? = null
constructor(context: Context, listOfMkabaln: ArrayList<MeetingDetails>) : super() {
this.listOfMkabala = listOfMkabaln
this.context = context
}
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
val mo3d = listOfmo3d[p0]
var inflatormo3d = context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
var myViewmo3d = inflatormo3d.inflate(R.layout.fragment_item, null)
lvMo3d.onItemClickListener = AdapterView.OnItemClickListener { adapterView, view, i, l ->
Toast.makeText(context, " TEST STACK ", Toast.LENGTH_LONG).show()
}
myViewmo3d.meeting_name.text = mo3d.name1!!
myViewmo3d.meeting_date.text = mo3d.date.toString()!!
myViewmo3d.attendance_number.text = mo3d.n2.toString()!!
return myViewmo3d
}
override fun getItem(p0: Int): Any {
return listOfmo3d[p0]
}
}
I want listener for each item in my ListView
And when I used this method setOnClickListener in adapter it's not working, where can I use?
Try this in your activity class
lv.setOnItemClickListener { parent, view, position, id ->
Toast.makeText(this, "Position Clicked:"+" "+position,Toast.LENGTH_SHORT).show()
}
Although a little quirky this works fine for me.
latestMessagesAdapter.setOnItemLongClickListener { item, view ->
val row = item as LatestMessageRow
return#setOnItemLongClickListener(true)
}
First of all I would like to tell that it is RecyclerView rather than ListView. You can find plenty information why to do in such. For example you can read it hear :
RecyclerView vs. ListView
Regarding your question how to do it in correct way with RecyclerView.
Insert dependencies with RecyclerView, they are now in support library in Kotlin.
implementation "com.android.support:appcompat-v7:25.4.0"
First change your ListView with RecyclerView in xml layout like this:
<android.support.v7.widget.RecyclerView
android:id="#+id/accountList"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Create Adapter for RecyclerView:
class AccountListAdapter(val accountList: AccountList, val itemListener: (Account) -> Unit) :
RecyclerView.Adapter<AccountListAdapter.ViewHolder>(){
override fun getItemCount(): Int = accountList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
holder.bind(accountList[position])
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder{
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_account, parent, false)
return ViewHolder(view, itemListener)
}
class ViewHolder(itemView: View, val itemClick: (Account) -> Unit): RecyclerView.ViewHolder(itemView){
fun bind(account : Account){
with(account){
itemView.accountName.text = title
itemView.setOnClickListener{ itemClick(this)}
}
}
}
}
item_account.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="wrap_content">
<TextView
android:id="#+id/accountName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Models (in Kotlin you can put them in one file and name for example AccountModels.kt) :
data class AccountList(val accounts: List<Account>){
val size : Int
get() = accounts.size
operator fun get(position: Int) = accounts[position]
}
data class Account(val id : Long, val title : String, val balance : Int, val defCurrency: Int)
In Fragment/Activity connect your Adapter to RecyclerView:
override fun onStart() {
super.onStart()
setupAdapter()
}
fun setupAdapter(){
Log.d(TAG, "updating ui..")
val account1 = Account(1,"Credit", 1000, 2)
val account2 = Account(2, "Debit", 500, 2)
val account3 = Account(3, "Cash", 7000, 2)
val accounts : List<Account> = listOf(account1, account2, account3)
val adapter = AccountListAdapter(AccountList(accounts)){
val title = it.title
Log.d(TAG, "$title clicked")
}
accountList.layoutManager = LinearLayoutManager(activity)
accountList.adapter = adapter
}
That is all. Everything should work now. Hope it helps.
Related
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
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])
}
}
I'm using androidx.recyclerview.widget.RecyclerView to display a list of items, separated by an other item as a "header" with some aggregated values.
When i put only one item in my list without adding the header, everything is ok and the item is displayed correctly. As soon as i add the header item, only the header is displayed and the one single item isn't shown.
When i add two items and the header, the header and one item are displayed. I don't know why the last item of my list is missing altough it exists in the adapters datasource.
My ListAdapter inherits from RecyclerView.Adapter<RecyclerView.ViewHolder> and uses two ViewHolders detected by a viewType property of my list items.
When loading the data, the onBindViewHolder method isn't called for the last item in my list, even tough the item is in the visible section of my screen.
Does anybody has a hint, why this happens?
class ListAdapter(val onClick: (position: Long) -> Unit,
val onLongClick: (Long) -> Unit,
val onShareClick: (id: Long?) -> Unit) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
BindableAdapter<List<ListAdapterItem<*>>> {
var items: List<ListAdapterItem<*>> = emptyList()
private var actionMode: ActionMode? = null
var tracker: SelectionTracker<Long>? = null
init {
setHasStableIds(true)
}
override fun setData(data: List<ListAdapterItem<*>>) {
this.items = data // all items are set correctly here!!
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int {
return if (items.isEmpty()) EMPTY else items[position].viewType
}
override fun getItemCount(): Int {
return if (items.isEmpty()) 1 else items.filter { it.viewType == ITEM }.size
}
override fun getItemId(position: Int): Long = position.toLong()
fun getItem(position: Long): ListViewModel.ListItem = item[position.toInt()].value as ListViewModel.ListItem
fun setActionMode(actionMode: ActionMode?) {
this.actionMode = actionMode
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
EMPTY -> EmptyViewHolder(parent)
HEADER -> HistoryGroupHeaderViewHolder(parent)
else -> HistoryViewHolder(parent)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is HistoryViewHolder) {
val item = items[position].value as ListViewModel.ListItem
tracker?.let {
holder.bind(item, it.isSelected(position.toLong()))
}
holder.itemView.setOnClickListener {
onClick(position.toLong())
}
holder.itemView.findViewById<AppCompatImageView>(R.id.history_item_share)?.setOnClickListener {
onShareClick(item.id)
}
}
else if (holder is HistoryGroupHeaderViewHolder) {
val header = items[position].value as ListViewModel.ListSectionHeader
holder.bind(header)
}
}
class HistoryViewHolder(
private val parent: ViewGroup,
private val binding: at.app.databinding.ViewHistoryListItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_history_list_item,
parent,
false
)
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ListViewModel.ListItem, isActivated: Boolean = false) {
binding.model = item
itemView.isActivated = isActivated
val imageView = itemView.findViewById<AppCompatImageView>(R.id.history_item_image)
if(itemView.isActivated) {
val parameter = imageView?.layoutParams as ConstraintLayout.LayoutParams
parameter.setMargins(
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt()
)
imageView.layoutParams = parameter
} else {
val parameter = imageView?.layoutParams as ConstraintLayout.LayoutParams
parameter.setMargins(0,0,0,0)
imageView.layoutParams = parameter
}
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long? = itemId
}
}
class HistoryGroupHeaderViewHolder(
private val parent: ViewGroup,
private val binding: at.app.databinding.ViewHistoryListGroupHeaderItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_history_list_group_header_item,
parent,
false
)
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ListViewModel.ListSectionHeader) {
binding.model = item
}
}
class EmptyViewHolder(
private val parent: ViewGroup, view: View = LayoutInflater.from(parent.context).inflate(
R.layout.view_history_empty_item,
parent,
false
)
) : RecyclerView.ViewHolder(view)
companion object {
const val EMPTY = 0
const val ITEM = 1
const val HEADER = 2
}
}
class MyItemDetailsLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
private val log = LoggerFactory.getLogger(ListAdapter::class.java)
override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
val view = recyclerView.findChildViewUnder(e.x, e.y)
if (view != null) {
return try {
if(recyclerView.getChildViewHolder(view) is ListAdapter.HistoryViewHolder) {
(recyclerView.getChildViewHolder(view) as ListAdapter.HistoryViewHolder)
.getItemDetails()
} else {
null
}
} catch (ex: Exception) {
log.error("Error on getItemDetails. ", ex)
null
}
}
return null
}
}
data class ListAdapterItem<out T>(val value: T, val viewType: Int)
And this is my layout:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="at.app.ui.viewmodel.ListViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/list_app_bar"
layout="#layout/layout_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/history_recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/transparent"
android:scrollbars="vertical"
app:data="#{viewModel.items}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/list_app_bar" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
When i add two items and the header, the header and one item are
displayed.
problem is in your getItemCount method.
override fun getItemCount(): Int {
return if (items.isEmpty()) 1 else items.filter { it.viewType == ITEM }.size
}
If you want to show 1 header and 2 elements that means that there are must be 3 items in recyclerview, so getItemCount must return 3. But now it looks like getItemCount will return 2, thats why recycerlview doesn't even create third element.
I have implemented a recyclerview within recyclerview. Here is my code
activity_main
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/rv_activity_main"
app:layoutManager="android.support.v7.widget.GridLayoutManager"
app:spanCount="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
MainActivity
class MainActivity : AppCompatActivity() {
private var graduationList = ArrayList<Graduation>()
private var yearList = ArrayList<YearList>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initList()
rv_activity_main.adapter = MainAdapter(graduationList)
}
private fun initList(){
graduationList.clear()
yearList.clear()
yearList.add(YearList("2011"))
yearList.add(YearList("2012"))
yearList.add(YearList("2013"))
yearList.add(YearList("2014"))
yearList.add(YearList("2015"))
yearList.add(YearList("2016"))
yearList.add(YearList("2017"))
yearList.add(YearList("2018"))
yearList.add(YearList("2019"))
graduationList.add(Graduation("UG",yearList))
graduationList.add(Graduation("PG",yearList))
}
}
Graduation Pojo Class
data class Graduation(
var name: String,
var yearList: ArrayList<YearList>
)
YearList Pojo Class
data class YearList(var yearName: String)
MainAdapter
class MainAdapter(private var graduationList: ArrayList<Graduation>): RecyclerView.Adapter<MainAdapter.MainViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): MainViewHolder {
val itemView = LayoutInflater.from(p0.context).inflate(R.layout.cell_main_adapter,p0,false)
return MainViewHolder(itemView)
}
override fun getItemCount(): Int {
return graduationList.size
}
override fun onBindViewHolder(p0: MainViewHolder, p1: Int) {
p0.graduationTextView.text = graduationList[p1].name
p0.graduationRecyclerView.adapter = SecondAdapter(graduationList[p1].yearList)
}
inner class MainViewHolder(view: View): RecyclerView.ViewHolder(view){
var graduationTextView: TextView = view.findViewById(R.id.tv_cell_main_adapter)
var graduationRecyclerView: RecyclerView = view.findViewById(R.id.rv_cell_main_adapter)
}
}
SecondAdapter
class SecondAdapter(private var yearList: ArrayList<YearList>): RecyclerView.Adapter<SecondAdapter.SecondViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): SecondViewHolder {
val itemView = LayoutInflater.from(p0.context).inflate(R.layout.cell_second_adapter,p0,false)
return SecondViewHolder(itemView)
}
override fun getItemCount(): Int {
return yearList.size
}
override fun onBindViewHolder(p0: SecondViewHolder, p1: Int) {
p0.yearTextView.text = yearList[p1].yearName
p0.itemView.setOnClickListener {
Log.i("Pritish",yearList[p1].yearName+"\n")
}
}
inner class SecondViewHolder(view: View): RecyclerView.ViewHolder(view){
var yearTextView: TextView = view.findViewById(R.id.tv_cell_year_title)
}
}
ItemClick for MainAdapter should not be clickable so I have not added any itemClick for it. Suppose user clicks a particular year I know which year the user selected but my question is, how to know when a year is clicked whether it's from UG or PG without implementing a itemclick listener for MainAdapter?
Try this way,
Pass Graduation object in SecondAdapter constuctor from MainAdapter like below.
p0.graduationRecyclerView.adapter = SecondAdapter(graduationList[p1])
Second Adapter
class SecondAdapter(private var graduation: Graduation): RecyclerView.Adapter<SecondAdapter.SecondViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): SecondViewHolder {
val itemView = LayoutInflater.from(p0.context).inflate(R.layout.cell_second_adapter,p0,false)
return SecondViewHolder(itemView)
}
override fun getItemCount(): Int {
return graduation.yearList.size
}
override fun onBindViewHolder(p0: SecondViewHolder, p1: Int) {
p0.yearTextView.text = graduation.yearList[p1].yearName
p0.itemView.setOnClickListener {
Log.i("Pritish",graduation.yearList[p1].yearName+" "+graduation.name+"\n")
}
}
inner class SecondViewHolder(view: View): RecyclerView.ViewHolder(view){
var yearTextView: TextView = view.findViewById(R.id.tv_cell_year_title)
}
}
Our problem is displaying records from a SQLite database on an activity. We are able to write data to the database we verified that the data is written with DBBrowser. The other part of this issue is the RecyclerAdapter. We have spent two days construction the syntax. The program flow is simple one line of EditText is saved then we navigate to the PersonActivity that should display the saved data.
In the PersonActivity we query the database for all records and return a contactList. Our Design is MVC, We will post the code below for the DBHelper and the RecyclerAdapter and the PersonActivity to display the data with the XML files involved. Also the Model Class.
class Contact{
var id: Int = 0
var name: String = ""
}
DBHelper insert data and queryAll DB is created no need for all the code
// Start of CRUD functions
fun insert(values: ContentValues): Long {
val Id = db!!.insert(dbTable, null, values)
return Id
}
fun queryAll(): List<Contact> {
val contactList = ArrayList<Contact>()
val selectQuery = "SELECT * FROM $dbTable"
val cursor = db!!.rawQuery(selectQuery, null)
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
val contact = Contact()
contact.id = Integer.parseInt(cursor.getString(cursor.getColumnIndex(colId)))
contact.name = cursor.getString(cursor.getColumnIndex(colName))
contactList.add(contact)
} while (cursor.moveToNext())
}
}
cursor.close()
return contactList
}
RecyclerAdapter NOTE comments
class PersonRecyclerAdapter(val context:Context, val
items:List<Contact>):RecyclerView.Adapter<RecyclerView.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
RecyclerView.ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.single_card, parent, false))
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
val item = items.get(position)
viewHolder.bindItem(item)
//viewHolder.itemName.text = items[position]
// WHAT IS CORRECT SYNTAX FOR LINE OF CODE ABOVE
}
private fun Any.bindItem(item: Contact) {
// AS Added this fun ?
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val itemName = view.tvName
}
}
PersonActivity that should display the contactList data
class PersonActivity : AppCompatActivity() {
private var rvRecyclerView: RecyclerView? = null
private var contactList:List<Contact> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_person)
val names = contactList
rvRecyclerView?.layoutManager = LinearLayoutManager(this)
rvRecyclerView?.setHasFixedSize(true)
rvRecyclerView?.adapter = PersonRecyclerAdapter(this, names)
callHOME()
}
private fun callHOME(){
val db = DBHelper(this)
contactList = db.queryAll()
}
}
XML files for PersonActivity and the inflated file
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.androidstackoverflow.kotlincontacts.PersonActivity"
tools:showIn="#layout/activity_person">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rvRecylerView">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
Inflated view XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/list_new_card"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="2dp"
android:background="#color/colorAccent"
android:orientation="vertical">
<TextView
android:id="#+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:textColor="#color/color_Black"
android:textSize="20sp"
android:text="#string/app_name"
android:textStyle="bold" />
</LinearLayout>
Please understand we have looked at numerous code variations for this problem!
Before we ask for help. The Questions are two fold
How to show the data on the PersonActivity?
Is the PersonRecyclerAdapter syntax correct?
First of all callHOME() should be called before initializing names variable. That is:
class PersonActivity : AppCompatActivity() {
private var rvRecyclerView: RecyclerView? = null
private var contactList:List<Contact> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_person)
callHOME()
val names = contactList
rvRecyclerView?.layoutManager = LinearLayoutManager(this)
rvRecyclerView?.setHasFixedSize(true)
rvRecyclerView?.adapter = PersonRecyclerAdapter(this, names)
}
private fun callHOME(){
val db = DBHelper(this)
contactList = db.queryAll()
}
You also extended your PersonalRecyclerAdapter class with wrong RecyclerView.Adapter<Recycler.ViewHolder> you have to use RecyclerView.Adapter<MyViewHolder>.
RecyclerView.Adapter with own ViewHolder as argument, not default view holder.
Using extension function here is overkill and unnecessary. Though could be used as follows..
class PersonRecyclerAdapter(val context:Context, val
items:List<Contact>):RecyclerView.Adapter<MyViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
MyViewHolder {
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.single_card,parent, false))
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(viewHolder: MyViewHolder, position:
Int) {
val item = items.get(position)
viewHolder.bindItem(item)
}
private fun MyViewHolder.bindItem(item: Contact) {
itemName.text = item.name
// or use
// itemView.findViewById<TextView>(R.id.tvName).text = item.name
}
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val itemName = view.tvName
}
Instead just use this
class PersonRecyclerAdapter(val context:Context, val
items:List<Contact>):RecyclerView.Adapter<MyViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
MyViewHolder { return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.single_card, parent, false)) }
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(viewHolder: MyViewHolder, position: Int) {
val item = items.get(position)
viewHolder.itemName.text = items[position].name
// or use
// viewHolder.itemView.findViewById<TextView>(R.id.tvName).text = item.name
}
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val itemName = view.tvName
}
First off #kumarshivang made some very good suggestions about how to construct RecyclerAdapter and his best point was not to use the inner class. So I will post code here that uses a much cleaner code design for a RecyclerAdapter. Though even with a better constructed RecyclerAdapter our problem of not seeing the stored data was not solved. Because we knew that the data was in the database logic would tell us two things were wrong either the data had no container to show the view OR the data was not available YEP it was the latter and this one line of code in PersonActivity is what was missing.
listItems = db.queryAll()
Here is the cleaner RecyclerAdapter (OFF to the Refactor FACTORY we GO)
Notice the word "card" the author used a CardView widget to display data in the RecyclerView widget.
class HabitsAdapter(val habits:List<Habit>): RecyclerView.Adapter<HabitsAdapter.HabitViewHolder>() {
class HabitViewHolder(val card:View):RecyclerView.ViewHolder(card)
override fun onBindViewHolder(holder: HabitViewHolder, index: Int) {
if(holder != null){
val habit = habits[index]
holder.card.tvTitle.text = habit.title
holder.card.tvDescription.text = habit.description
holder.card.ivINsingle_card.setImageBitmap(habit.image)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HabitViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.single_card, parent,false)
return HabitViewHolder(view)
}
override fun getItemCount(): Int {
return habits.size
}
}