Create a popup menu with edit/delete functions using recycler view - android

So I want to make a function which opens a popup menu using Recycler view, I've got the card and icons ready but im not sure how to implement into my
StudentAdapter.kt class.
Im new to Kotlin so popup windows are new to me.
I have the code to edit/delete but first of all how do I get the popup menu to work in my recyclerview?
card_items_rec (row for recyclerview)
<?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="wrap_content"
android:background="#color/white"
android:padding="1dp"
android:layout_margin="1dp"
android:weightSum="1">
<TextView
android:id="#+id/tvId"
android:layout_width="wrap_content"
android:layout_height="1dp"
tools:text="Id"
tools:visibility="invisible"
tools:ignore="MissingConstraints" />
<TextView
android:id="#+id/tvEmail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:textAlignment="center"
android:textSize="17dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/tvBuyAmount"
app:layout_constraintHorizontal_bias="0.135"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.533"
tools:text="Date" />
<TextView
android:id="#+id/tvName"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="4dp"
android:textAlignment="center"
android:textSize="17dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/tvBuyAmount"
app:layout_constraintHorizontal_bias="0.94"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Name" />
<TextView
android:id="#+id/tvBuyAmount"
android:layout_width="59dp"
android:layout_height="wrap_content"
android:padding="7dp"
android:textAlignment="center"
android:textSize="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/tvUseAmount"
app:layout_constraintHorizontal_bias="0.885"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="aaa" />
<TextView
android:id="#+id/tvUseAmount"
android:layout_width="49dp"
android:layout_height="wrap_content"
android:layout_marginStart="248dp"
android:padding="7dp"
android:textAlignment="center"
android:textSize="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/deleteBtn"
app:layout_constraintHorizontal_bias="0.269"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="aaa" />
<ImageView
android:id="#+id/popupButton"
android:layout_width="67dp"
android:layout_height="28dp"
android:layout_marginStart="28dp"
android:src="#drawable/ic_menu_more"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="#+id/tvUseAmount"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/deleteBtn"
android:layout_width="67dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="削除"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.982"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
show_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/editText"
android:title="編集"
android:icon="#drawable/ic_edit" />
<item
android:id="#+id/delete"
android:title="削除"
android:icon="#drawable/ic_delete" />
</menu>
Student.adapter.kt
class StudentAdapter: RecyclerView.Adapter<StudentAdapter.StudentViewHolder>() {
private var stdList: ArrayList<StudentModel> = ArrayList()
private var onClickItem: ((StudentModel) -> Unit)?=null
private var onClickDeleteItem: ((StudentModel) -> Unit)?=null
fun addItems(items: ArrayList<StudentModel>){
this.stdList=items
notifyDataSetChanged()
}
fun setOnClickItem(callback:(StudentModel)->Unit){
this.onClickItem = callback
}
fun setOnClickDeleteItem(callback: (StudentModel) -> Unit){
this.onClickDeleteItem = callback
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)= StudentViewHolder (
LayoutInflater.from(parent.context).inflate(R.layout.card_items_rec,parent,false)
)
override fun onBindViewHolder(holder: StudentViewHolder, position: Int) {
val std = stdList[position]
holder.bindView(std)
holder.itemView.setOnClickListener{onClickItem?.invoke(std)}
holder.btnDelete.setOnClickListener{onClickDeleteItem?.invoke(std)}
}
override fun getItemCount(): Int {
return stdList.size
}
class StudentViewHolder(var view: View):RecyclerView.ViewHolder(view){
var id:TextView = view.findViewById<TextView>(R.id.tvId)
var name:TextView = view.findViewById<TextView>(R.id.tvName)
var email:TextView = view.findViewById<TextView>(R.id.tvEmail)
var buyAmount:TextView = view.findViewById<TextView>(R.id.tvBuyAmount)
var useAmount:TextView = view.findViewById<TextView>(R.id.tvUseAmount)
var btnDelete:Button = view.findViewById<Button>(R.id.deleteBtn)
var popupButton: ImageView = view.findViewById(R.id.popupButton)
fun bindView(std:StudentModel){
id.text = std.id.toString()
name.text = std.name
email.text = std.email
buyAmount.text = std.buyamount.toString()
useAmount.text = std.useamount.toString()
}
}
}
allRecordPage.kt
class allRecordpage : AppCompatActivity() {
private lateinit var edName: EditText
private lateinit var edEmail: Button
private lateinit var edBuyAmount: EditText
private lateinit var edUseAmount: EditText
private lateinit var edReason: EditText
private lateinit var btnAdd: Button
private lateinit var btnView: Button
private lateinit var btnUpdate: Button
private lateinit var sqLiteHelper: SQLiteHelper
private lateinit var recyclerView: RecyclerView
private var adapter: StudentAdapter?=null
private var std:StudentModel?=null
val dataHolder = "DataHolder"
#RequiresApi(Build.VERSION_CODES.N)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_all_recordpage)
val textView: TextView =findViewById(R.id.DateText)
val simpleDateFormat= SimpleDateFormat("yyyy/MM/dd\n HH:mm", Locale.getDefault()).format(
Date()
)
val currentDateAndTime: String = simpleDateFormat.format(Date())
textView.text = currentDateAndTime
val button = findViewById<Button>(R.id.goBackToHome)
button.setOnClickListener{
val intent = Intent(this,MainMenu::class.java)
startActivity(intent)
}
initView()
initRecyclerView()
sqLiteHelper= SQLiteHelper(this)
//Show recycler view
val stdList = sqLiteHelper.getAllStudent()
Log.e("おっけ","${stdList.size}")
adapter?.setOnClickItem {
//購入・使用編集画面に遷移
if(it.buyamount > 0){
val intent = Intent(this,buyDetailsScreen::class.java)
intent.putExtra("date",it.email)
intent.putExtra("name", it.name)
intent.putExtra("buyAmount", it.buyamount)
startActivity(intent)
}else if (it.useamount > 0){
val intent = Intent(this,useDetailsScreen::class.java)
intent.putExtra("date",it.email)
intent.putExtra("name",it.name)
intent.putExtra("useAmount",it.useamount)
intent.putExtra("reason",it.reason)
startActivity(intent)
}else{
Toast.makeText(this,"エラーが発生しました",Toast.LENGTH_LONG).show()
}
}
adapter?.addItems(stdList)
adapter?.setOnClickDeleteItem{
deleteStudent(it.id)
}
}
private fun deleteStudent(id:Int){
val builder = AlertDialog.Builder(this)
builder.setMessage("データを削除してよろしいですか")
builder.setCancelable(true)
builder.setNegativeButton("いいえ"){dialog, _ ->
dialog.dismiss()
}
builder.setPositiveButton("はい"){dialog, _ ->
sqLiteHelper.deleteStudentById(id)
getStudents()
dialog.dismiss()
}
val alert = builder.create()
alert.show()
}
private fun updateStudent(){
val name = edName.text.toString()
val email = edEmail.text.toString()
val buyAmount = edBuyAmount.text.toString().toInt()
val useAmount = edUseAmount.text.toString().toInt()
val reason = edReason.text.toString()
//Check record not changed
if(name == std?.name && email == std?.email && buyAmount == std?.buyamount && useAmount == std?.useamount && reason == std?.reason){
Toast.makeText(this,"データが変更されてない", Toast.LENGTH_SHORT).show()
return
}
if(std == null) return
val std = StudentModel(id=std!!.id,name = name,email = email, buyamount = buyAmount, useamount = useAmount, reason = reason)
val status = sqLiteHelper.updateStudent(std)
if(status > -1){
clearEditText()
getStudents()
}else{
Toast.makeText(this,"更新失敗した", Toast.LENGTH_SHORT).show()
}
}
private fun getStudents(){
val stdList = sqLiteHelper.getAllStudent()
Log.e("おっけ","${stdList.size}")
adapter?.addItems(stdList)
}
private fun addStudent(){
val name = edName.text.toString()
val email = edEmail.text.toString()
val buyAmount = edBuyAmount.text.toString().toInt()
val useAmount = edUseAmount.text.toString().toInt()
val reason = edReason.text.toString()
if(name.isEmpty()||email.isEmpty()|| buyAmount.toString().isEmpty() ||useAmount.toString().isEmpty() || reason.toString().isEmpty()){
Toast.makeText(this,"データを入力してください", Toast.LENGTH_SHORT).show()
}else{
val std = StudentModel(name = name, email=email, buyamount=buyAmount, useamount=useAmount, reason = reason)
val status = sqLiteHelper.insertStudent(std)
//Check Insert success or not success
if(status > -2){
Toast.makeText(this,"データを追加しました。", Toast.LENGTH_SHORT).show()
clearEditText()
}else{
Toast.makeText(this,"データが保存されてないようです。", Toast.LENGTH_SHORT).show()
}
}
}
private fun clearEditText(){
edName.setText("")
edEmail.text = ""
edBuyAmount.setText("")
edUseAmount.setText("")
edReason.setText("")
edName.requestFocus()
}
private fun initRecyclerView(){
recyclerView.layoutManager=LinearLayoutManager(this)
adapter = StudentAdapter()
recyclerView.adapter=adapter
}
private fun initView(){
recyclerView=findViewById(R.id.recyclerView)
}
}
I have the code to edit/delete but first of all how do I get the popup menu to work in my recyclerview?

