Whats wrong with the next layouts?
I pretend to include a RecyclerView inside a DialogFragment to list Checkboxes and handle the check.
Issues:
Item text should fill the content of the screen.
On scroll the items arrange to the right position.
dialog.xml:
<LinearLayout 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"
android:orientation="vertical"
android:paddingLeft="#dimen/spacing_large"
android:paddingRight="#dimen/spacing_large"
android:paddingTop="#dimen/spacing_large">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/degrees_picker"
android:textAppearance="#style/TextAppearance.AppCompat.Title" />
<ProgressBar
android:id="#+id/degreesProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="#dimen/spacing_large"
android:indeterminate="true"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
android:id="#+id/degreesRecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="#dimen/spacing_medium"
android:layout_weight="1"
tools:listitem="#layout/degrees_item" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right">
<Button
android:id="#+id/degreesCancelButton"
style="#style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/cancel" />
<Button
android:id="#+id/degreesOkButton"
style="#style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/ok" />
</LinearLayout>
</LinearLayout>
degrees_item.xml (if this just have a CheckBox same thing happens):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:orientation="horizontal">
<TextView
android:id="#+id/degreesItemTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:gravity="center_vertical"
tools:text="Degree tal tal tal tal tal tal tal tal tal tal tal tal tal tal tal tal" />
<CheckBox
android:id="#+id/degreesItemCheck"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="#dimen/spacing_medium" />
</LinearLayout>
adapter:
class DegreesAdapter(val preselectedItems: List<Degree>?) : RecyclerView.Adapter<DegreesAdapter.ViewHolder>(), SubtopicsItemListener {
var items = listOf<Degree>()
set(values) {
field = values.map { CheckableDegree(it, checked = preselectedItems?.contains(it) == true) }
notifyDataSetChanged()
}
val selectedItems get() = items.filter { (it as? CheckableDegree)?.checked == true }
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent?.context)
val contactView = inflater.inflate(R.layout.degrees_item, parent, false)
return ViewHolder(contactView, this)
}
override fun getItemCount() = items.size
override fun getItemId(position: Int): Long {
return items[position].id.toLong()
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
val degree = items[position]
if (degree is CheckableDegree) {
holder?.itemView?.degreesItemCheck?.isChecked = degree.checked
}
holder?.itemView?.degreesItemTextView?.text = degree.description
}
override fun onItemCheck(position: Int, checked: Boolean) {
(items[position] as? CheckableDegree)?.checked = checked
}
class ViewHolder(itemView: View, listener: SubtopicsItemListener) : RecyclerView.ViewHolder(itemView) {
init {
itemView.degreesItemCheck.setOnCheckedChangeListener { _, isChecked ->
listener.onItemCheck(layoutPosition, isChecked)
}
}
}
}
interface SubtopicsItemListener {
fun onItemCheck(position: Int, checked: Boolean)
}
class CheckableDegree(degree: Degree, var checked: Boolean = false)
: Degree(degree.id, degree.description), Parcelable {
companion object {
#JvmField val CREATOR: Parcelable.Creator<CheckableDegree> = object : Parcelable.Creator<CheckableDegree> {
override fun createFromParcel(source: Parcel): CheckableDegree = CheckableDegree(source)
override fun newArray(size: Int): Array<CheckableDegree?> = arrayOfNulls(size)
}
}
constructor(source: Parcel)
: this(source.readParcelable<Degree>(Degree::class.java.classLoader),
1 == source.readInt())
override fun describeContents() = 0
override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags)
dest.writeInt((if (checked) 1 else 0))
}
}
DegreesDialogFragment:
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.medanswers.R
import com.medanswers.api.error.ApiError
import com.medanswers.api.repositories.user.Degree
import com.medanswers.base.MyActivity
import kotlinx.android.synthetic.main.degrees_dialog.*
import javax.inject.Inject
/**
* Show the lists of subtopics
*/
class DegreesDialogFragment : DialogFragment(), DegreesDialogContract.View {
val listener: Listener?
get() = (activity as? Listener) ?: (targetFragment as? Listener)
#Inject lateinit var presenter: DegreesDialogContract.Presenter
companion object {
val extrasPreselectedItems = "preselected_items"
fun newInstance(preselectedItems: List<Degree>?): DegreesDialogFragment {
val frag = DegreesDialogFragment()
frag.arguments = Bundle().apply {
preselectedItems?.let { putParcelableArray(extrasPreselectedItems, it.toTypedArray()) }
}
return frag
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, state: Bundle?): View? {
return inflater?.inflate(R.layout.degrees_dialog, container)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as MyActivity).activityComponent.inject(this)
#Suppress("UNCHECKED_CAST")
val preselectedItems =
(arguments.getParcelableArray(extrasPreselectedItems) as? Array<Degree>)?.toList()
// calls initViews(preselectedItems)
presenter.attach(this, preselectedItems)
}
override fun initViews(preselectedItems: List<Degree>?) {
degreesRecyclerView.apply {
layoutManager = LinearLayoutManager(activity)
adapter = DegreesAdapter(preselectedItems)
}
degreesOkButton.setOnClickListener {
(degreesRecyclerView?.adapter as? DegreesAdapter)?.selectedItems?.let {
listener?.onDegreesDialogOkClick(it)
dismiss()
}
}
degreesCancelButton.setOnClickListener { this#DegreesDialogFragment.dismiss() }
}
override fun showItems(degrees: List<Degree>) {
(degreesRecyclerView?.adapter as? DegreesAdapter)?.items = degrees
}
override fun showProgress() {
degreesProgressBar.visibility = View.VISIBLE
}
override fun dismissProgress() {
degreesProgressBar.visibility = View.GONE
}
override fun close(apiError: ApiError?) {
apiError?.let { listener?.onDegreesDialogError(it) }
dismiss()
}
interface Listener {
fun onDegreesDialogOkClick(degrees: List<Degree>)
fun onDegreesDialogError(apiError: ApiError)
}
}
You need to set up your DialogFragment width from code like below:
WindowManager.LayoutParams p = getDialog().getWindow().getAttributes();
p.width = ViewGroup.LayoutParams.MATCH_PARENT;
p.height = ViewGroup.LayoutParams.MATCH_PARENT;
getDialog().getWindow().setGravity(Gravity.CENTER);
getDialog().getWindow().setAttributes(p);
You can take a look at my EasyFilePickerDialog, and check the file FilePickerDialogFragment.java for more detail.
Related
I have created a recycle view and inside that using card view for items. I have a delete button inside a card view whenever I click on that button my item is deleted from SQLite database. But to reflect it on UI, app need to restart. How can I notify adpater that item is deleted?
activity_main.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"
tools:context=".MainActivity">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="57dp"
android:layout_height="64dp"
android:layout_marginEnd="40dp"
android:layout_marginBottom="40dp"
android:clickable="true"
android:onClick="addNewCredentials"
app:backgroundTint="#270867"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="#android:drawable/ic_menu_add" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" >
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
list_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/urlView"
android:layout_width="300dp"
android:layout_height="30dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:text="url"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="#+id/userNameView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<TextView
android:id="#+id/userNameView"
android:layout_width="300dp"
android:layout_height="25dp"
android:layout_marginBottom="16dp"
android:text="userName"
app:layout_constraintBottom_toTopOf="#+id/passwordView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/passwordView"
android:layout_width="300dp"
android:layout_height="25dp"
android:layout_marginBottom="16dp"
android:text="password"
app:layout_constraintBottom_toTopOf="#+id/noteView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/noteView"
android:layout_width="300dp"
android:layout_height="30dp"
android:layout_marginBottom="16dp"
android:text="note"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/delButton"
android:layout_width="78dp"
android:layout_height="40dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="36dp"
android:background="#E6360F"
android:text="#string/delete_credential_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
MainActivity.kt
package com.example.passwordmanager
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL,false)
val db = DataBaseHandler(this)
val detailsData = db.readCredentials()
val adapter = CredentialAdapter(detailsData,this,{credentialsModel: CredentialsModel->deleteClick(credentialsModel)})
recyclerView.adapter = adapter
}
fun deleteClick(credential: CredentialsModel){
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
//adapter.notifyItemRemoved(position)
Toast.makeText(applicationContext,"Deleted", Toast.LENGTH_SHORT).show()
}
}
fun addNewCredentials(view : View){
print("hello world")
val intent = Intent(this, AddDetailActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
}
}
CredentialAdapter.kt
package com.example.passwordmanager
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.list_item_layout.view.*
class CredentialAdapter(
private val items: List<CredentialsModel>,
ctx: Context, val clickListener: (CredentialsModel) -> Unit
): RecyclerView.Adapter<CredentialAdapter.ViewHolder>() {
var context = ctx
class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
fun bind(credential: CredentialsModel,clickListener: (CredentialsModel) -> Unit){
itemView.urlView.text = credential.url
itemView.userNameView.text = credential.userName
itemView.passwordView.text = credential.password
itemView.noteView.text = credential.note
itemView.delButton.setOnClickListener{clickListener(credential)}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item_layout,parent,false))
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val credential:CredentialsModel = items[position]
holder.bind(credential,clickListener)
}
}
add remove setOnClickListener in your onBindViewHolder.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.remove.setOnClickListener {
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
notifyItemRemoved(holder.getAdapterPosition())
}
}
}
The best way to handle these kinds of situations is to use LiveData.
LiveData is basically an observable class which reads data only when there is a change.
What you can do is create a set function in your adapter like:
internal fun setData(data: List<Data>) {
this.data= dataList //this datalist is a list defined in your adapter
notifyDataSetChanged()
}
now in your main activity/fragment, create a LiveData List outside the onCreate function like this:
private lateinit var allData:LiveData<List<Data>>
Now inside your onCreate function, use can observe the livedata and set the data for recyclerview like this:
allData.observe(this, Observer { data->
data?.let { adapter.setData(it) }
})
You are deleting the item from database but not from the list inside recyclerview adapter.
class CredentialAdapter(
private val items: ArrayList<CredentialsModel>, // Change list to arraylist
ctx: Context, val clickListener: (CredentialsModel, Int) -> Unit
): RecyclerView.Adapter<CredentialAdapter.ViewHolder>() {
...
...
fun remove(position: Int) {
// Remove and notify the adapter to reload
items.removeAt(position)
notifyItemRemoved(position)
}
class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView) {
fun bind(credential: CredentialsModel,clickListener: (CredentialsModel, Int) -> Unit){
...
...
// Pass adapter item position so that we can update the list after delete
itemView.delButton.setOnClickListener{clickListener(credential, adapterPosition)
}
}
...
...
}
Inside MainActivity.kt
fun deleteClick(credential: CredentialsModel, position: Int) {
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
adapter.remove(position)
Toast.makeText(applicationContext,"Deleted", Toast.LENGTH_SHORT).show()
}
}
use ListAdpater
class AdapterMain(var onClickListener: (Int) -> Unit) :
ListAdapter<Note, AdapterMain.NoteViewHolder>(DIFFCALBACK) {
companion object DIFFCALBACK : DiffUtil.ItemCallback<Note>() {
override fun areItemsTheSame(oldItem: Note, newItem: Note): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Note, newItem: Note): Boolean {
return oldItem.title == newItem.title &&
oldItem.description == newItem.description &&
oldItem.priority == newItem.priority
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.note_item, parent, false)
return NoteViewHolder(view)
}
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
holder.txtTitle.text = getItem(position).title
holder.txtDesc.text = getItem(position).description
holder.txtPriority.text = getItem(position).priority.toString()
}
inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var txtTitle: TextView = itemView.txt_title
var txtDesc: TextView = itemView.txt_desc
var txtPriority: TextView = itemView.txt_priority
init {
itemView.setOnClickListener { onClickListener(adapterPosition) }
}
}
fun getNoteAt(position: Int): Note {
return getItem(position)
}
}
you can see complete code of simple NoteApp with kotlin , recyclerView , MVVM and..
class coba : AppCompatActivity() {
private lateinit var recycleView :RecyclerView
private lateinit var datalis :ArrayList
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_coba)
recycleView = findViewById(R.id.rcycoba)
datalis = ArrayList()
val dtnama = arrayOf(
"Danial Sanganus",
"Bijonia Skolin",
"Alianes Pertoli",
"Sivanian Pertici",
"Olehsan alausi"
)
for (i in dtnama.indices){
datalis.add(
dataCoba(
dtnama[i]
)
)
populateData()
}
}
private fun populateData(){
val linearManager = LinearLayoutManager(this)
linearManager.reverseLayout=true
linearManager.stackFromEnd=true
recycleView.layoutManager=linearManager
val adp =adpCoba(this,datalis)
recycleView.adapter=adp
}
}
I want to bind data to my recylerview adapter. This is my current code following the MVVM pattern
Fragment
class NotificationFragment : Fragment() {
var customeProgressDialog: CustomeProgressDialog? = null
private val appPreferences: AppPreference by inject()
private val notificationViewModel: NotificationViewModel by viewModel()
private lateinit var binding: FragmentNotificationBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentNotificationBinding.inflate(inflater, container, false)
return binding.getRoot()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.notification.layoutManager=LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
customeProgressDialog = CustomeProgressDialog(activity)
notificationViewModel.notifications(
appPreferences.getUsername(),
appPreferences.getPassword(),
appPreferences.getUserId()
)
initObservables()
}
private fun initObservables() {
notificationViewModel.progressDialog?.observe(this, Observer {
if (it!!) customeProgressDialog?.show() else customeProgressDialog?.dismiss()
})
notificationViewModel.apiResponse?.observe(
viewLifecycleOwner,
androidx.lifecycle.Observer { response ->
if (response.dataList != null) {
val notificationAdapter = NotificationAdapter(response.dataList as List<Data>)
notificationAdapter.notifyDataSetChanged()
binding.notification.adapter = notificationAdapter
}
})
}
}
View model
class NotificationViewModel(networkCall: NetworkCall) : ViewModel(),
Callback<ApiResponse> {
var progressDialog: SingleLiveEvent<Boolean>? = null
var apiResponse: MutableLiveData<ApiResponse>? = null
var networkCall: NetworkCall;
init {
progressDialog = SingleLiveEvent<Boolean>()
apiResponse = MutableLiveData<ApiResponse>()
this.networkCall = networkCall
}
fun notifications(username: String?, password: String?, userId: String?) {
progressDialog?.value = true
val apiPost = ApiPost()
apiPost.userName = username
apiPost.password = password
apiPost.UserId = userId
apiPost.FileType = NetworkConstant.FILE_TYPE_NOT
networkCall.getPDF(apiPost).enqueue(this)
}
override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
progressDialog?.value = false
}
override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
progressDialog?.value = false
apiResponse?.value = response.body()
}
}
The adapter
class NotificationAdapter(private val list: List<Data>) :
RecyclerView.Adapter<NotificationAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ElementListBinding.inflate(inflater)
// val view = inflater.inflate(R.layout.element_list, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val movie: Data = list[position]
holder.bind(movie)
holder.itemView.setOnClickListener {
if (!TextUtils.isEmpty(movie.filePath)) {
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(movie.filePath))
holder.itemView.context.startActivity(browserIntent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
override fun getItemCount(): Int = list.size
inner class ViewHolder(binding: ElementListBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(movie: Data) {
binding.item = movie
}
}
}
unable to find binding object
the recylerview element list xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="data"
type="com.mountmeru.model.Data" />
</data>
<androidx.cardview.widget.CardView
android:id="#+id/main_cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/main_cardrl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<RelativeLayout
android:id="#+id/rl_newsdate"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.3">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_notifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:maxLines="2"
android:text="#{data.displayName}"
android:textColor="#android:color/black"
android:textSize="16sp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_brief"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_notifi"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_marginRight="10dp"
android:textColor="#android:color/black"
android:textSize="16sp"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_brief"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
android:layout_marginRight="10dp"
android:maxLines="1"
android:text="hey i am date"
android:textColor="#color/inactive_text"
android:textSize="14sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="#+id/rl_newsdate"
android:layout_weight="0.7"
android:padding="5dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_notifi"
android:layout_width="match_parent"
android:layout_height="75dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="#drawable/mer" />
</RelativeLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.cardview.widget.CardView>
</layout>
Can someone confirm me is my implementation of MVVM correct or it needs some refactoring?
How do I make of data binding in my recyclerview list element xml?
You have already used <layout> as parent tag in element_list.xml. Now you can inflate it in the adapter class using DataBinding. See the example below:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ElementListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(Binding)
}
You have to modify your ViewHolder class as well as shown below:
inner class ViewHolder(val binding: ElementListBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(movie: Data) {
with(itemView) {
binding.tvNotifi.text = movie.displayName
binding.tvDate.text = movie.UpdatedDate
if (movie.description != null) {
binding.tvBrief.text = movie.description
binding.tvBrief.visibility = View.VISIBLE
}
}
}
}
I want to bind data on adapter from viewmodel in xml layout file
This is my fragment class.
class NotificationFragment : Fragment() {
var customeProgressDialog: CustomeProgressDialog? = null
private val appPreferences: AppPreference by inject()
private val notificationViewModel: NotificationViewModel by viewModel()
private lateinit var binding: FragmentNotificationBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentNotificationBinding.inflate(inflater, container, false)
return binding.getRoot()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.notification.layoutManager=LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
customeProgressDialog = CustomeProgressDialog(activity)
notificationViewModel.notifications(
appPreferences.getUsername(),
appPreferences.getPassword(),
appPreferences.getUserId()
)
initObservables()
}
private fun initObservables() {
notificationViewModel.progressDialog?.observe(this, Observer {
if (it!!) customeProgressDialog?.show() else customeProgressDialog?.dismiss()
})
notificationViewModel.apiResponse?.observe(
viewLifecycleOwner,
androidx.lifecycle.Observer { response ->
if (response.dataList != null) {
var notificationAdapter = NotificationAdapter(response.dataList as List<Data>)
notificationAdapter.notifyDataSetChanged()
binding.notification.adapter = notificationAdapter
}
})
}
}
My viewmodel
class NotificationViewModel(networkCall: NetworkCall) : ViewModel(),
Callback<ApiResponse> {
var progressDialog: SingleLiveEvent<Boolean>? = null
var apiResponse: MutableLiveData<ApiResponse>? = null
var networkCall: NetworkCall;
init {
progressDialog = SingleLiveEvent<Boolean>()
apiResponse = MutableLiveData<ApiResponse>()
this.networkCall = networkCall
}
fun notifications(username: String?, password: String?, userId: String?) {
progressDialog?.value = true
val apiPost = ApiPost()
apiPost.userName = username
apiPost.password = password
apiPost.UserId = userId
apiPost.FileType = NetworkConstant.FILE_TYPE_NOT
networkCall.getPDF(apiPost).enqueue(this)
}
override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
progressDialog?.value = false
}
override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
progressDialog?.value = false
apiResponse?.value = response.body()
}
}
The adapter class
lass NotificationAdapter(private val list: List<Data>) :
RecyclerView.Adapter<NotificationAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.element_list, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val movie: Data = list[position]
holder.bind(movie)
holder.itemView.setOnClickListener {
if (!TextUtils.isEmpty(movie.filePath)) {
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(movie.filePath))
holder.itemView.context.startActivity(browserIntent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
override fun getItemCount(): Int = list.size
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(movie: Data) {
with(itemView) {
tv_notifi.text = movie.displayName
tv_date.text = movie.UpdatedDate
if (movie.description != null) {
tv_brief.text = movie.description
tv_brief.visibility = View.VISIBLE
}
}
}
}
}
This is my item xml layout
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="com.mountmeru.viewmodel.NotificationViewModel" />
</data>
<androidx.cardview.widget.CardView
android:id="#+id/main_cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/main_cardrl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<RelativeLayout
android:id="#+id/rl_newsdate"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.3">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_notifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:maxLines="2"
android:text="hey i am notification text"
android:textColor="#android:color/black"
android:textSize="16sp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_brief"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_notifi"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_marginRight="10dp"
android:textColor="#android:color/black"
android:textSize="16sp"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_brief"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
android:layout_marginRight="10dp"
android:maxLines="1"
android:text="hey i am date"
android:textColor="#color/inactive_text"
android:textSize="14sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="#+id/rl_newsdate"
android:layout_weight="0.7"
android:padding="5dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_notifi"
android:layout_width="match_parent"
android:layout_height="75dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="#drawable/mer" />
</RelativeLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.cardview.widget.CardView>
</layout>
I was able to do the binding for the fragment but for adapter I am unsure how to proceed.
If I understand your question correctly, you want to pass data to your adapter inside xml. For this, you will need to write custom binding adapter for your RecyclerView.
This link has all you need.
https://android.jlelse.eu/how-to-bind-a-list-of-items-to-a-recyclerview-with-android-data-binding-1bd08b4796b4
I've been trying for a week loading items inside some horizontal recyclerviews contained in one vertical recyclerview(parent). Both need to have endless scrolling the horizontal ones to the right and the vertical while descending.
Right now the endless scrolling and the loading works. However, this is not working correctly because the horizontal ones are loading crazy data from the other children (My guess is that I'm creating the presenters inside the ViewHolder and some how data gets mixed up)
Is there any other way of doing this. That could work better and faster than my approach. Also, some this I can notice the vertical loading is not as smooth as I've seen in some other apps.
Any help would be greatly appreciated
class EventCatalogAdapter(private val presenter: EventCatalogPresenter):
RecyclerView.Adapter<EventCatalogAdapter.ViewHolder>() {
override fun getItemCount() = presenter.getCategoryEventCount()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
EventCatalogAdapter.ViewHolder(parent.inflate(R.layout.item_event_catalog), viewType)
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
presenter.bind(holder, position)
override fun getItemViewType(position: Int) = presenter.getItemType(position)
class ViewHolder(itemView: View, type: Int): RecyclerView.ViewHolder(itemView),
EventCatalogItemView, EventViewList, OnItemClickListener<Event>,
EndlessScrollListener.DataLoader {
private lateinit var companyId: String
private lateinit var catId: String
private val eventPresenter = EventPresenterImpl(this)
override val titleFormat: String? get() = ""
init {
val layoutManager = HorizontalLinearLayoutManager(itemView.context,
LinearLayoutManager.HORIZONTAL, false)
itemView.rvEvents.layoutManager = layoutManager
itemView.rvEvents.addItemDecoration(HorizontalItemDecorator(itemView.context))
itemView.rvEvents.adapter = EventAdapter(eventPresenter, type, this)
itemView.rvEvents.addOnScrollListener(EndlessScrollListener(layoutManager, this))
}
override fun setTitleVisibility(b: Boolean) {
itemView.tvCategoryTitle.visibility = if(!b) View.GONE else View.VISIBLE
}
override fun setCategoryTitle(title: String) {
itemView.tvCategoryTitle.text = title
}
override fun eventsByCategory(companyId: String, categoryId: String) {
this.companyId = companyId
catId = categoryId
eventPresenter.getEventsByCategories(companyId, categoryId)
}
override fun loadMoreData(totalItems: Int) {
Log.d("LOAD", "Event presenter: $eventPresenter for category: $catId")
eventPresenter.getMoreEventsByCategories(companyId, catId, totalItems)
}
override fun setActiveEvent(event: Event?) {}
override fun showSettings() {}
override fun sendMessage(action: String, bundle: Bundle?) {}
override fun showStatus(status: Int) {}
override fun refresh() {
itemView.rvEvents.adapter.notifyDataSetChanged()
}
override fun showMessageTemplate(code: Int) {}
override fun hideMessageTemplate() {
refresh()
}
override fun onItemClick(item: Event) {
}
}
}
class EventAdapter(private val presenter: EventPresenter,
private val listener: OnItemClickListener<Event>):
RecyclerView.Adapter<EventAdapter.ViewHolderItemView>() {
private var type: Int = EVENT_STANDARD
constructor(presenter: EventPresenter, type: Int, listener: OnItemClickListener<Event>) :
this(presenter, listener) {
this.type = type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ViewHolderItemView(parent.inflate(type))
override fun onBindViewHolder(holder: ViewHolderItemView, position: Int) =
presenter.bind(holder, position)
override fun getItemCount() = presenter.getCount()
class ViewHolderItemView(itemView: View) : RecyclerView.ViewHolder(itemView), EventItemView {
override fun setEventDate(date: Date?) {
itemView.tvDate.text = Tools.formatDate(itemView.context, date)
}
override fun setName(name: String) {
itemView.tvName.text = name
}
override fun setScannerVisibility(scannerVisibility: Boolean) {
itemView.ibScanner.visibility = if (scannerVisibility) View.VISIBLE else View.INVISIBLE
}
override fun setEventPoster(posterUrl: String, transformation: Transformation?) {
Tools.loadImage(posterUrl, itemView.ivPoster, transformation, R.mipmap.portrait_test)
}
override fun setTotalRegistrants(totalRegistrants: Long) {
itemView.tvQtyRegs.text = totalRegistrants.toString()
}
override fun addScanAction(event: Event) {
itemView.ibScanner.setOnClickListener {
val auth = FirebaseAuth.getInstance()
val prefs = PreferenceHelper.customPrefs(itemView.context, auth.currentUser!!.uid)
prefs.edit().putString(EventInteractorImpl.FIELD_EVENT_ID, event.eventId).apply()
(itemView.context as SettingsActivity).launchScanner()
}
}
override fun addEventAction(event: Event) {
itemView.setOnClickListener {
(itemView.context as SettingsActivity).showRegistrants(event)
}
}
}
}
// Parent items:
<android.support.constraint.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="220dp">
<TextView
android:id="#+id/tvCategoryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/vertical_spacing"
android:paddingBottom="#dimen/vertical_spacing"
android:layout_marginEnd="#dimen/horizontal_spacing"
android:layout_marginStart="#dimen/horizontal_spacing"
android:textAlignment="center"
android:textSize="18sp"
android:textAllCaps="true"
android:fontFamily="sans-serif-light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="#string/txt_cat_title" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rvEvents"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="#dimen/vertical_spacing"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvCategoryTitle" />
</android.support.constraint.ConstraintLayout>
// Children items
<android.support.constraint.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="110dp"
android:layout_height="match_parent"
android:stateListAnimator="#animator/tile_elevation">
<ImageView
android:id="#+id/ivPoster"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="#string/txt_event_image"
android:src="#mipmap/portrait_test"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tvName"
style="#style/SubTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textIsSelectable="false"
android:textAllCaps="true"
app:layout_constraintBottom_toTopOf="#+id/ibRegistrants"
app:layout_constraintStart_toStartOf="#+id/ibRegistrants"
tools:text="Washington D.C. " />
<TextView
android:id="#+id/tvDate"
style="#style/SubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false"
app:layout_constraintBottom_toTopOf="#+id/tvName"
app:layout_constraintStart_toStartOf="#+id/ibRegistrants"
tools:text="03/23" />
<TextView
android:id="#+id/tvQtyRegs"
style="#style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textIsSelectable="false"
app:layout_constraintBottom_toBottomOf="#+id/ibRegistrants"
app:layout_constraintStart_toEndOf="#+id/ibRegistrants"
app:layout_constraintTop_toTopOf="#+id/ibRegistrants"
tools:text="140" />
<ImageView
android:id="#+id/ibRegistrants"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:contentDescription="#string/txt_registrants"
android:src="#drawable/ic_registrants_48px"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="#+id/ibScanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="#string/txt_registrants"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_scan_action" />
</android.support.constraint.ConstraintLayout>
I'm trying to create a simple recyclerView in Kotlin with data that I get via Volley (which I have confirmed returns the correct data), I keep running to the error E/RecyclerView: No adapter attached; skipping layout when in fact I have specified an adapter with the custom adapter class that I created:
class ImageAdapter(var c: Context, var list: ArrayList<Image>) : RecyclerView.Adapter<ImageAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder? {
val layoutInflater = LayoutInflater.from(parent.context)
return ViewHolder(layoutInflater.inflate(R.layout.image_cardview, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val imageUrl = list[position].url
val submitter = list[position].submitter
val color = list[position].color
holder.submitterTV.text = submitter
holder.card.setCardBackgroundColor(Color.parseColor(color))
}
override fun getItemCount() = list.size
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val card = itemView.card
val submitterTV = itemView.submitter
val imageView = itemView.image
}
}
This is my MainActivty class, where I make the actual call for the JSON and attempt to attach my adapter with my ArrayList that I have created:
class MainActivity : AppCompatActivity() {
val images = ArrayList<Image>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
imageList.layoutManager = LinearLayoutManager(applicationContext)
request("https://api.unsplash.com/photos/curated/?client_id=API_KEY")
imageList.adapter = ImageAdapter(applicationContext, images)
}
private fun request(url: String) {
val queue = Volley.newRequestQueue(this)
val stringRequest = JsonArrayRequest(url, Response.Listener<JSONArray> { response ->
try {
for (i in 0..(response.length() - 1)) {
val image: Image = Image(response.getJSONObject(i).getJSONObject("urls").getString("full"), response.getJSONObject(i).getJSONObject("user").getString("username"), response.getJSONObject(i).getString("color"))
images.add(image)
}
imageList.adapter.notifyDataSetChanged()
} catch (e: JSONException) {
e.printStackTrace()
}
}, Response.ErrorListener { })
queue.add(stringRequest)
}
}
I have created a custom data class Image which stores three fields: the imageUrl, submitter and color, all of which are derived from the JSON. I thought that calling notifyDataSetChanged() on my adapter after the request was completed would allow the recyclerView to get updated and show the items, but nothing at all shows up on the screen. Does anyone have any idea as to where I messed up?
You can download the source code from here(Recyclerview In Kotlin Android)
MainActivity.kt:
package com.deepshikha.recyclerviewkotlin
import android.app.Activity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.view.Window
import android.view.WindowManager
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.activity_main.*
import net.simplifiedcoding.recyclerviewexample.CustomAdapter
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
setContentView(R.layout.activity_main)
val users = ArrayList<Model_Details>()
users.add(Model_Details("Kaju katli", "Kaju katli, also known as kaju Katari or kaju barfi, is an Indian dessert similar to a barfi.",R.drawable.kaju))
users.add(Model_Details("Doughnut", "The doughnut is popular in many countries and prepared in various forms as a sweet snack that can be homemade or purchased in bakeries, supermarkets, food stalls, and franchised specialty outlets",R.drawable.donuts))
users.add(Model_Details("Panna cotta", "Panna cotta is an Italian dessert of sweetened cream thickened with gelatin and molded. The cream may be aromatized with rum, coffee, vanilla, or other flavorings.",R.drawable.panna_cotta))
users.add(Model_Details("Rose Cookies", "Rose cooky is a famous South Indian snack made during festivals",R.drawable.rosecookies))
users.add(Model_Details("Belgian waffle", "In North America, Belgian waffles are a variety of waffle with a lighter batter, larger squares, and deeper pockets than ordinary American waffles",R.drawable.belgianwaffle))
val obj_adapter = CustomAdapter(users)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
recyclerView.adapter = obj_adapter
}
}
CustomAdapter.kt:
package net.simplifiedcoding.recyclerviewexample
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.deepshikha.recyclerviewkotlin.Model_Details
import com.deepshikha.recyclerviewkotlin.R
import kotlinx.android.synthetic.main.adapter_details.view.*
class CustomAdapter(val userList: ArrayList<Model_Details>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
//this method is returning the view for each item in the list
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.adapter_details, parent, false)
return ViewHolder(v)
}
//this method is binding the data on the list
override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) {
holder.bindItems(userList[position])
}
//this method is giving the size of the list
override fun getItemCount(): Int {
return userList.size
}
//the class is hodling the list view
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(user: Model_Details) {
itemView.tv_name.text=user.name
itemView.tv_des.text=user.des
itemView.iv_name.setImageResource(user.image)
}
}
}
Thanks!
Look at following example, I think you this can give you an idea.
You can get the example from here : https://github.com/Siddharha/RecyclerviewTest_Kotlin
Main Activity
class MainActivity : AppCompatActivity() {
private var myAdapter: MyAdapter? = null
private var arrayList: ArrayList<MyItem>? = null
private var layoutManager: RecyclerView.LayoutManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initialize()
setupList()
loaddata()
}
private fun loaddata() {
for (i in 0..9) {
val myItem = MyItem()
myItem.name = "Sid_" + i
myItem.number = "098899876" + i
arrayList!!.add(myItem)
}
myAdapter!!.notifyDataSetChanged()
}
private fun setupList() {
rlItems!!.layoutManager = layoutManager
rlItems!!.adapter = myAdapter
}
private fun initialize() {
arrayList = ArrayList<MyItem>()
layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
myAdapter = MyAdapter(arrayList!!, this, R.layout.item_cell)
}
}
Adapter Class
internal class MyAdapter(private val arrayList: ArrayList<MyItem>,
private val context: Context,
private val layout: Int) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(layout, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: MyAdapter.ViewHolder, position: Int) {
holder.bindItems(arrayList[position])
}
override fun getItemCount(): Int {
return arrayList.size
}
internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(items: MyItem) {
itemView.tvName.text = items.name
itemView.tvNumber.text = items.number
}
}
}
Item Class
internal class MyItem {
var name: String? = null
var number: String? = null
}
try to use Delegate Adapters below is reference link.
Ref Link : https://android.jlelse.eu/keddit-part-4-recyclerview-delegate-adapters-data-classes-with-kotlin-9248f44327f7
I have my RecyclerView Adapter which is as follows,
internal class OptionsAdapter : RecyclerView.Adapter<OptionsAdapter.ViewHolder>() {
private val mOptionList = ArrayList<Option>()
private var mOnItemClickListener: OnItemClickListener<Option>? = null
var data: List<Option>
get() = mOptionList
set(list) {
mOptionList.clear()
mOptionList.addAll(list)
notifyDataSetChanged()
}
fun setOnItemClickListener(listener: OnItemClickListener<Option>) {
mOnItemClickListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_text, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val option = getItem(position)
holder.textViewOption.setText(option.text)
holder.textViewDescription.setText(option.subText)
}
private fun getItem(index: Int): Option {
return mOptionList[index]
}
override fun getItemCount(): Int {
return mOptionList.size
}
internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textViewOption: TextView
var textViewDescription: TextView
var imageViewOptionIcon: ImageView
init {
textViewOption = itemView.findViewById<TextView>(R.id.textview_option)
imageViewOptionIcon = itemView.findViewById<ImageView>(R.id.imageview_option_icon)
textViewDescription = itemView.findViewById<TextView>(R.id.textview_description)
itemView.setOnClickListener {
if (mOnItemClickListener != null) {
mOnItemClickListener!!.onItemClick(getItem(adapterPosition))
}
}
}
}
}
On .MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//rcv is id of Recyclerview
rcv.layoutManager = LinearLayoutManager(this)
rcv.adapter = MyAdpater()
}
Create a new .kt file for creating Adapter
class MyAdpater : RecyclerView.Adapter<ViewHolder>() {
val arr = listOf("A", "B", "C", "D", "E") //static array
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.setText(arr.get(position)) //set text to textview by position
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(TextView(parent.context))
}
override fun getItemCount(): Int {
return arr.count() //return array count
}}
class ViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView){ //ViewHolder with textview}
Recyclerview with different listener for different view
AddressAdapter
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
class AddressAdapter (private val listener: (AddressData, Int,Int) -> Unit):
RecyclerView.Adapter<AddressViewHolder>() {
var lstAddress = emptyList<AddressData>()
fun setData(items: List<AddressData>) {
lstAddress = items
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddressViewHolder {
return AddressViewHolder.create(parent)
}
override fun getItemCount(): Int {
return lstAddress.size
}
override fun onBindViewHolder(holder: AddressViewHolder, position: Int) {
return holder.bind(lstAddress[position], listener, position)
}
fun getData(): List<AddressData> {
return lstAddress
}
}
AddressViewHolder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_address.view.*
class AddressViewHolder(override val containerView: View) :
RecyclerView.ViewHolder(containerView), LayoutContainer {
companion object {
fun create(parent: ViewGroup): AddressViewHolder {
val view =
LayoutInflater.from(parent.context)
.inflate(R.layout.item_address, parent, false)
return AddressViewHolder(view)
}
}
fun bind(
item: AddressData,
listener: (AddressData, Int,Int) -> Unit,
position: Int
) {
item.address.let {
itemView.tvAddress.text = it
}
itemView.tvEdit.setOnClickListener {
listener.invoke(item, position,R.id.tvEdit)
}
itemView.tvDelete.setOnClickListener {
listener.invoke(item, position,R.id.tvDelete)
}
itemView . tvAddress . setOnClickListener {
listener.invoke(item, position, R.id.tvAddress)
}
}
}
item_address
<?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">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvWorkPlace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="#dimen/letter_medium"
android:layout_marginTop="#dimen/letter_medium"
android:gravity="top"
android:text="#string/work_place"
android:textColor="#color/black_color"
android:textSize="#dimen/letter_medium"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvDefault"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginStart="#dimen/spacing_medium"
android:text="#string/txt_default"
android:textColor="#002D56"
android:textSize="#dimen/letter_small"
app:layout_constraintBottom_toBottomOf="#+id/tvWorkPlace"
app:layout_constraintStart_toEndOf="#+id/tvWorkPlace"
app:layout_constraintTop_toTopOf="#+id/tvWorkPlace" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="#dimen/spacing_small"
android:text="76 Playfair Road, #04-06, LHK2, Singapore 367996"
android:textSize="#dimen/letter_small"
app:layout_constraintStart_toStartOf="#+id/tvWorkPlace"
app:layout_constraintTop_toBottomOf="#+id/tvWorkPlace" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="#dimen/spacing_large"
android:text="#string/txt_edit"
android:textAllCaps="true"
android:textColor="#color/colorSecondary"
android:textSize="#dimen/letter_small"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="#+id/tvAddress"
app:layout_constraintTop_toBottomOf="#+id/tvAddress" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginStart="#dimen/spacing_large"
android:text="#string/txt_delete"
android:textAllCaps="true"
android:textColor="#color/colorPrimaryNew"
android:textSize="#dimen/letter_small"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="#+id/tvEdit"
app:layout_constraintTop_toTopOf="#+id/tvEdit" />
<View
android:layout_width="match_parent"
android:layout_height="0.2dp"
android:layout_marginTop="#dimen/spacing_medium"
android:background="#color/divider_color"
app:layout_constraintTop_toBottomOf="#+id/tvDelete" />
</androidx.constraintlayout.widget.ConstraintLayout>
AddressData
class AddressData (
var code: Int,
var address:String
)
In Activity
private fun setUpAdapter() {
adapter = AddressAdapter(fun(item: AddressData, position: Int, type: Int) {
when (type) {
R.id.tvEdit -> {
context.showToast(
"Editbtn" + position,
showInReleaseBuild = true
)
}
R.id.tvDelete -> {
context.showToast(
"Deletebtn" + position,
showInReleaseBuild = true
)
}
R.id.tvAddress -> {
context.showToast(
"addressbtn" + position,
showInReleaseBuild = true
)
}
}
})
rvAddress.adapter = adapter
rvAddress.setEmptyView(llEmptyViewMain)
val addressData = AddressData(1, "abcd")
val addressData1 = AddressData(2, "abceeed")
lstData = listOf(addressData, addressData1)
adapter.setData(lstData)
}
CustomRecycler Xml
<com.example.views.CustomRecyclerView
android:id="#+id/rvAddress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="#dimen/mediumSize"
android:layout_marginTop="#dimen/mediumSize"
android:layout_marginEnd="#dimen/mediumSize"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:list_type="list"
tools:listitem="#layout/item_address" />
<include layout="#layout/layout_recycler_emptyview" />
I have created a nice adapter may be it helped in reducing code..
class ResultActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.question_list_activity)
if (intent != null) {
var results = intent.getParcelableArrayListExtra<Parcelable>("keyResults") as ArrayList<ResultBO>
if (results.size > 0) {
recycler_view.adapter = ResultAdapter(results, object : OnRecyclerItemClickListener {
override fun onItemClicked(view: View?, position: Int) {
/*var intent = Intent(this#SubjectListActivity,McqActivity::class.java)
intent.putExtra("keyTagBO",subjects.get(position))
startActivity(intent)*/
}
});
}
}
}
}
item_result.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/titleTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Dummy Value"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Large" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<TextView
android:id="#+id/option1Tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Dummy"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="#+id/option2Tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Dummy Value"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<TextView
android:id="#+id/option3Tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Dummy"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="#+id/option4Tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Dummy Value"
android:textAlignment="center"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
</LinearLayout>
</LinearLayout>
ResultADapter.class
class ResultAdapter(items: List<ResultBO>, onRecyclerItemClickListener: OnRecyclerItemClickListener) : BaseAdapter<ResultBO, ResultViewHolder>(items, onRecyclerItemClickListener) {
override fun onCreateViewHolder(parent: ViewGroup, pos: Int): ResultViewHolder {
return ResultViewHolder(parent, R.layout.item_result,onRecyclerItemClickListener)
}
}
BaseViewHolder.class
abstract class BaseViewHolder<T : BaseModel>(parent: ViewGroup, #LayoutRes itemLayoutId: Int,
var onRecyclerItemClickListener: OnRecyclerItemClickListener) :
RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(itemLayoutId, parent,
false)), View.OnClickListener {
override fun onClick(v: View?) {
onRecyclerItemClickListener.onItemClicked(v, adapterPosition)
}
abstract fun bindData(data: T)
init {
itemView.setOnClickListener(this)
}
}
BaseAdapter
abstract class BaseAdapter<T : BaseModel, U : BaseViewHolder<T>>
(var items: List<T>, var onRecyclerItemClickListener: OnRecyclerItemClickListener)
: RecyclerView.Adapter<U>() {
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: U, pos: Int) {
holder.bindData(items.get(pos))
}
}
ResultViewHolder.class
class ResultViewHolder(parent: ViewGroup, itemLayoutId: Int, onRecyclerItemClickListener: OnRecyclerItemClickListener) : BaseViewHolder<ResultBO>(parent, itemLayoutId, onRecyclerItemClickListener) {
override fun bindData(data: ResultBO) {
itemView.titleTv.setText(data.questionBO.title)
itemView.option1Tv.setText(data.questionBO.options.get(0))
itemView.option2Tv.setText(data.questionBO.options.get(1))
itemView.option3Tv.setText(data.questionBO.options.get(2))
itemView.option4Tv.setText(data.questionBO.options.get(3))
when(data.orignalAnswer()) {
OptionType.A -> itemView.option1Tv.setBackgroundColor(Color.GREEN)
OptionType.B -> itemView.option2Tv.setBackgroundColor(Color.GREEN)
OptionType.C -> itemView.option3Tv.setBackgroundColor(Color.GREEN)
OptionType.D -> itemView.option4Tv.setBackgroundColor(Color.GREEN)
}
if(!data.isCorrectlyAnswered()){
when(data.selectedOption) {
OptionType.A -> itemView.option1Tv.setBackgroundColor(Color.RED)
OptionType.B -> itemView.option2Tv.setBackgroundColor(Color.RED)
OptionType.C -> itemView.option3Tv.setBackgroundColor(Color.RED)
OptionType.D -> itemView.option4Tv.setBackgroundColor(Color.RED)
}
}
}
}