I've a mutableLiveData of list of items in my fragment and I have a custom dialog that I pass a single item from this list to it . this is my code:
adapter.itemPosPriceListener.observe(this, Observer { pos ->
activity?.let { act ->
val dialog = DialogRoomPrices(act)
dialog.item.value = it.get(pos)
}
})
this is my dialog :
class DialogRoomPrices(act: Activity) {
private var dialog: Dialog
private var act: Activity
val item = MutableLiveData<RoomItem>()
init {
dialog = Dialog(act, R.style.DialogAnimation)
dialog.setContentView(R.layout.dialog_room_prices)
this.act = act
makeDialog()
val lp = WindowManager.LayoutParams()
lp.copyFrom(dialog.window?.attributes)
lp.width = WindowManager.LayoutParams.MATCH_PARENT
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
dialog.getWindow()?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
dialog.show()
dialog.window?.attributes = lp
}
private fun makeDialog() {
item?.value?.let { row ->
dialog.et_roomp_price_paye.setText(row.price_paye)
dialog.et_roomp_price_akhathafte.setText(row.price_akhathafte)
////and 10 more rows of these
}
dialog.bt_next.setOnClickListener {
/////need to update the item and observe it in my fragment
dialog.dismiss()
}
}
as you can see ,dialog has a form and after filling the data and clicking on submit button , I need to update my item in fragment class .
the question is , how can I update the this item and notify my fragment's item about it ?
I don't see the whole code, but such communication can be done simply with a callback.
Solution will look like
class DialogRoomPrices(act: Activity) {
interface Listener() {
fun onNext(data: SomeData)
}
private var listener: Listener?
private var dialog: Dialog
private var act: Activity
val item = MutableLiveData<RoomItem>()
...
dialog.bt_next.setOnClickListener {
val dataToBeSetToHostingFragment = getData()
listener?.onNext(dataToBeSetToHostingFragment)
dialog.dismiss()
}
In the Fragment
class HostingFragment(): Listener {
adapter.itemPosPriceListener.observe(this, Observer { pos ->
activity?.let { act ->
val dialog = DialogRoomPrices(act)
dialog.item.value = it.get(pos)
dialog.listener = object : DialogRoomPrices.Listener {
override onNext(data: SomeData) {
// Assign data to your liveData, force update recycler view or do any other action you want to
}
}
}
})
Related
Here are the adapter class and I am trying to create a button on click listener within OnCreateViewHolder to click and move on to the next page of the Pager2ViewHolder and nothing seems to work for me.
I really appreciate any help that will be given.
package com.example.apitestquiz.viewmodel
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView
import com.example.apitestquiz.R
import com.example.apitestquiz.model.QuestionModelItem
class ViewPagerAdapter(private var list:List<QuestionModelItem>):RecyclerView.Adapter<ViewPagerAdapter.Pager2ViewHolder>() {
private lateinit var radioButton: RadioButton
private var score:IntArray = IntArray(list.size)
private var answerCorrect = Array(list.size){""}
private var count:IntArray = IntArray(list.size)
inner class Pager2ViewHolder(itemView : View): RecyclerView.ViewHolder(itemView){
//item_page content
val radioGroup: RadioGroup = itemView.findViewById(R.id.radioGroup2)
val textView: TextView = itemView.findViewById(R.id.textQuestion2)
val buttonRight: Button = itemView.findViewById(R.id.buttonRight)
val buttonLeft: Button = itemView.findViewById(R.id.buttonLeft)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerAdapter.Pager2ViewHolder {
return Pager2ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_page,parent,false))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: ViewPagerAdapter.Pager2ViewHolder, position:Int){
holder.textView.text = list[position].question
val answers = getAnswerCollection(list,position)
holder.radioGroup.children.forEachIndexed { index, view ->
radioButton = view as RadioButton
radioButton.text = answers[index]
}
holder.radioGroup.setOnCheckedChangeListener { _, checkedId ->
val radio:RadioButton = holder.radioGroup.findViewById(checkedId)
if(radio.text.toString().equals(answerCorrect[position],true)){
if (score[position]<=0)
count[position] = 1
score[position] = 1
}else{
if (score[position]>=0){
count[position] = 1
score[position] = 0
}
}
}
// holder.buttonRight.setOnClickListener {
// val context = holder.itemView.context
// if(count.sum() == 5){
// val intent = Intent (context, EndActivity::class.java)
// intent.putExtra("scoreFin",score.sum().toString())
// context.startActivity(intent)
// }else{ Toast.makeText(context,"Answer All Question",Toast.LENGTH_SHORT).show() }
// }
when(position){
0-> {
holder.buttonLeft.visibility = View.INVISIBLE
holder.buttonRight.visibility = View.VISIBLE
holder.buttonRight.text = "Next"
}
4 ->{
holder.buttonLeft.visibility = View.VISIBLE
holder.buttonRight.visibility = View.VISIBLE
holder.buttonRight.text = "Finish"
holder.buttonLeft.text = "Previous"
}
else -> {
holder.buttonLeft.visibility = View.VISIBLE
holder.buttonRight.visibility = View.VISIBLE
holder.buttonRight.text = "Next"
holder.buttonLeft.text = "Previous"
}
}
}
private fun getAnswerCollection(x:List<QuestionModelItem>,y:Int): MutableList<String> {
answerCorrect[y] = x[y].correctAnswer //Getting answer from API
val answerWrong:List<String> = x[y].incorrectAnswers
val answerCollect = answerWrong + x[y].correctAnswer //Getting answer collections and shuffling
val answerShuffle = answerCollect.toMutableList()
answerShuffle.shuffle()
return answerShuffle
}
}
I have tried some animation but that does not scroll/swipe to the next activity only a blank page
if you take a look into the documentation of ViewPager2 https://developer.android.com/reference/kotlin/androidx/viewpager2/widget/ViewPager2 you can see that has setCurrentItem method. I suggest you add a callback for clicking on button that has to move to the next page. It will look something like this:
class ViewPagerAdapter(private var list:List<QuestionModelItem>, val onButtonRightClick: (Int) -> Unit))
And in buttonRight onClickListener you call that callback with current index:
buttonRight.setOnClickListener {
onButtonRightClick(position)
}
The last step is to properly add a callback to the adapter, so in your Fragment or Activity wherever you have your adapter you must create it in that way:
val adapter = ViewPagerAdapter(list) {
viewPager2.setCurrentItem(it + 1)
}
I made an assumption that your ViewPagerAdapter can be referenced as viewPager2.
I have custom dialog where inside its xml has a Submit button that whenever clicked, it should dismiss the softKeyboard.
I have the following code:
MainActivity.kt (Inherits from BaseActivity)
val updateDialog = Dialog(this, R.style.CustomDialog)
updateDialog.setContentView(R.layout.dialog_update)
val tvSubmitToFirestore = updateDialog.findViewById<HelveticaBoldTextView>(R.id.tv_update_item)
tvSubmitToFirestore.setOnClickListener {
hideKeyboard(currentFocus ?: View(this))
}
BaseActivity:
open class BaseActivity : AppCompatActivity() {
//hide keyboard when instance of an event
fun Context.hideKeyboard(view: View) {
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
}
But the keyboard remains on its position after clicking the said button.
Update:
//update item
val tvUpdate = showItemDialog.findViewById<HelveticaNormalTextView>(R.id.tv_update)
tvUpdate.setOnClickListener {
//close search dialog
showItemDialog.dismiss()
val updateDialog = Dialog(this, R.style.CustomDialog)
updateDialog.setContentView(R.layout.dialog_update)
updateDialog.setCancelable(false)
//spinner settings
val spinner = updateDialog.findViewById<Spinner>(R.id.sp_update)
populateSpinner(spinner, dialogProductCategory.text.toString())
//set default values of update dialog fields == the current product's properties(name, category, price)
val etProductName = updateDialog.findViewById<TextInputEditText>(R.id.et_update_product_name)
val etProductPrice = updateDialog.findViewById<TextInputEditText>(R.id.et_update_product_price)
etProductName.setText(dialogProdName.text.toString())
etProductPrice.setText(dialogProductPrice.text.toString())
//update item
val tvSubmitToFirestore = updateDialog.findViewById<HelveticaBoldTextView>(R.id.tv_update_item)
//TODO allow user to update an item with the same name provided that it should only exist once
// and must be unique
tvSubmitToFirestore.setOnClickListener {
// hideKeyboard(currentFocus ?: tvSubmitToFirestore)
hideKeyboard1(it, this#MainActivity)
val newProductName = etProductName.text.toString().trim()
val newProductCategory = spinner.selectedItem.toString().uppercase()
val newProductPrice = etProductPrice.text.toString().toDouble()
GlobalScope.launch {
validateProduct(newProductName, newProductCategory, newProductPrice)
}
//TODO : allow user to reuse the product name when updating
}
You can use the new WindowInsetsController library to hide and show keyboard. Use this function:
fun hideKeyboard(view: View, activity: Activity) {
WindowInsetsControllerCompat(activity.window, view).hide(WindowInsetsCompat.Type.ime())
}
In your case, you call use it like this:
tvSubmitToFirestore.setOnClickListener { view ->
hideKeyboard(view, this) // 'this' refers to MainActivity
}
I created a scrollView programmaticaly that contains 20 views each with an image and a text.
I have two questions :
1 - is the id assignment correct and is my setOnClickListener correct?
2 - By which method onClick can I know which view of the scrollView the user has clicked?
See my code below
private var integerList: MutableList<Int>? = mutableListOf()
private var cellNo: MutableList<String>? = mutableListOf()
private var sv_mvmtChoosed = ""
private fun showSpinner() {
/* SCROllL VIEW */
var linearLayout: LinearLayout? = null
linearLayout = findViewById(R.id.linear1)
val layoutInflater = LayoutInflater.from(this)
var randIndex = 0
for (posIndex in 0..19) {
val rand = Random()
randIndex = rand.nextInt(20)
while (integerList!!.contains(randIndex)) {
randIndex = rand.nextInt(20)
}
integerList!!.add(randIndex)
// Create the view...
val view: View = layoutInflater.inflate(R.layout.scroll_bckgrnd, linearLayout, false)
// give it an id
view.id = generateViewId()
view.setOnClickListener(this)
cellNo!!.add(view.id.toString())
println(cellNo)
//... then populate it with image and text
val iv = view.findViewById<ImageView>(R.id.iv)
iv.setImageResource(sv_photoImage[randIndex])
val tv = view.findViewById<TextView>(R.id.tv)
tv.text = sv_photoName[randIndex]
linearLayout?.addView(view)
}
// which view the user did select?
fun onClick(view: View?) {
when (view!!.id) {
??? -> doSomething
}
}
}
Any idea to get me back on track will be welcome.
Its probably better to make a new OnClickListener for every view.
view.setOnClickListener(this)
needs to be this
view.setOnClickListener {
// do something
}
or
view.setOnClickListener(createOnClickListner())
fun createOnClickListner():View.OnClickListener{
return object :View.OnClickListener{
override fun onClick(view : View) {
//do something with the view that was clicked
}
}
}
Thanks a lot avalerio.
I finally found a solution as follow :
I replaced :
// give it an id
view.id = generateViewId()
view.setOnClickListener(this)
cellNo!!.add(view.id.toString())
println(cellNo)
with :
// give it an id
view.id = posIndex
view.setOnClickListener(this)
then I did this :
// the onClickListener for my 20 images/text
override fun onClick(view: View?) {
when (view!!.id) {
// Now de position clicked on the ScrollView
in 0..19 -> didHeSucceeded(view!!.id)
}
}
And use the result:
private fun didHeSucceeded(scrllPos: Int) {
// TODO: close de scrollView, how ? :-)
spinnerChoice = nameOfTechScrollVw[scrllPos]
succes = (!allreadyChoosedArray.contains(spinnerChoice)) && (currentArray.contains(spinnerChoice
))
if (succes) {
...
...
}
It works perfectly
Updating value of recyclerview but unable to update corresponding data in model class
Model classes
#Parcelize
class GetStockListData : ArrayList<GetStockListDataItem>(), Parcelable
#Parcelize
data class GetStockListDataItem(
var Qty:#RawValue Double,
var selectedQty: Double
): Parcelable
able to change recyclerview element using alertbox as follows
private fun showAlertDialog(stockListData: GetStockListData, position: Int) {
val layoutInflater = LayoutInflater.from(context)
val customView =
layoutInflater.inflate(R.layout.change_qty_dialog, null)
val myBox: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(context)
myBox.setView(customView)
val dialog = myBox.create()
dialog.show()
val etQuantity = customView.findViewById<AppCompatEditText>(R.id.et_quantity)
if (stockListData[position].Qty < args.getListDetailsByRNumberModelItem.ReqQty) {
val df = DecimalFormat("#.##")
df.roundingMode = RoundingMode.CEILING
etQuantity.setText(df.format(stockListData[position].Qty).toString())
} else
etQuantity.setText(args.getListDetailsByRNumberModelItem.ReqQty.toString())
etQuantity.setSelection(etQuantity.text.toString().length)
etQuantity.requestFocus()
requireContext().showKeyboard()
customView.findViewById<Button>(R.id.btnDone).setOnClickListener {
if(!etQuantity.text.isNullOrEmpty()) {
val qtyStr = etQuantity.text.toString().trim()
var qtyDouble = qtyStr.toDouble()
stockListData[position].selectedQty = qtyDouble
adapter.notifyDataSetChanged()
dialog.dismiss()
}
}
}
for (i in 0 until stockListData.size){
sum += stockListData[i].selectedQty
}
here if user edit Recyclerview list item multiple times, each value added to list. Finally if i try to retrieve sum of all recyclerview elements getting wrong value because in model class values are added when i try to replace element.
Instead of passing whole list as parameter to showAlertDialog() method, you could just pass single item which has to be updated. And one more thing, you should not call adapter.notifyDataSetChanged() for single item updation, rather call adapter.notifyItemChanged(position). Look at below code, I am getting correct sum :
private fun showRecipeMeasureDialog(recipeItem: RecipeItem?,position: Int){
val dialogView = LayoutInflater.from(context).inflate(R.layout.add_recipe_measure_dialog, null)
val dialog = AlertDialog.Builder(context, R.style.RoundedCornersDialog).setView(dialogView).show()
dialog.setCancelable(false)
val displayRectangle = Rect()
val window = activity?.window
window?.decorView?.getWindowVisibleDisplayFrame(displayRectangle)
dialog.window?.setLayout(
(displayRectangle.width() * 0.5f).toInt(),
dialog.window!!.attributes.height
)
context?.resources?.let {
dialogView.findViewById<TextView>(R.id.recipe_measure_title).text = java.lang.String.format(it.getString(R.string.addRecipeMeasure), unitsArray[currentMeasurementUnits - 1])
}
val doneBtn = dialogView.findViewById<ImageButton>(R.id.recipe_measure_done_btn)
val closeBtn = dialogView.findViewById<ImageButton>(R.id.close_btn_add_recipe)
val conversionEditText = dialogView.findViewById<ClearFocusEditText>(R.id.recipe_conversion_tie)
doneBtn.isEnabled = false
if (recipeItem != null ){
conversionEditText.setText("${recipeItem.conversionRatio}")
}
closeBtn.setOnClickListener {
context?.hideKeyboard(it)
dialog.dismiss() }
doneBtn.setOnClickListener {
context?.hideKeyboard(it)
val conversionRatio = conversionEditText.text.toString().toFloat()
if (recipeItem != null){
recipeItem.conversionRatio = conversionRatio
recipeItemList[position] = recipeItem
recipeAdapter.notifyItemChanged(position)
}else{
recipeItemList.add(0,RecipeItem(0,0,conversionRatio,0)) // Use your data class in place of RecipeItem
recipeAdapter.notifyItemInserted(0) // new Item is added to index zero, so adapter has to be updated at index zero
}
// calculating sum
val sum = recipeItemList.map { it.conversionRatio }.sum()
Log.d("tisha==>>","Conversion ratio sum = $sum")
dialog.cancel()
}
}
I want to display information that RecyclerView have no items, but I can't check if Firestore collection is empty. How to set some kind of listener which check if RecyclerView have items or not?
I'm assuming you're using Firebase UI (otherwise you would already have a query callback to hook into). In your FirestoreRecyclerAdapter, you can override onDataChanged & onError:
typealias DataChangedListener = (count: Int) -> Unit
typealias ErrorListener = (error: FirebaseFirestoreException) -> Unit
class MyAdapter(
options: FirestoreRecyclerOptions<MyModel>,
private val onDataChangedListener: DataChangedListener = {},
private val onErrorListener: ErrorListener = {}
) : FirestoreRecyclerAdapter<MyModel, MyViewHolder>(options) {
...
// Notify Activity/Fragment/ViewModel
override fun onDataChanged() =
onDataChangedListener.invoke(itemCount)
// Notify Activity/Fragment/ViewModel
override fun onError(e: FirebaseFirestoreException) =
onErrorListener.invoke(e)
}
You can use it like this:
recyclerView.adapter = MyAdapter(
options,
{ count -> showHideNoData(count > 0) },
{ error -> showError(error) }
)
...
fun showHideNoData(haveData: Boolean) {
recyclerView.isVisible = haveData
noDataView.isVisible = !haveData
errorView.isVisible = false
}
fun showError(error: FirebaseFirestoreException) {
recyclerView.isVisible = false
noDataView.isVisible = false
errorView.isVisible = true
// Logging & other UI changes
}
If it will be useful here is my solution. I simply called this function in the fragment where RecyclerView lives:
private fun setUpRecyclerView() {
val viewManagerPortrait = LinearLayoutManager(activity)
val viewManagerLandscape = GridLayoutManager(activity, 3)
val query = docRef.orderBy("title", Query.Direction.ASCENDING)
query.addSnapshotListener { p0, _ ->
if (p0 != null) {
if(p0.size() > 0) {
emptyAds.visibility = View.GONE;
listItems.visibility = View.VISIBLE
}else {
emptyAds.visibility = View.VISIBLE;
listItems.visibility = View.GONE
}
}
}
val options = FirestoreRecyclerOptions.Builder<Item>()
.setQuery(query,Item::class.java)
.setLifecycleOwner(this)
.build()
mAdapter = ItemCardsAdapter(this,options)
listItems.apply {
setHasFixedSize(true)
// use a linear layout manager if portrait, grid one else
layoutManager = if(activity!!.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)
viewManagerLandscape
else
viewManagerPortrait
adapter = mAdapter
}
}
As you can see the if statement (inside the SnapShotListener) on size checks whether the database at that reference is empty, showing a message in the layout instead of the RecyclerView.