Related

Communication between ViewPager's host Fragment and ViewPager's Fragment item

I have a Fragment(InsiraDocumentosFragment) that has a viewpager and in that fragment I initialize an adapter(InsiraDocumentoAdapter) to initialize the viewpager.
The adapter(InsiraDocumentoAdapter) initializes generic fragments (DocumentoFragment) that I update dynamically, I can now update the image that is shown inside it, but I need to get the click on that image to either change it or to remove it, but this needs to be done in the Fragment that contains the viewpager, because is in this fragment(InsiraDocumentosFragment) that I have the viewmodel that communicates with the database.
How can I watch from InsiraDocumentosFragment the click on the imageview of DocumentFragment ?
Here are my code samples.
class InsiraDocumentosFragment : Fragment() {
private lateinit var _view: View
private lateinit var _imgBack: ImageView
private lateinit var _viewpager: ViewPager
private lateinit var _imgCamera: ImageView
private lateinit var _footerTitle: TextView
private lateinit var _titles: List<String>
private lateinit var _files: MutableList<File?>
private lateinit var _viewModel: DocumentoViewModel
private lateinit var _viewModelProposta: PropostaViewModel
private lateinit var _viewModelArquivo: ArquivoViewModel
private lateinit var _adapter: InsiraDocumentoAdapter
private lateinit var _proposta: PropostaEntity
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_insira_documento, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_view = view
_imgBack = _view.findViewById(R.id.imgBack)
_viewpager = _view.findViewById(R.id.viewpager)
_imgCamera = _view.findViewById(R.id.cameraDocumento)
_footerTitle = _view.findViewById(R.id.footerTitle)
_files = mutableListOf(null, null, null)
_titles = listOf(getString(R.string.identidade), getString(R.string.comprovante_renda), getString(R.string.foto))
_adapter = InsiraDocumentoAdapter(_titles, _files, childFragmentManager)
_viewpager.adapter = _adapter
_viewModel = ViewModelProvider(
this,
DocumentoViewModel.DocumentoViewModelFactory(DocumentoRepositorio(AppDatabase.getDatabase(_view.context).documentoDao()))
).get(DocumentoViewModel::class.java)
_viewModelProposta = ViewModelProvider(
this,
PropostaViewModel.PropostaViewModelFactory(PropostaRepositorio(AppDatabase.getDatabase(_view.context).propostaDao()))
).get(PropostaViewModel::class.java)
_viewModelArquivo = ViewModelProvider(
this,
ArquivoViewModel.ArquivoViewModelFactory(ArquivoRepositorio(AppDatabase.getDatabase(_view.context).arquivoDao()))
).get(ArquivoViewModel::class.java)
addViewPagerListener()
onClickImgBack()
onClickImgCamera()
onClickFooterTitle()
val propostaStr = arguments?.getString(PROPOSTA)
if (!propostaStr.isNullOrBlank()) {
_proposta = jsonToObjProposta(propostaStr)
}
}
private fun addViewPagerListener() {
_viewpager.addOnPageChangeListener(object : OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
if (position == _viewpager.adapter!!.count - 1) {
_footerTitle.text = getString(R.string.concluir)
} else {
_footerTitle.text = getString(R.string.proximo)
}
updateViewPagerLayout(position)
}
})
}
private fun updateViewPagerLayout(currentPage: Int) {
when (currentPage) {
0 -> {
habilitaIndicadorPagina(_view.findViewById(R.id.primeiraPagina), _view.findViewById(R.id.imgPrimeiraPagina), R.drawable.primeira_pagina_selecionada)
desabilitaIndicadorPagina(_view.findViewById(R.id.segundaPagina), _view.findViewById(R.id.imgSegundaPagina), R.drawable.segunda_pagina_nao_selecionada)
desabilitaIndicadorPagina(_view.findViewById(R.id.terceiraPagina), _view.findViewById(R.id.imgTerceiraPagina), R.drawable.terceira_pagina_nao_selecionada)
}
1 -> {
desabilitaIndicadorPagina(_view.findViewById(R.id.primeiraPagina), _view.findViewById(R.id.imgPrimeiraPagina), R.drawable.primeira_pagina_nao_selecionada)
habilitaIndicadorPagina(_view.findViewById(R.id.segundaPagina), _view.findViewById(R.id.imgSegundaPagina), R.drawable.segunda_pagina_selecionada)
desabilitaIndicadorPagina(_view.findViewById(R.id.terceiraPagina), _view.findViewById(R.id.imgTerceiraPagina), R.drawable.terceira_pagina_nao_selecionada)
}
2 -> {
desabilitaIndicadorPagina(_view.findViewById(R.id.primeiraPagina), _view.findViewById(R.id.imgPrimeiraPagina), R.drawable.primeira_pagina_nao_selecionada)
desabilitaIndicadorPagina(_view.findViewById(R.id.segundaPagina), _view.findViewById(R.id.imgSegundaPagina), R.drawable.segunda_pagina_nao_selecionada)
habilitaIndicadorPagina(_view.findViewById(R.id.terceiraPagina), _view.findViewById(R.id.imgTerceiraPagina), R.drawable.terceira_pagina_selecionada)
}
}
}
private fun desabilitaIndicadorPagina(layout: ConstraintLayout, image: ImageView, imageDrawable: Int) {
layout.setBackgroundResource(R.drawable.not_selected_indicator)
image.setBackgroundResource(imageDrawable)
}
private fun habilitaIndicadorPagina(layout: ConstraintLayout, image: ImageView, imageDrawable: Int, ) {
layout.setBackgroundResource(R.drawable.indicador_selecionado)
image.setBackgroundResource(imageDrawable)
}
private fun onClickFooterTitle() {
_footerTitle.setOnClickListener {
if (_viewpager.currentItem == _viewpager.adapter!!.count - 1) {
var success = true
for (i in 0..2) {
if (_files[i] == null || _files[i]?.path.isNullOrEmpty()) {
Toast.makeText(_view.context, R.string.favor_preencher_todas_as_fotos, Toast.LENGTH_LONG).show()
_viewpager.currentItem = i
success = false
break
}
}
if (success) {
_viewModelProposta.addProposta(_proposta.numeroProposta, _proposta.valorPropasta, _proposta.clienteId).observe(viewLifecycleOwner, { idProposta ->
_proposta.id = idProposta
_viewModel.addDocumento("Nome do Documento", _proposta.id).observe(viewLifecycleOwner, { idDocumento ->
val arquivos = mutableListOf<ArquivoEntity>()
for (i in 0..2) {
arquivos.add(ArquivoEntity(0, _files[i]!!.path, _titles[i], idDocumento))
}
_viewModelArquivo.addArquivos(arquivos).observe(viewLifecycleOwner, {
_view.findNavController().popBackStack(R.id.clienteFragment, false)
})
})
})
}
} else {
_viewpager.currentItem = _viewpager.currentItem + 1
}
}
}
private fun onClickImgBack() {
_imgBack.setOnClickListener {
_view.findNavController().navigateUp()
}
}
private fun onClickImgCamera() {
_imgCamera.setOnClickListener {
val intent = Intent(_view.context, ScannerActivity::class.java)
startActivityForResult(intent, REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val mediaList = ScannerActivity.getOutputDirectory(_view.context).listFiles()?.toMutableList() ?: mutableListOf()
if (mediaList.size > 0) {
val documentoFile = mediaList[mediaList.size - 1]
_files.removeAt(_viewpager.currentItem)
_files.add(_viewpager.currentItem, documentoFile)
_adapter.notifyDataSetChanged()
}
}
}
private fun jsonToObjProposta(json: String): PropostaEntity {
val gson = Gson()
val arrayTutorialType = object : TypeToken<PropostaEntity>() {}.type
return gson.fromJson(json, arrayTutorialType)
}
override fun onResume() {
super.onResume()
_view = requireView()
}
companion object {
const val REQUEST_CODE = 1
const val IMAGE = "IMAGE"
const val ID_PROPOSTA = "ID_PROPOSTA"
const val PROPOSTA = "PROPOSTA"
}
}
class InsiraDocumentoAdapter(private val titles: List<String>, private var files: List<File?>, manager: FragmentManager): FragmentStatePagerAdapter(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getCount() = 3
override fun getItem(position: Int): Fragment {
return DocumentoFragment.newInstance(titles[position], files[position])
}
override fun getItemPosition(`object`: Any): Int {
return POSITION_NONE
}
}
class DocumentoFragment : Fragment() {
private lateinit var _view: View
private lateinit var _documento: ImageView
private lateinit var _documentoShape: ImageView
private lateinit var _txtTitle: TextView
private var _file: File? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_documento, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_view = view
}
private fun setUpView() {
_documento = _view.findViewById(R.id.documento)
_documentoShape = _view.findViewById(R.id.documentoShape)
_txtTitle = _view.findViewById(R.id.txtTitle)
_txtTitle.text = arguments?.getString(TITLE)
if (!arguments?.getString(FILE).isNullOrEmpty()) {
_file = File(arguments?.getString(FILE))
}
onImgClick()
insereImagem()
}
private fun onImgClick() {
_documento.setOnClickListener {
if (_documentoShape.visibility == View.GONE && _file != null) {
val intent = Intent(_view.context, FullScreenActivity::class.java)
intent.putExtra(InsiraDocumentosFragment.IMAGE, _file?.path)
intent.putExtra(TITLE, _txtTitle.text)
startActivity(intent)
}
}
}
private fun insereImagem() {
if (_file != null) {
Picasso.get().load(_file!!).into(_view.findViewById<ImageView>(R.id.documento))
_view.findViewById<ImageView>(R.id.documentoShape)?.visibility = View.GONE
} else {
_view.findViewById<ImageView>(R.id.documentoShape)?.visibility = View.VISIBLE
}
}
override fun onResume() {
super.onResume()
setUpView()
}
companion object {
fun newInstance(title: String, file: File?): DocumentoFragment {
val fragmentFirst = DocumentoFragment()
val args = bundleOf(
TITLE to title,
FILE to file?.path
)
fragmentFirst.arguments = args
return fragmentFirst
}
const val TITLE = "TITLE"
const val FILE = "FILE"
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".documento.view.DocumentoFragment">
<TextView
android:id="#+id/txtTitle"
android:layout_width="wrap_content"
android:layout_height="#dimen/dimen_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="Identidade"
android:textColor="#015669"
android:textSize="#dimen/dimen_20sp"
/>
<ImageView
android:id="#+id/documento"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#ffffff"
android:src="#drawable/rectangle_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/txtTitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:scaleType="fitXY"
android:layout_marginTop="#dimen/dimen_20dp"
android:contentDescription="#string/foto_documento"/>
<ImageView
android:id="#+id/documentoShape"
android:layout_width="88dp"
android:layout_height="101dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:src="#drawable/document_shape"
android:contentDescription="#string/foto_documento"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".documento.view.InsiraDocumentosFragment"
android:background="#f0f0f0">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="#dimen/dimen_56dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#drawable/rectangle_blue_toolbar">
<ImageView
android:id="#+id/imgBack"
android:layout_width="#dimen/dimen_12dp"
android:layout_height="#dimen/dimen_20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="#dimen/dimen_20dp"
android:layout_marginTop="18dp"
android:src="#drawable/tint_color"
android:contentDescription="#string/voltar"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="#string/insira_os_documentos"
android:textColor="#ffffff"
android:textSize="#dimen/dimen_20sp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/pagerIndicator"
android:layout_width="wrap_content"
android:layout_height="#dimen/dimen_30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/toolbar"
android:layout_marginTop="#dimen/dimen_20dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/primeiraPagina"
android:layout_width="#dimen/dimen_30dp"
android:layout_height="#dimen/dimen_30dp"
android:background="#drawable/indicador_selecionado"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/segundaPagina"
android:layout_marginHorizontal="#dimen/dimen_10dp"
>
<ImageView
android:id="#+id/imgPrimeiraPagina"
android:layout_width="#dimen/dimen_9dp"
android:layout_height="#dimen/dimen_18dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="#drawable/primeira_pagina_selecionada"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/segundaPagina"
android:layout_width="#dimen/dimen_30dp"
android:layout_height="#dimen/dimen_30dp"
android:background="#drawable/not_selected_indicator"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="#id/primeiraPagina"
android:layout_marginHorizontal="#dimen/dimen_10dp"
>
<ImageView
android:id="#+id/imgSegundaPagina"
android:layout_width="#dimen/dimen_9dp"
android:layout_height="#dimen/dimen_18dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="#drawable/segunda_pagina_nao_selecionada"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/terceiraPagina"
android:layout_width="#dimen/dimen_30dp"
android:layout_height="#dimen/dimen_30dp"
android:background="#drawable/not_selected_indicator"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="#id/segundaPagina"
android:layout_marginHorizontal="#dimen/dimen_10dp"
>
<ImageView
android:id="#+id/imgTerceiraPagina"
android:layout_width="#dimen/dimen_9dp"
android:layout_height="#dimen/dimen_18dp"
android:layout_gravity="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="#drawable/terceira_pagina"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewpager"
android:layout_width="#dimen/dimen_280dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/pagerIndicator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#id/footer"
android:layout_marginHorizontal="#dimen/dimen_40dp"
android:layout_marginTop="#dimen/dimen_20dp"
android:layout_marginBottom="#dimen/dimen_20dp"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/footer"
android:layout_width="match_parent"
android:layout_height="#dimen/dimen_69dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="#drawable/footer">
<ImageView
android:id="#+id/imgDocumento"
android:layout_width="#dimen/dimen_26dp"
android:layout_height="#dimen/dimen_30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="#dimen/dimen_20dp"
android:layout_marginVertical="#dimen/dimen_20dp"
android:src="#drawable/combined_shape"
android:backgroundTint="#0179b0"
android:contentDescription="#string/formato_documento"
/>
<TextView
android:id="#+id/footerTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#id/imgDocumento"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="#+id/cameraDocumento"
app:layout_constraintBottom_toBottomOf="parent"
android:textSize="#dimen/dimen_20sp"
android:textColor="#0179b0"
android:gravity="center"
android:text="#string/proximo"/>
<ImageView
android:id="#+id/cameraDocumento"
android:layout_width="#dimen/dimen_33dp"
android:layout_height="#dimen/dimen_30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="#dimen/dimen_20dp"
android:layout_marginVertical="#dimen/dimen_20dp"
android:src="#drawable/camera_shape"
android:backgroundTint="#0179b0"
android:contentDescription="#string/capturar_foto"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
I need to get the click on that image to either change it or to remove it, but this needs to be done in the Fragment that contains the viewpager, because is in this fragment(InsiraDocumentosFragment) that I have the viewmodel that communicates with the database.
You have a couple of options to do that:
First one:
Make the ViewModel shared between both fragments (InsiraDocumentosFragment & DocumentoFragment) by instantiating it in both fragments with the activity as the owner using requireActivity() that hosts both fragments:
val viewModel =
ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
And then no need to access the parent fragment for changing the image; because now you've an instance of the ViewModel in the DocumentoFragment.
Second one:
Access the InsiraDocumentosFragment from the DocumentoFragment using requireParentFragment(), but first create a method in parent that you want to call from the page fragment to do your needed job.
Assuming the method that you created in InsiraDocumentosFragment is someMethodInParent()
In DocumentoFragment:
val parentFragment = requireParentFragment() as InsiraDocumentosFragment
parentFragment.someMethodInParent()

how to binding correctly recyclerview items in kotlin?

I am developing a news app and I am following MVVM with data binding in recycler view I am trying to bind items but I am just stuck below my recyclerview items xml file
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="article"
type="yodgorbek.komilov.musobaqayangiliklari.model.Article">
</variable>
</data>
<androidx.cardview.widget.CardView 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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp">
<ImageView
android:text="#{article.urlToImage}"
android:id="#+id/imageView"
android:layout_width="100dp"
android:layout_height="85dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:contentDescription="bbc"
tools:background="#color/colorPrimary" />
<TextView
android:id="#+id/articleTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="#id/imageView"
android:layout_toRightOf="#id/imageView"
android:ellipsize="end"
android:lines="3"
android:maxLines="3"
android:text="#{article.title}" />
<ImageView
android:id="#+id/imageCategory"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_toEndOf="#id/imageView"
android:layout_toRightOf="#id/imageView"
android:src="#drawable/ic_espn"
tools:background="#color/colorPrimary" />
<TextView
android:id="#+id/articleSourceName"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_toEndOf="#id/imageCategory"
android:layout_toRightOf="#id/imageCategory"
android:gravity="center|start"
android:text="#{article.source.name}" />
<TextView
android:id="#+id/articleTime"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="#id/articleSourceName"
android:layout_toRightOf="#id/articleSourceName"
android:gravity="center|start"
android:text="#{article.publishedAt}"
android:textColor="#android:color/darker_gray"
tools:ignore="NotSibling" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</layout>
below TopHeadlinesAdapter.kt where I am trying to implement data binding logic
#Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
class TopHeadlinesAdapter(val context: Context, private val article: List<Article>) :
RecyclerView.Adapter<TopHeadlinesAdapter.MyViewHolder>() {
private var articleList: List<Article> by Delegates.observable(emptyList()) { _, _, _ ->
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater =
LayoutInflater.from(parent.context)//.inflate(R.layout.news_list, parent, false)
// val binding = Article.inflate(inflater)
return MyViewHolder(article, parent)
}
override fun getItemCount(): Int {
return articleList.size
}
#SuppressLint("NewApi")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) =
holder.bind(articleList[position])
//holder.articleTitle.text = articleList.get(position).title
//holder.articleSourceName.text = articleList.get(position).source.name
//Picasso.get().load(articleList.get(position).urlToImage).into(holder.image)
// val input = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")
// val output = SimpleDateFormat("dd/MM/yyyy")
// var d = Date()
// try {
// d = input.parse(articleList[5].publishedAt)
// } catch (e: ParseException) {
// try {
// val fallback = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
// fallback.timeZone = TimeZone.getTimeZone("UTC")
// d = fallback.parse(articleList[5].publishedAt)
// } catch (e2: ParseException) {
// // TODO handle error
// val formatted = output.format(d)
// val timelinePoint = LocalDateTime.parse(formatted)
// val now = LocalDateTime.now()
//
// var elapsedTime = Duration.between(timelinePoint, now)
//
// println(timelinePoint)
// println(now)
// elapsedTime.toMinutes()
//
// holder.articleTime.text = "${elapsedTime.toMinutes()}"
//
//
fun updateData(newList: List<Article>) {
articleList = newList
Log.e("articleListSize", articleList?.size.toString())
}
inner class MyViewHolder(private val binding: Article) : RecyclerView.ViewHolder(binding.root) {
fun bind(article: Article) {
binding.article = article
// val image: ImageView = itemView!!.findViewById(R.id.imageView)
// val articleTitle: TextView = itemView!!.findViewById(R.id.articleTitle)
// val articleSourceName: TextView = itemView!!.findViewById(R.id.articleSourceName)
// val imageCategory: ImageView = itemView!!.findViewById(R.id.imageCategory)
// val articleTime: TextView = itemView!!.findViewById(R.id.articleTime)
}
}
}
below my Article.kt data class
data class Article(
val author: String,
val content: String,
val description: String,
val publishedAt: String,
val source: Source,
val title: String,
val url: String,
val urlToImage: String
)
below fragment_top_headlines.xml where I am hosting recyclerview
<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="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="#+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
below TopHeadlinesFragment.kt
class TopHeadlinesFragment : Fragment() {
private val viewModel by viewModel<MainViewModel>()
private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
// private val newsRepository: NewsRepository by inject()
private lateinit var article:List<Article>
//3
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(
R.layout.fragment_top_headlines
, container, false
)
val recyclerView = view.findViewById(R.id.recyclerView) as RecyclerView
val pb = view.findViewById(R.id.pb) as ProgressBar
topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context, article)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()
return view
}
private fun initViewModel() {
viewModel?.sportList?.observe(this, Observer { newList ->
topHeadlinesAdapter.updateData(newList)
})
viewModel?.showLoading?.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
viewModel?.showError?.observe(this, Observer { showError ->
(showError)
})
viewModel?.loadNews()
}
}
I want to know what I have to do in order to bind correctly recyclerview items and show correctly in my app?
try this in
class yourAdapterName(val context: Context, var listData:
MutableList<Article>) :
RecyclerView.Adapter<LanguageAdapter.ViewHolder>() {
lateinit var bindind: YourBinding
fun onRefresh(listData: MutableList<Article>) {
this.listData = listData
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
ViewHolder {
bindind = YourBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bindind)
}
override fun getItemCount(): Int {
return listData.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.setData(listData[position])
}
inner class ViewHolder(private val binding: LangugaeItemBinding) : RecyclerView.ViewHolder(binding.getRoot()) {
fun setData(model: Article) {
with(binding) {
artical = model
executePendingBindings()
}
}
}
}
and call these into onViewCreated in fragment
val recyclerView = view.findViewById(R.id.recyclerView) as
RecyclerView
val pb = view.findViewById(R.id.pb) as ProgressBar
topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context,
article)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()

