Lista con elemento
- avatar
- text1
- text2
y división con secciones
seccion1 elemento 1 elemento 2
seccion2 elemento 3 elemento 4
<?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:id="@+id/drawItem" | |
android:layout_width="match_parent" | |
android:layout_height="72dp" | |
android:background="?attr/selectableItemBackground" | |
android:clickable="true" | |
android:focusable="true"> | |
<androidx.appcompat.widget.AppCompatImageView | |
android:id="@+id/avatar" | |
android:layout_width="40dp" | |
android:layout_height="40dp" | |
android:layout_marginStart="16dp" | |
android:layout_marginTop="8dp" | |
android:layout_marginBottom="8dp" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintStart_toStartOf="parent" | |
app:layout_constraintTop_toTopOf="parent" | |
app:srcCompat="@android:drawable/sym_def_app_icon" /> | |
<TextView | |
android:id="@+id/title" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:layout_marginStart="16dp" | |
android:layout_marginTop="16dp" | |
android:layout_marginEnd="16dp" | |
android:ellipsize="end" | |
android:singleLine="true" | |
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" | |
app:layout_constraintEnd_toEndOf="parent" | |
app:layout_constraintStart_toEndOf="@+id/avatar" | |
app:layout_constraintTop_toTopOf="parent" | |
tools:text="Two-Line item" /> | |
<TextView | |
android:id="@+id/summary" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:ellipsize="end" | |
android:singleLine="true" | |
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" | |
android:textColor="?android:textColorSecondary" | |
app:layout_constraintEnd_toEndOf="@+id/title" | |
app:layout_constraintStart_toStartOf="@+id/title" | |
app:layout_constraintTop_toBottomOf="@+id/title" | |
tools:text="Secondary text" /> | |
</androidx.constraintlayout.widget.ConstraintLayout> |
package app.webserveis.testlist | |
import android.os.Bundle | |
import android.util.Log | |
import android.view.Menu | |
import android.view.MenuItem | |
import android.view.View | |
import android.widget.Toast | |
import androidx.appcompat.app.AppCompatActivity | |
import androidx.recyclerview.widget.LinearLayoutManager | |
import app.webserveis.testlist.models.CustomModel | |
import app.webserveis.testlist.models.MessageModel | |
import app.webserveis.testlist.models.SectionModel | |
import com.google.android.material.snackbar.Snackbar | |
import kotlinx.android.synthetic.main.activity_main.* | |
import kotlinx.android.synthetic.main.content_main.* | |
/* | |
http://worldpopulationreview.com/world-cities/ | |
https://www.suntos.com.np/kotlin-android-sample-projects-with-source-code-in-android-studio/simple-recyclerview-android-example-in-kotlin-source-code.html#update-main-activity | |
https://developer.android.com/guide/topics/ui/layout/recyclerview | |
https://medium.com/@hinchman_amanda/working-with-recyclerview-in-android-kotlin-84a62aef94ec | |
*/ | |
class MainActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
setSupportActionBar(toolbar) | |
fab.setOnClickListener { view -> | |
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) | |
.setAction("Action", null).show() | |
} | |
initRecyclerView() | |
} | |
private fun initRecyclerView() { | |
val mAdapter = MyCustomSectionAdapter(dummyData()) | |
recycler_view.setHasFixedSize(true) | |
recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) | |
recycler_view.adapter = mAdapter | |
mAdapter.setOnItemClickListener(object : MyCustomSectionAdapter.ClickListener { | |
override fun onItemClick(v: View, position: Int) { | |
Log.v(TAG, "onItemClick $position") | |
Toast.makeText( | |
this@MainActivity, | |
"Clicked($position): ${mAdapter.getItem(position)}", | |
Toast.LENGTH_SHORT | |
).show() | |
} | |
}) | |
} | |
override fun onCreateOptionsMenu(menu: Menu): Boolean { | |
menuInflater.inflate(R.menu.menu_main, menu) | |
return true | |
} | |
override fun onOptionsItemSelected(item: MenuItem): Boolean { | |
return when (item.itemId) { | |
R.id.action_settings -> true | |
else -> super.onOptionsItemSelected(item) | |
} | |
} | |
private fun dummyData(): List<Any> { | |
val myList = mutableListOf<Any>() | |
myList.add(SectionModel("Section 1")) | |
myList.add(CustomModel("Application 1 lorem impsum alore noire", "com.application1.app com.application1.app",null)) | |
myList.add(CustomModel("Application 2", "com.application2.app",null)) | |
myList.add(CustomModel("Application 3", "com.application3.app",null)) | |
myList.add(CustomModel("Application 4", "com.application4.app",null)) | |
myList.add(CustomModel("Application 5", "com.application5.app",null)) | |
myList.add(CustomModel("Application 6", "com.application6.app",null)) | |
myList.add(CustomModel("Application 7", "com.application7.app",null)) | |
myList.add(CustomModel("Application 8", "com.application8.app",null)) | |
myList.add(SectionModel("Section 2")) | |
myList.add(CustomModel("Application 9", "com.application9.app",null)) | |
myList.add(CustomModel("Application 10", "com.application10.app",null)) | |
myList.add(CustomModel("Application 11", "com.application11.app",null)) | |
myList.add(CustomModel("Application 12", "com.application12.app",null)) | |
myList.add(CustomModel("Application 13", "com.application13.app",null)) | |
myList.add(CustomModel("Application 14", "com.application14.app",null)) | |
myList.add(CustomModel("Application 15", "com.application15.app",null)) | |
return myList | |
} | |
companion object { | |
val TAG: String = | |
MainActivity::class.java.simpleName | |
} | |
} |
package app.webserveis.testlist | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import android.widget.TextView | |
import androidx.recyclerview.widget.RecyclerView | |
import app.webserveis.testlist.models.CustomModel | |
import app.webserveis.testlist.models.SectionModel | |
/* | |
https://github.com/JakeSteam/StickyHeaders/blob/master/app/src/main/java/uk/co/jakelee/stickyheadersdemo/ContentAdapter.kt | |
https://medium.com/@ivancse.58/android-and-kotlin-recyclerview-with-multiple-view-types-65285a254393 | |
https://www.android4dev.com/how-to-create-recyclerview-with-multiple-view-types/ | |
*/ | |
class MyCustomSectionAdapter(private val mDataSet: List<Any>?) : | |
RecyclerView.Adapter<BaseViewHolder<*>>() { | |
private var clickListener: ClickListener? = null | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) { | |
TYPE_SECTION -> SectionViewHolder( | |
LayoutInflater.from(parent.context) | |
.inflate(R.layout.simple_section_item_1, parent, false) | |
) | |
TYPE_CUSTOM -> MessageViewHolder( | |
LayoutInflater.from(parent.context) | |
.inflate(R.layout.custom_list_item_2, parent, false) | |
) | |
else -> throw IllegalArgumentException("Invalid view type") | |
} | |
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) { | |
val item = mDataSet?.get(position) | |
when (holder) { | |
is SectionViewHolder -> holder.bind(item as SectionModel) | |
is MessageViewHolder -> holder.bind(item as CustomModel) | |
else -> throw IllegalArgumentException() | |
} | |
} | |
override fun getItemCount(): Int { | |
return mDataSet?.size ?: 0 | |
} | |
fun getItem(position: Int): Any? { | |
//return if (mDataSet != null) mDataSet[position] else null | |
return mDataSet?.get(position) | |
} | |
override fun getItemViewType(position: Int): Int = | |
when (mDataSet?.get(position)) { | |
is SectionModel -> TYPE_SECTION | |
is CustomModel -> TYPE_CUSTOM | |
else -> throw IllegalArgumentException("Invalid type of data $position") | |
} | |
fun setOnItemClickListener(clickListener: ClickListener) { | |
this.clickListener = clickListener | |
} | |
inner class SectionViewHolder(itemView: View) : BaseViewHolder<SectionModel>(itemView) { | |
private var title: TextView = itemView.findViewById(android.R.id.text1) as TextView | |
override fun bind(item: SectionModel) { | |
title.text = item.title | |
} | |
} | |
inner class MessageViewHolder(itemView: View) : BaseViewHolder<CustomModel>(itemView), | |
View.OnClickListener { | |
private val text1 = itemView.findViewById(R.id.title) as TextView | |
private val text2 = itemView.findViewById(R.id.summary) as TextView | |
init { | |
if (clickListener != null) { | |
itemView.setOnClickListener(this) | |
} | |
} | |
override fun bind(item: CustomModel) { | |
text1.text = item.title | |
text2.text = item.summary | |
} | |
override fun onClick(v: View?) { | |
if (v != null) { | |
clickListener?.onItemClick(v, adapterPosition) | |
} | |
} | |
} | |
companion object { | |
private const val TYPE_SECTION = 0 | |
private const val TYPE_CUSTOM = 1 | |
} | |
interface ClickListener { | |
fun onItemClick(v: View, position: Int) | |
} | |
} |
<?xml version="1.0" encoding="utf-8"?> | |
<TextView xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:id="@android:id/text1" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
tools:text="Sub header" | |
android:textColor="?android:colorAccent" | |
android:gravity="center_vertical" | |
android:minHeight="?android:attr/listPreferredItemHeightSmall" | |
android:paddingStart="?android:attr/listPreferredItemPaddingStart" | |
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" | |
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" /> |