refresh data in a Firestore Paging Adapter using Swipe Refresh Layout

I am using FirestorePagingAdapter to show data in my app but when I use Swipe Refresh Layout did not work with FirestorePagingAdapter, swipe refresh layout.isVisible = true in LOAD_MORE when I don't scroll
how to work with a Swipe Refresh Layout in FirestorePagingAdapter?
ViewHolde.class:
class ViewHolde(item: View) : RecyclerView.ViewHolder(item){
val username = item.findViewById(R.id.iduser) as TextView
val title = item.findViewById(R.id.idtitle) as TextView
val decription = item.findViewById(R.id.iddes) as TextView
}
User.class:
class User {
lateinit var userphoto: StorageReference
lateinit var username:String
lateinit var title:String
lateinit var notes: String
lateinit var id: String
var isSelected:Boolean = false
constructor(){
}
constructor( id: String?,username:String?,title: String?, notes: String?) {
this.title= title!!
this.id= id!!
this.username= username!!
this.notes= notes!!
}
}
RecyclerActivity.class:
class RecyclerActivity : AppCompatActivity(),SwipeRefreshLayout.OnRefreshListener {
var mylist=ArrayList<User>()
var rv:RecyclerView? = null
var currentuser = FirebaseAuth.getInstance().currentUser!!.uid
val str= FirebaseStorage.getInstance().reference
private var mAuthStateListener: FirebaseAuth.AuthStateListener? = null
var db = FirebaseFirestore.getInstance();
private val notebookRef2 = db.collection("ProfileData")
private val mQuery = notebookRef2.orderBy("title", Query.Direction.ASCENDING)
val config = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(2)
.setPageSize(6)
.build()
val options = FirestorePagingOptions.Builder<User>()
.setLifecycleOwner(this)
.setQuery(mQuery, config, User::class.java)
.build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler)
rec()
var swipeContainer = findViewById(R.id.swipeContainer)as SwipeRefreshLayout
swipeContainer.setOnRefreshListener(this);
swipeContainer = findViewById(R.id.swipeContainer)as SwipeRefreshLayout
swipeContainer.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
swipeContainer.setOnRefreshListener {
rv?.invalidate();
swipeContainer.setRefreshing(false);
}
}
override fun onRefresh() {
Handler().postDelayed( Runnable() {
swipeContainer.setRefreshing(false);
}, 3000);
}
fun rec(){
rv=findViewById(R.id.idrecycler) as? RecyclerView
rv!!.setHasFixedSize(true)
rv!!.layoutManager = LinearLayoutManager(this)
db.collection("ProfileData").orderBy("title", Query.Direction.DESCENDING)
.addSnapshotListener { querySnapshot, firebaseFirestoreException ->
if (querySnapshot != null) {
for(da in querySnapshot) {
val note = da.toObject(Note::class.java)
var title = note.title
var description = note.notes
var username = note!!.username
var id = note!!.id
mylist.add(User(id!!,username!!, title!!, description!!))
}
}
}
setupadapter()
}
fun setupadapter(){
rv=findViewById(R.id.idrecycler) as? RecyclerView
var mAdapter = object : FirestorePagingAdapter<User, ViewHolde>(options) {
override fun onLoadingStateChanged(state: LoadingState) {
swipeRefreshLayout2.isVisible = false
when (state) {
LoadingState.LOADING_INITIAL -> {
swipeRefreshLayout2.isVisible = true
}
LoadingState.LOADING_MORE -> {
swipeRefreshLayout.isVisible = true
}
LoadingState.LOADED -> {
swipeRefreshLayout.isVisible = false
}
LoadingState.ERROR -> {
swipeRefreshLayout.isVisible = false
}
LoadingState.FINISHED -> {
swipeRefreshLayout.isVisible = false
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolde {
var v = LayoutInflater.from(parent.context)
.inflate(R.layout.iktem_list, parent, false)
return ViewHolde(v)
}
override fun onBindViewHolder(holder: ViewHolde, position: Int, p2: User) {
holder.username.text = mylist.get(position).username
holder.title.text = mylist.get(position).title
holder.decription.text = mylist.get(position).notes
}
}
rv!!.adapter = mAdapter
}
}
ActivityRecycler.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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"
android:id="#+id/iddrawer2"
tools:context=".RecyclerActivity">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_height="match_parent"
android:layout_width="match_parent" >
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#4C00CF"
android:id="#+id/idtoolbar2"
tools:ignore="MissingConstraints"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swipeContainer"
android:layout_width="match_parent"
android:layout_marginTop="50dp"
android:layout_height="460dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/idrecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="#+id/swipeRefreshLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="340dp"
android:visibility="gone"
/>
<ProgressBar
android:id="#+id/swipeRefreshLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="450dp"
android:visibility="gone"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:headerLayout="#layout/header"
android:id="#+id/idnavigatuonv2"
android:layout_gravity="start"
app:menu="#menu/draw_menu"/>
</androidx.drawerlayout.widget.DrawerLayout >
So I'm using the adapter to show transaction history and I have a button to show the filtered transaction history which has status == success.
On button click, I'm updating the adapter the following way:
baseQuery = firestore.collection("transactions")
.whereEqualTo("status", "success")
.orderBy("__name__", Query.Direction.DESCENDING);
options = new FirestorePagingOptions.Builder<ItemTxn>()
.setLifecycleOwner(this)
.setQuery(baseQuery, config, ItemTxn.class)
.build();
adapter.updateOptions(options);
https://github.com/firebase/FirebaseUI-Android/issues/1726#issuecomment-573033035

Optimize RecycleView for Chat Android

I have RecycleView to show Chat from local database use Room and it works fine but when I try to scroll fast it does not respond for a couple seconds.
I already try to make simple logic onBindViewHolder but still the same. its because I use View.GONE and View.Visible in time group? how can I make this faster when scroll and I load data from local with limit 100.
this my Adapter using SortedList
class ContentAdapter(private val userUid: String, private val mListener: (code: Int, data: Content)->Unit): ExperimentalAdapter<Content, ContentAdapter.Holder>() {
private var lastDateTime: DateTime = DateFactory().getDateTimeDefaultTimezone()
private var lastPosition = 0
init {
mData = SortedListAsync(
Content::class.java,
object : SortedListAsyncAdapterCallback<Content>(this) {
override fun areContentsTheSame(oldItem: Content?, newItem: Content?): Boolean {
if(oldItem!!.dateTime == null){
oldItem.calculateDateTime()
}
if(newItem!!.dateTime == null){
newItem.calculateDateTime()
}
return oldItem.content == newItem.content &&
oldItem.status == newItem.status &&
oldItem.createdAt == newItem.createdAt
}
override fun areItemsTheSame(item1: Content?, item2: Content?): Boolean {
if(item1!!.dateTime == null){
item1.calculateDateTime()
}
if(item2!!.dateTime == null){
item2.calculateDateTime()
}
return item1.uid == item2.uid
}
override fun compare(o1: Content?, o2: Content?): Int {
if(o1!!.dateTime == null){
o1.calculateDateTime()
}
if(o2!!.dateTime == null){
o2.calculateDateTime()
}
return o2.createdAt.toDateTime().millis
.compareTo(o1.createdAt.toDateTime().millis)
}
})
setHasStableIds(true)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val timeExec = measureNanoTime {
var item: Content = mData.get(position)
var isShowTime = false
var timeString = ""
var itemBefore: Content? = null
if (position != 0) {
itemBefore = mData.get(position - 1)
}
if (itemBefore != null) {
val nowDate = item.dateTime!!
val beforeDate = itemBefore.dateTime!!
if(item.beforeDateTime == null || item.beforeDateTime != beforeDate){
d("do checking time")
if (!DateFactory().isStillInOneDay(nowDate, beforeDate)) {
when {
DateFactory().isStillInOneDay(lastDateTime, beforeDate) -> {
isShowTime = true
timeString = "Today"
}
DateFactory().isYesterday(lastDateTime, beforeDate) -> {
isShowTime = true
timeString = "Yesterday"
}
DateFactory().isTheDayBeforeYesterday(lastDateTime, beforeDate) -> {
isShowTime = true
timeString = "${beforeDate.dayOfWeek().asShortText}, ${beforeDate.dayOfMonth().get()}/${beforeDate.monthOfYear().get()}/${beforeDate.year().get()}"
}
else -> {
isShowTime = true
timeString = "${beforeDate.dayOfWeek().asShortText}, ${beforeDate.dayOfMonth().get()}/${beforeDate.monthOfYear().get()}/${beforeDate.year().get()}"
}
}
}
mData.get(position).apply {
this.timeString = timeString
this.isShowTime = isShowTime
this.beforeDateTime = beforeDate
}
item = mData.get(position)
}else{
d("Skip checking time")
}
lastPosition = position
}
holder.bind(item, item.isShowTime, item.timeString)
}
d("Time for exec ${mData.get(position).content} is $timeExec")
}
override fun getItemViewType(position: Int): Int {
val type = mData.get(position)
if(type.type == GlobalConfig.CONTENT_TYPE_IMAGE){
return if (type.createdBy == userUid){
10
}else{
11
}
}else if(type.type == GlobalConfig.CONTENT_TYPE_TEXT){
return if (type.createdBy == userUid){
20
}else{
22
}
}
return 99
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
d("inflate calling")
when(viewType){
10 -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_image_mine, parent, false)
return HolderImageMine(view, mListener)
}
11 -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_image_other, parent, false)
return HolderImageOther(view, mListener)
}
20 -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_text_mine, parent, false)
return HolderTextMine(view, mListener)
}
22 -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_text_other, parent, false)
return HolderTextOther(view, mListener)
}
else -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_item_chat_text_mine, parent, false)
return HolderTextMine(view, mListener)
}
}
}
abstract class Holder(v: View): RecyclerView.ViewHolder(v){
private val mTimeLastRead: TextView = v.findViewById(R.id.victTVLastRead)
private val mTimeLastIndex: TextView = v.findViewById(R.id.victTVTimeIndex)
private val mHolderView: ConstraintLayout = v.findViewById(R.id.victRoot) ?: v.findViewById(R.id.include)
open fun bind(data: Content, isShowTime: Boolean, timeString: String){
if (mTimeLastRead.visibility != View.GONE) mTimeLastRead.visibility = View.GONE
if(isShowTime){
if (mTimeLastIndex.visibility != View.VISIBLE) mTimeLastIndex.visibility = View.VISIBLE
if(mTimeLastIndex.text != timeString) mTimeLastIndex.text = timeString
}else{
if(mTimeLastIndex.visibility != View.GONE) mTimeLastIndex.visibility = View.GONE
if(mTimeLastIndex.text != timeString) mTimeLastIndex.text = timeString
}
if(mTimeLastRead.visibility == View.GONE && mTimeLastIndex.visibility == View.GONE){
if(mHolderView.visibility != View.GONE) mHolderView.visibility = View.GONE
}else{
if(mHolderView.visibility != View.VISIBLE) mHolderView.visibility = View.VISIBLE
}
}
}
class HolderTextMine(v: View,private val l: (code: Int, data: Content)->Unit): Holder(v){
private val mTimeText: TextView = v.findViewById(R.id.viChatTextMineTime)
private val mReadText: TextView = v.findViewById(R.id.viChatTextMineRead)
private val mContentText: TextView = v.findViewById(R.id.viChatTextMineContent)
override fun bind(data: Content, isShowTime: Boolean, timeString: String) {
super.bind(data, isShowTime, timeString)
mTimeText.text = data.timeContentString
mContentText.text = data.content
mReadText.text = data.statusText
itemView.apply {
setOnClickListener {
l(ON_CLICK, data)
}
}
}
}
class HolderTextOther(v: View,private val l: (code: Int, data: Content)->Unit): Holder(v){
private val mTimeText: TextView = v.findViewById(R.id.viChatTextTime)
private val mContentText: TextView = v.findViewById(R.id.viChatTextContent)
override fun bind(data: Content, isShowTime: Boolean, timeString: String) {
super.bind(data, isShowTime, timeString)
mTimeText.text = data.timeContentString
mContentText.text = data.content
itemView.apply {
setOnClickListener {
l(ON_CLICK, data)
}
}
}
}
class HolderImageMine(v: View,private val l: (code: Int, data: Content)->Unit): Holder(v){
private val mTimeText: TextView = v.findViewById(R.id.viChatImageTime)
private val mReadText: TextView = v.findViewById(R.id.viChatImageRead)
private val mImageView: ImageView = v.findViewById(R.id.viChatImageView)
override fun bind(data: Content, isShowTime: Boolean, timeString: String) {
super.bind(data, isShowTime, timeString)
mTimeText.text = data.timeContentString
if(data.status != 0) {
val link = MediaApi().createUrlPictureContent(data.content)
val token = SharedUtils(itemView.context).getToken()!!
Glide.with(itemView).load(Connection.headerUrl(link, token))
.thumbnail(0.1f).apply(
RequestOptions().dontAnimate().dontTransform().diskCacheStrategy(DiskCacheStrategy.ALL).override(18, 18)
).into(mImageView)
}else{
Glide.with(itemView).load(data.content).thumbnail(0.1f).apply(
RequestOptions().dontAnimate().dontTransform().diskCacheStrategy(DiskCacheStrategy.ALL).override(18, 18)
).into(mImageView)
}
mReadText.text = data.statusText
itemView.apply {
setOnClickListener {
l(ON_CLICK, data)
}
}
}
}
class HolderImageOther(v: View,private val l: (code: Int, data: Content)->Unit): Holder(v){
private val mTimeText: TextView = v.findViewById(R.id.viChatImageOtherTime)
private val mImageView: ImageView = v.findViewById(R.id.viChatImageOtherView)
override fun bind(data: Content, isShowTime: Boolean, timeString: String) {
super.bind(data, isShowTime, timeString)
mTimeText.text = data.timeContentString
val link = MediaApi().createUrlPictureContent(data.content)
val token = SharedUtils(itemView.context).getToken()!!
Glide.with(itemView).load(Connection.headerUrl(link, token)).thumbnail(0.1f).apply(
RequestOptions().dontAnimate().dontTransform().diskCacheStrategy(DiskCacheStrategy.ALL).override(18, 18)
).into(mImageView)
itemView.apply {
setOnClickListener {
l(ON_CLICK, data)
}
}
}
}
companion object {
const val ON_CLICK = 1
}
}
and this my RecycleView in Fragment
mRecycleView.apply {
layoutManager = object :LinearLayoutManager(context, RecyclerView.VERTICAL, true){
override fun supportsPredictiveItemAnimations(): Boolean {
return false
}
}
this.layoutManager?.isItemPrefetchEnabled = true
adapter = ContentAdapter(SharedUtils(context!!).getUid()!!){ code, data ->
when(code){
ContentAdapter.ON_CLICK -> {
if(data.type == GlobalConfig.CONTENT_TYPE_IMAGE){
val bundle = bundleOf(
Pair("image", data.content)
)
view?.findNavController()?.navigate(R.id.action_chatFragment_to_viewImageFragment, bundle)
}
}
}
}
clearOnScrollListeners()
addOnScrollListener(object :EndlessRecyclerOnScrollListener(layoutManager as LinearLayoutManager) {
override fun onLoadMore(current_page: Int, totalItem: Int) {
rLaunch {
mLoading = true
withContext(Dispatchers.Main) {
viewModel.isLoading.value = true
}
viewModel.loadContent(totalItem)
}
d("Load content Chat!")
}
})
itemAnimator = null
setOnTouchListener { view, motionEvent ->
this#ChatFragment.activity?.hideKeyboardFrom(mTextContent)
return#setOnTouchListener false }
}
this is my XML for show image message
<?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/viChatRootImageMine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="#+id/viChatRoomImageCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:cardBackgroundColor="#dbe8ff"
app:cardCornerRadius="10dp"
app:cardElevation="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/viChatRootImageLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/viChatImageView"
android:layout_width="100dp"
android:layout_height="150dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/viChatImageTime"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#color/white" />
<TextView
android:id="#+id/viChatImageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp"
android:text="07:00"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/viChatImageRead" />
<TextView
android:id="#+id/viChatImageRead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:text="R"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<include
android:id="#+id/include"
layout="#layout/view_item_chat_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/space4" />
<Space
android:id="#+id/space4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/viChatRoomImageCard" />
</androidx.constraintlayout.widget.ConstraintLayout>
and this for show text
<?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/viChatTextMineRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent">
<androidx.cardview.widget.CardView
android:id="#+id/viChatTextMineCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:padding="5dp"
app:cardBackgroundColor="#dbe8ff"
app:cardCornerRadius="10dp"
app:cardElevation="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/viChatTextMineLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/viChatTextMineTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp"
android:text="07:00"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/viChatTextMineRead" />
<TextView
android:id="#+id/viChatTextMineRead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="R"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="#+id/viChatTextMineTime"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="#+id/viChatTextMineContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:scrollbarStyle="outsideOverlay"
android:text="Rofie Sagara"
android:textColor="#color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/viChatTextMineTime"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedWidth="true" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="22dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<include
android:id="#+id/include"
layout="#layout/view_item_chat_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/space" />
<Space
android:id="#+id/space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/viChatTextMineCard" />
</androidx.constraintlayout.widget.ConstraintLayout>
Thanks
Thanks #FarshidABZ
Change ConstraintLayout to Linear make more fast in RecycleView
see this article

Kotlin - custom dialog in Android

I want to create a custom dialog in Kotlin. I looked through questions on this theme on Stack Overflow, but I could not find any useful information. How can I do it?
You can use below code for a custom Dialog. It's my working code.
private fun showDialog(title: String) {
val dialog = Dialog(activity)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setCancelable(false)
dialog.setContentView(R.layout.custom_layout)
val body = dialog.findViewById(R.id.body) as TextView
body.text = title
val yesBtn = dialog.findViewById(R.id.yesBtn) as Button
val noBtn = dialog.findViewById(R.id.noBtn) as Button
yesBtn.setOnClickListener {
dialog.dismiss()
}
noBtn.setOnClickListener {
dialog.dismiss()
}
dialog.show()
}
custom_dialog.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/fitsdk_white_rectangle"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="30dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="30dp"
android:orientation="vertical">
<TextView
android:id="#+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/error_timeout_title"
android:textColor="#color/black" />
<TextView
android:id="#+id/tvBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="#string/error_timeout_body"
android:textColor="#color/black" />
<Button
android:id="#+id/btn_yes"
android:layout_width="100dp"
android:layout_height="30dp"
android:background="#android:color/white"
android:clickable="true"
android:text="Yes"
android:textColor="#5DBCD2"
android:textStyle="bold" />
</LinearLayout>
</FrameLayout>
CustomDialogClass.kt
class CustomDialogClass(context: Context) : Dialog(context) {
init {
setCancelable(false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.custom_dialog)
}
}
Below my solution as a kind of "message box".
I have not implemented an "OK" button. The message box should close after clicking on it.
Here the layout element (*/layout/message_box.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:orientation="vertical">
<TextView
android:id="#+id/message_box_header"
android:layout_width="match_parent"
android:layout_height="30dp"
android:textAlignment="center"
android:textSize="20sp" />
<TextView
android:id="#+id/message_box_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textSize="20sp" />
</LinearLayout>
</LinearLayout>
This function I've implement in a Fragment class. It is written in Kotlin.
fun showMessageBox(text: String){
//Inflate the dialog as custom view
val messageBoxView = LayoutInflater.from(activity).inflate(R.layout.message_box, null)
//AlertDialogBuilder
val messageBoxBuilder = AlertDialog.Builder(activity).setView(messageBoxView)
//setting text values
messageBoxView.message_box_header.text = "This is message header"
messageBoxView.message_box_content.text = "This is message content"
//show dialog
val messageBoxInstance = messageBoxBuilder.show()
//set Listener
messageBoxView.setOnClickListener(){
//close dialog
messageBoxInstance.dismiss()
}
}
you have a clean codes on an extension function of context in kotlin and use it on
all of your codes
fun Context.showDialog(
title: String,
description: String,
titleOfPositiveButton: String? = null,
titleOfNegativeButton: String? = null,
positiveButtonFunction: (() -> Unit)? = null,
negativeButtonFunction: (() -> Unit)? = null
) {
val dialog = Dialog(this, R.style.Theme_Dialog)
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) // if you have blue line on top of your dialog, you need use this code
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.setCancelable(false)
dialog.setContentView(R.layout.dialog_custom_layout)
val dialogTitle = dialog.findViewById(R.id.title) as TextView
val dialogDescription = dialog.findViewById(R.id.description) as TextView
val dialogPositiveButton = dialog.findViewById(R.id.positiveButton) as TextView
val dialogNegativeButton = dialog.findViewById(R.id.negativeButton) as TextView
dialogTitle.text = title
dialogDescription.text = description
titleOfPositiveButton?.let { dialogPositiveButton.text = it } ?: dialogPositiveButton.makeGone()
titleOfNegativeButton?.let { dialogNegativeButton.text = it } ?: dialogNegativeButton.makeGone()
dialogPositiveButton.setOnClickListener {
positiveButtonFunction?.invoke()
dialog.dismiss()
}
dialogNegativeButton.setOnClickListener {
negativeButtonFunction?.invoke()
dialog.dismiss()
}
dialog.show()
}
and this is a sample of use this function
requireContext().showDialog(
title = "Your Title",
description = "Your Description",
titleOfPositiveButton = "yes",
titleOfNegativeButton = "No",
positiveButtonFunction = { // Run codes after click on positive button },
negativeButtonFunction = { // Run codes after click on negative button }
)
and you need to have style for constant with on design of your dialog
<style name="Theme_Dialog" parent="android:Theme.Holo.Dialog">
<item name="android:windowMinWidthMajor">90%</item>
<item name="android:windowMinWidthMinor">90%</item>
</style>
This is the way by which you can create your own dialog with custom layout.
val dialogBuilder = AlertDialog.Builder(context, R.style.AlertDialogTheme)
val inflater = this.layoutInflater
val dialogView = inflater.inflate(R.layout.layout_chat_type_selection, null)
dialogBuilder.setView(dialogView)
val radioGroupChat = dialogView.radio_group_chat
dialogView.radioButton_user_chat.isChecked = true
dialogBuilder.setPositiveButton(getString(R.string.ok_text), object : DialogInterface.OnClickListener {
override fun onClick(dialog: DialogInterface, id: Int) {
when (radioGroupChat.checkedRadioButtonId) {
R.id.radioButton_user_chat -> {
(activity as HomeActivity).replaceFragment(MySkippersFragment.getInstance(isFromChat = true))
}
R.id.radioButton_circle_chat -> {
(activity as HomeActivity).replaceFragment(PickCircleFragment.getInstance(
PickCircleFragment.NEW_CIRCLE_CHAT), true)
}
}
}
})
dialogBuilder.setNegativeButton(getString(R.string.cancel_text), object : DialogInterface.OnClickListener {
override fun onClick(dialog: DialogInterface?, which: Int) {
}
})
val alertDialog = dialogBuilder.create()
alertDialog.show()
Works fine. You can also customize it the way you want.
Main class:
class CustomAlertDialogOneButton( activity: Activity?,
private val alertOneButtonClickListener: OnAlertOneButtonClickListener):Dialog(activity!!) {
private var title = ""
private var text = ""
private var dialogId = -1
private var buttonName = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.custom_alert_one_button_dialog)
setCancelable(false)
this.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT))
viewsClickListenerInit()
}
override fun onStart() {
initDialog()
super.onStart()
}
private fun fillFields(title: String, text: String?, dialogId: Int, buttonName: String) {
clearDialog()
this.title = title
this.text = text ?: ""
this.dialogId = dialogId
this.buttonName = buttonName
}
private fun clearDialog() {
title = ""
text = ""
}
private fun initDialog() {
if (title.isNotBlank()) {
tvAlertTitle.text = title
}
if (text.isNotBlank()) {
tvAlertText.text = text
}
tvAlertButtonOk.text = buttonName
}
fun show(title: String, text: String?, dialogId: Int = -1, buttonName: String = ResourcesRepository.resources.getString(R.string.ok)) {
fillFields(title, text, dialogId, buttonName)
super.show()
}
private fun viewsClickListenerInit() {
tvAlertButtonOk.setOnClickListener {
alertOneButtonClickListener.okClickListener(dialogId)
dismiss()
}
}}
XML for it: name it likes - custom_alert_one_button_dialog.xml
<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="280dp"
android:layout_height="145dp"
android:background="#color/colorWhite"
android:orientation="vertical">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="#+id/tvAlertTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="24dp"
android:paddingTop="22dp"
android:textAlignment="center"
android:textColor="#color/colorDarkGrey"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tvAlertText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="24dp"
android:paddingTop="13dp"
android:paddingEnd="24dp"
android:paddingBottom="10dp"
android:textColor="#color/colorGrey"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/tvAlertTitle" />
<TextView
android:id="#+id/tvAlertButtonOk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:paddingTop="10dp"
android:paddingEnd="34dp"
android:paddingBottom="18dp"
android:text="#string/ok"
android:textColor="#color/colorDarkGrey"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
Interface for button click listener:
interface OnAlertOneButtonClickListener {
fun okClickListener(dialogId: Int = -1)}
Implementation:
class SomeActivity : AppCompatActivity(), OnAlertOneButtonClickListener {
****
private var customDialogOneButton by lazy {
CustomAlertDialogOneButton(this, this)
}
****
customDialogOneButton.show(
title = "some title",
text = "some text",
dialogId = some int constant,
buttonName = "some button name"
)
****
override fun okClickListener(dialogId: Int) {
when (dialogId) {
some int constant -> {
// call method
}
}
}
My custom dialog xml file:
<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"
android:background="#color/dialogWindowBackground">
<TextView
android:id="#+id/popup_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16sp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16sp"
android:layout_marginBottom="10dp"
android:text="#string/body"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout
android:id="#+id/linearLayoutOpt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16sp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16sp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/popup_dialog">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:orientation="horizontal">
<TextView
android:id="#+id/no_opt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingTop="8dp"
android:paddingRight="8dp"
android:paddingBottom="18dp"
android:text="No"
android:textAllCaps="false"
android:textColor="#color/colorAccent" />
<Space
android:layout_width="32sp"
android:layout_height="12sp" />
<TextView
android:id="#+id/yes_opt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingTop="8dp"
android:paddingRight="8dp"
android:paddingBottom="18dp"
android:text="Yes"
android:textAllCaps="false"
android:textColor="#color/colorAccent" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
My Kotlin code:
private fun showDialog() {
val customDialog = Dialog(requireActivity())
customDialog.setContentView(R.layout.custom_dialog)
customDialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val yesBtn = customDialog.findViewById(R.id.yes_opt) as TextView
val noBtn = customDialog.findViewById(R.id.no_opt) as TextView
yesBtn.setOnClickListener {
//Do something here
customDialog.dismiss()
}
noBtn.setOnClickListener {
customDialog.dismiss()
}
customDialog.show()
}
If someone wants to show 2 buttons on dialog ( GENERIC SOLUTION ) KOTLIN
fun createCustomTwoButtonDialog(
msg: String, context: Context?
, positiveButtonText: String, negativeButtonText: String,
isCancellable: Boolean,
dialogListener: DialogListener
) {
context?.let { context ->
val builder =
AlertDialog.Builder(context)
builder.setTitle("Your desired title")
builder.setMessage(msg)
builder.setCancelable(isCancellable)
builder.setPositiveButton(positiveButtonText) { dialogInterface: DialogInterface?, i: Int ->
dialogListener.onPositiveClick()
dialogInterface?.dismiss()
}
builder.setNegativeButton(negativeButtonText)
{ dialogInterface: DialogInterface?, i: Int ->
dialogListener.onNegativeClick()
dialogInterface?.dismiss()
}
val alertDialog = builder.create()
alertDialog.show()
}
}
DialogListener is interface with 2 methods onNegativeClick() and onPositiveClick()
I created this Kotlin Class :
class AnimDialog(private val context: Context) {
private val dialog = Dialog(context)
fun test(s: String) {
Log.i("TAG", "test: $s")
}
init{
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
}
fun one(headerImage :Int?=null,
description: String,
titleOfPositiveButton: String? = null,
titleOfNegativeButton: String? = null,
positiveButtonFunction: (() -> Unit)? = null,
negativeButtonFunction: (() -> Unit)? = null
) :Dialog{
dialog.setCancelable(false)
dialog.setContentView(R.layout.dialog_one)
val dialogHeaderImage=dialog.findViewById(R.id.imageHead_dia) as ImageView
val dialogDescription = dialog.findViewById(R.id.description) as TextView
val dialogPositiveButton = dialog.findViewById(R.id.positiveButton) as Button
val dialogNegativeButton = dialog.findViewById(R.id.negativeButton) as Button
headerImage?.let { dialogHeaderImage.setImageResource(it) }
dialogHeaderImage.
startAnimation(
AnimationUtils.loadAnimation(
context,
R.anim.bounce
)
)
dialogDescription.text = description
titleOfPositiveButton?.let { dialogPositiveButton.text = it } ?: dialogPositiveButton.text
titleOfNegativeButton?.let { dialogNegativeButton.text = it } ?: dialogNegativeButton.text
dialogPositiveButton.setOnClickListener {
positiveButtonFunction?.invoke()
dialog.dismiss()
}
dialogNegativeButton.setOnClickListener {
negativeButtonFunction?.invoke()
dialog.dismiss()
}
return dialog
}
Where you must define :
1- Layout <dialog_one> include positive btn, negative btn, image view, text view
2- Animation <bounce> if you would like add anim to the header image
Now in Activity:
val animDialog = AnimDialog(this)
animDialog.one(
headerImage = android.R.drawable.ic_delete,
description = "Sometimes we looking for new dialog to fill better \n ",
titleOfPositiveButton = "Accept it",
titleOfNegativeButton = "Not now",
positiveButtonFunction = {
animDialog.test("OK")
},
negativeButtonFunction = {
animDialog.test("NO")
}).show()

Categories

Resources