I've got a Popup menu that should launch a Maps intent whenever a Popup menu item is clicked. In popupMenu.setOnMenuItemClickListener, does anyone know how I can pass the String of the clicked Popup menu item (from the arrayAMap array) and use it for an intent? I've already got the Arrays but I can't seem to figure out that correct way to implement this function.
class MyAdapter(
private val mCtx: Context,
var myList: MutableList<ItemRV>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), PopupMenu.OnMenuItemClickListener {
private var mClickListener: ItemClickListener? = null
lateinit var mAdView : AdView
private val itemRV = 1
private val itemAD = 2
override fun getItemViewType(position: Int): Int {
return if (position % 4 == 0) {
itemAD
} else {
itemRV
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == itemAD) {
val v = LayoutInflater.from(mCtx).inflate(R.layout.item_ad, parent, false)
AdViewHolder(v)
} else {
val v = LayoutInflater.from(mCtx).inflate(R.layout.item_rv, parent, false)
AreaViewHolder(v)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(holder) {
is AdViewHolder -> {
MobileAds.initialize(mCtx) {}
mAdView = holder.itemView.findViewById(R.id.adView)
val adRequest = AdRequest.Builder().build()
mAdView.loadAd(adRequest)
}
is AreaViewHolder -> {
val positionToBind = position - position / 4 - 1
val product = myList[positionToBind]
holder.tvTitle.text = product.itemTitle
}
}
}
override fun getItemCount(): Int {
return myList.size
}
inner class AdViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView
.ViewHolder(itemView), View.OnClickListener {
override fun onClick(v: View?) {
}
}
inner class AreaViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView
.ViewHolder(itemView), View.OnClickListener {
var tvTitle: TextView = itemView.tvtitle
// Use package name which we want to check
private val isAppInstalled = appInstalledOrNot("com.google.android.apps.maps")
private val isLiteAppInstalled = appInstalledOrNot("com.google.android.apps.mapslite")
fun launchMapIntent(nameLocation: String) {
val mapPkg = when {
isAppInstalled -> "com.google.android.apps.maps"
isLiteAppInstalled -> "com.google.android.apps.mapslite"
else -> null
}
val mapIntent = if(mapPkg != null) {
val gmmIntentUri = Uri.parse("geo:0,0?q=$nameLocation")
Intent(Intent.ACTION_VIEW, gmmIntentUri).setPackage(mapPkg)
} else {
val encLoc = Uri.encode(nameLocation)
val str = "https://www.google.com/maps/place/$encLoc/"
Intent(Intent.ACTION_VIEW, Uri.parse(str))
}
mCtx.startActivity(mapIntent)
}
val arrayA = arrayOf(view.resources.getString(R.string.stockholm),
view.resources.getString(R.string.copenhagen))
val arrayAMap = arrayOf("Stockholm, Sweden", "Copenhagen, Denmark")
fun launchPopupMenu(namePopupItemLocation: Array<String>, nameLocation: Array<String>){
val popupMenu = PopupMenu(ibMap.context, ibMap)
for (item in namePopupItemLocation) {
popupMenu.menu.add(item)
}
popupMenu.setOnMenuItemClickListener {
launchMapIntent(nameLocation.get())
true
}
popupMenu.show()
}
ibMap.setOnClickListener {
when(tvTitle.text.toString()) {
"A" -> launchMapIntent("Paris, France")
"B" -> launchPopupMenu(arrayA, arrayAMap)
else -> return#setOnClickListener
}
}
}
private fun appInstalledOrNot(uri: String): Boolean {
val pm = mCtx.packageManager
try {
pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES)
return true
} catch (e: PackageManager.NameNotFoundException) {
}
return false
}
}
// Parent activity will implement this method to respond to click events
interface ItemClickListener {
fun onItemClick(view: View, position: Int)
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
}
}
You can get clicked menu item in setOnMenuItemClickListener and from that menu item you can get title of that menu
popupMenu.setOnMenuItemClickListener { item ->
nameLocation.forEach {
if (it.toLowerCase().startsWith(item.title.toLowerCase())) {
launchMapIntent(it)
}
}
true
}
Hope this will help!!
Related
I have a recyclerview that can have three different views base on the data with three different record types. If the data is composed of all type 1 records or both type 1 and type 2 records, then all of the data is displayed properly. But if a type 3 record is present, then no data following that type 3 record is displayed. And I am not getting any sort of error that I can find.
Any following type 1 and type 2 records are not displayed.
Here is the code for my recyclerview adapter:
class ShipsAdapter (private val stateroomInfo: ArrayList<StateroomAdapterData>,
private val listener: OnItemClickListener):
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var rowType = 0
override fun getItemViewType(position: Int): Int {
rowType = when (stateroomInfo[position].adapterRecordType) {
Constants.SHIP_ROW -> Constants.SHIP_ROW
Constants.STATEROOM_ROW -> Constants.STATEROOM_ROW
else -> Constants.STATEROOM_PROPERTIES_ROW
}
return rowType
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
when (rowType){
Constants.SHIP_ROW -> {
val view = ListShipsRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context),
parent, false)
ListShipsViewHolder(view)
}
Constants.STATEROOM_ROW -> {
val view = ListStateroomsRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context),
parent, false)
ListStateroomsViewHolder(view)
}
else -> {
val view = ListStateroomPropertiesRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context),
parent, false)
ListStateroomPropertiesViewHolder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (rowType) {
Constants.SHIP_ROW -> (holder as ListShipsViewHolder).bind(position)
Constants.STATEROOM_ROW -> (holder as ListStateroomsViewHolder).bind(position)
Constants.STATEROOM_PROPERTIES_ROW -> (holder as ListStateroomPropertiesViewHolder).bind(position)
}
}
override fun getItemCount(): Int {
return stateroomInfo.size
}
inner class ListShipsViewHolder(shipsRow: ListShipsRecyclerItemRowBinding):
RecyclerView.ViewHolder(shipsRow.root),
View.OnClickListener {
private val tvShip = shipsRow.tvShip
private var ivEditShip = shipsRow.ivEditShip
private var ivDeleteShip = shipsRow.ivDeleteShip
private var ivStateroom = shipsRow.ivStateroom
init {
ivEditShip.setOnClickListener(this)
ivDeleteShip.setOnClickListener(this)
ivStateroom.setOnClickListener(this)
}
override fun onClick(v: View?) {
if (adapterPosition != RecyclerView.NO_POSITION)
if (v != null) {
when (v.id) {
R.id.ivEditShip -> listener.onItemClicked(adapterPosition, Constants.EDIT_SHIP_CLICKED)
R.id.ivDeleteShip -> listener.onItemClicked(adapterPosition, Constants.DELETE_SHIP_CLICKED)
R.id.ivStateroom -> listener.onItemClicked(adapterPosition, Constants.STATEROOM_CLICKED)
}
}
}
fun bind(position: Int) {
val shipData = stateroomInfo[position]
tvShip.text = shipData.ship
}
}
inner class ListStateroomsViewHolder(stateroomRow: ListStateroomsRecyclerItemRowBinding):
RecyclerView.ViewHolder(stateroomRow.root),
View.OnClickListener {
private val tvStateroom = stateroomRow.tvStateroom
private var ivEditStateroom = stateroomRow.ivEditStateroom
private var ivDeleteStateroom = stateroomRow.ivDeleteStateroom
init {
ivEditStateroom.setOnClickListener(this)
ivDeleteStateroom.setOnClickListener(this)
}
override fun onClick(v: View?) {
if (adapterPosition != RecyclerView.NO_POSITION)
if (v != null) {
when (v.id) {
R.id.ivEditStateroom -> listener.onItemClicked(adapterPosition, Constants.EDIT_STATEROOM_CLICKED)
R.id.ivDeleteStateroom -> listener.onItemClicked(adapterPosition, Constants.DELETE_STATEROOM_CLICKED)
}
}
}
fun bind(position: Int) {
val stateroomData = stateroomInfo[position]
tvStateroom.text = stateroomData.stateroom
}
}
inner class ListStateroomPropertiesViewHolder(stateroomPropertiesRow: ListStateroomPropertiesRecyclerItemRowBinding):
RecyclerView.ViewHolder(stateroomPropertiesRow.root),
View.OnClickListener {
private val tvBedrooms = stateroomPropertiesRow.tvBedrooms
private val tvBaths = stateroomPropertiesRow.tvBathrooms
private val tvConnecting = stateroomPropertiesRow.tvIsConnecting
private val ivEditStateroomProperties = stateroomPropertiesRow.ivEditStateroomProperties
init {
ivEditStateroomProperties.setOnClickListener(this)
}
fun bind(position: Int) {
val stateroomPropertiesData = stateroomInfo[position]
tvBedrooms.text = stateroomPropertiesData.numBedrooms
tvBaths.text = stateroomPropertiesData.numBaths
tvConnecting.text = stateroomPropertiesData.connectingSuite
}
override fun onClick(v: View?) {
if (adapterPosition != RecyclerView.NO_POSITION)
if (v != null) {
listener.onItemClicked(adapterPosition, Constants.EDIT_STATEROOM_PROPERTIES_CLICKED)
}
}
}
// this is required to communicate with the click listener in the Fragments
interface OnItemClickListener {
fun onItemClicked(position: Int, clickType: Int)
}
}
I am trying to show multiple type of views in a recyclerview with a header and 5 rows followed by another header and 5 rows but only 4 rows are appearing.
Here is the log output from the adapter in the onBindViewHolder
Here is the code from the adapter
class DailyFragAdapter(
private val activityData: (DetailActivityData) -> Unit,
private val dates: List<Date>,
private val list : ArrayList<TabType1>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>()
/*
ListAdapter<TabType1, RecyclerView.ViewHolder>(Diff())*/ {
private lateinit var context: Context
private val HEADER = 1
private val ROW = 2
/*private class Diff : DiffUtil.ItemCallback<TabType1>() {
override fun areItemsTheSame(oldItem: TabType1, newItem: TabType1): Boolean {
return oldItem.header == newItem.header && oldItem.cps.cps[0].id == newItem.cps.cps[0].id
}
override fun areContentsTheSame(
oldItem: TabType1,
newItem: TabType1
): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
}*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
context = parent.context
val layoutInflater = LayoutInflater.from(context)
return if (viewType == 2) {
DVH(DailyFragRecyclerItemBinding.inflate(layoutInflater, parent, false))
} else {
TitleHolder(DailyTableHeaderBinding.inflate(layoutInflater, parent, false))
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val data: TabType1 = list[position]
if (holder is DVH) {
data.cps.cps.forEach {
Timber.i("is: a row")
holder.bind(it)
}
}else{
Timber.i("is: header")
holder as TitleHolder
holder.bind(data.header)
}
}
override fun getItemViewType(position: Int): Int {
val tabType1 = list[position]
return if (tabType1.header.title == "") {
ROW
} else {
HEADER
}
}
inner class TitleHolder(binding: DailyTableHeaderBinding) :
RecyclerView.ViewHolder(binding.root) {
private val groupHeader: TextView = binding.title
fun bind(title: Header) {
groupHeader.text = title.title
}
}
inner class DVH(binding: DailyFragRecyclerItemBinding) : RecyclerView.ViewHolder(binding.root) {
private var cpname: TextView = binding.cpName
private var day1: FrameLayout = binding.frameLayout
private var day2: FrameLayout = binding.frameLayout2
private var day3: FrameLayout = binding.frameLayout3
private var day4: FrameLayout = binding.frameLayout4
private var dayContainer: ArrayList<FrameLayout> = ArrayList()
fun bind(cpVo: CheckingPointVo) {
dayContainer.addAll(arrayListOf(day1, day2, day3, day4))
cpname.apply {
text = cpVo.name
setOnClickListener {
activityData.invoke(DetailActivityData(cpVo.id, cpVo.pcID))
}
}
val map = cpVo.chartEntriesMap
for (i in dates.indices) {
val dateID = BkpDateUtil.getDateId(dates[i])
Timber.i("dateID $dateID")
var ceVo: List<ChartEntryVo>? = map[dateID]
if (ceVo == null) {
ceVo = ArrayList<ChartEntryVo>()
ceVo.add(ChartEntryVo(0, dateID, 0L, 0L, "", false))
}
val entryValue = ceVo[0].value
when (cpVo.cpType) {
CheckingPointType.YES_NO -> {
val fl: FrameLayout = dayContainer[i]
fl.setOnClickListener {
openYesNoDialog(cpVo, ceVo[0])
}
if (entryValue == 1L) {
setIconInChartEntry(fl, R.drawable.ic_tick, cpVo)
} else {
setIconInChartEntry(fl, R.drawable.ic_no_entry, cpVo)
}
}
CheckingPointType.QUANTIFIABLE -> {
}
CheckingPointType.COUNTABLE -> {
}
CheckingPointType.CATEGORY -> {
}
CheckingPointType.TEXT -> {
}
}
}
dayContainer.clear()
}
}
private fun setIconInChartEntry(fl: FrameLayout, resID: Int, cpVo: CheckingPointVo) {
fl.getChildAt(0).visibility = GONE
val image: ImageView = fl.getChildAt(1) as ImageView
image.visibility = VISIBLE
image.setImageResource(resID)
/*image.setOnClickListener {
activityData.invoke(DetailActivityData(cpVo.id, cpVo.pcID))
}*/
}
private fun openYesNoDialog(cpVo: CheckingPointVo, ce: ChartEntryVo) {
val builder = AlertDialog.Builder(context)
val dataSourceArray = BkpUiUtil.getYesNoArray()
val date = getDateFromId(ce.dateKey)
val title: String = getDayText(date, 1) + " - " + cpVo.name
builder.setTitle(title)
builder.setSingleChoiceItems(
dataSourceArray, BkpUiUtil.getYesNoIndex(ce.value.toString())
) { dialog, which ->
ce.value = BkpUiUtil.getYesNoId(which)
activityData.invoke(
DetailActivityData(
cpvo = cpVo,
cevo = ce,updateChart = true
))
dialog.dismiss()
}
val d = builder.create()
d.show()
}
override fun getItemCount() = list.size
}
data class DetailActivityData(var cpID: Long = 0, var pcID: Long = 0, var cpvo:CheckingPointVo = CheckingPointVo(), var cevo:ChartEntryVo= ChartEntryVo(), var updateChart : Boolean = false)
data class TabType1(
val header: Header = Header(""), val cps: CheckingPointVoList = CheckingPointVoList(
cps = listOf(
CheckingPointVo()
)
)
)
This is how the adapter is given the data.
val data: ArrayList<TabType1> = arrayListOf(
TabType1(
header = Header("Group One")
),
TabType1(
cps = CheckingPointVoList(it)
),
TabType1(
header = Header("Group Two")
),
TabType1(
cps = CheckingPointVoList(it)
)
)
if(it1.updateChart){
vm.updateChartEntry(it1.cpvo,it1.cevo)
}else{
Intent(requireContext(), CpDetailActivity::class.java).apply {
putExtra("cprId", it1.cpID)
putExtra("pcId", it1.pcID)
requireActivity().startActivity(this)
}
}
}, arraylist,dayslist)
This is the output that I get.
What am I missing?
An Adapter should adapt ONE list of data, not multiple lists nested within others. Meaning you need an Adapter and RecyclerView for each list.
If you want to do that you need to add an Adapter/RecyclerView to DVH and display your CheckingPoint list in there.
look here ->
override fun getItemCount() = list.size
Its only going to bind 4 times because you only gave it 4 items.
And here you are just rebinding the same ViewHolder, you need to pass in the data to the ViewHolder and have it display the list of "cps"
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val data: TabType1 = list[position]
if (holder is DVH) {
holder.bind(data) //HERE
}else{
Timber.i("is: header")
holder as TitleHolder
holder.bind(data.header)
}
}
inner class DVH(binding: DailyFragRecyclerItemBinding) : RecyclerView.ViewHolder(binding.root) {
private var checkingPointVoRecyclerView: RecyclerView = binding.recyclerVew
private var checkingPointVoAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder> = CheckingPointVoAdapter(dates)
checkingPointVoRecyclerView.adapter = checkingPointVoAdapter
fun bind(data: TabType1) {
checkingPointVoAdapter.list = data.cps
checkingPointVoAdapter.notifyDataSetChanged()
}
}
class CheckingPointVoAdapter(
private val dates: List<Date>,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){
public var list: List<CheckingPointVo> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
context = parent.context
val layoutInflater = LayoutInflater.from(context)
return ViewHolder(CheckingPointRecyclerItemBinding.inflate(layoutInflater, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val cpv: CheckingPointVo = list[position]
holder as ViewHolder
holder.bind(cpv)
}
inner class ViewHolder(binding: CheckingPointRecyclerItemBinding) : RecyclerView.ViewHolder(binding.root) {
private var cpname: TextView = binding.cpName
private var day1: FrameLayout = binding.frameLayout
private var day2: FrameLayout = binding.frameLayout2
private var day3: FrameLayout = binding.frameLayout3
private var day4: FrameLayout = binding.frameLayout4
private var dayContainer: ArrayList<FrameLayout> = ArrayList()
fun bind(cpVo: CheckingPointVo) {
dayContainer.addAll(arrayListOf(day1, day2, day3, day4))
cpname.apply {
text = cpVo.name
setOnClickListener {
activityData.invoke(DetailActivityData(cpVo.id, cpVo.pcID))
}
}
val map = cpVo.chartEntriesMap
for (i in dates.indices) {
val dateID = BkpDateUtil.getDateId(dates[i])
Timber.i("dateID $dateID")
var ceVo: List<ChartEntryVo>? = map[dateID]
if (ceVo == null) {
ceVo = ArrayList<ChartEntryVo>()
ceVo.add(ChartEntryVo(0, dateID, 0L, 0L, "", false))
}
val entryValue = ceVo[0].value
when (cpVo.cpType) {
CheckingPointType.YES_NO -> {
val fl: FrameLayout = dayContainer[i]
fl.setOnClickListener {
openYesNoDialog(cpVo, ceVo[0])
}
if (entryValue == 1L) {
setIconInChartEntry(fl, R.drawable.ic_tick, cpVo)
} else {
setIconInChartEntry(fl, R.drawable.ic_no_entry, cpVo)
}
}
CheckingPointType.QUANTIFIABLE -> {
}
CheckingPointType.COUNTABLE -> {
}
CheckingPointType.CATEGORY -> {
}
CheckingPointType.TEXT -> {
}
}
}
dayContainer.clear()
}
}
}
I did not test this but this is like 90% of what you need.
OnBindViewHolder gives you a view or you can imagine it as a row
, then you bind data to that row (setting text, etc..)
let's look at what you actually did in OnBindViewHolder
data.cps.cps.forEach { // you bind the data to the same row multiple times
Timber.i("is: a row")
holder.bind(it)
}
But there is only one row.
To solve this you need to pass a list of header items and row items only
sealed class DataItem {
abstract val id: Long
data class RowItem(val row: Row) : DataItem() {
override val id = row.id
}
data class HeaderItem(val header: Header) : DataItem() {
override val id = header.id
}
}
The Transform your row and header data to the DataItem Like this
val rowItems: List<DataItem> = rowList.map { DataItem.RowItem(it) }
val headertems: List<DataItem> = headerList.map { DataItem.BankItem(it) }
val items = rowItems+ headertems
//then pass the items to the adapter
So your adapter now will create a new ViewHoler to each item in the list and you can check for it as
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ITEM_VIEW_TYPE_ITEM_ROW -> DVH(DailyFragRecyclerItemBinding.inflate(layoutInflater, parent, false))
ITEM_VIEW_TYPE_ITEM_HEADER -> TitleHolder(DailyTableHeaderBinding.inflate(layoutInflater, parent, false))
else -> throw ClassCastException("Unknown viewType $viewType")
}
}
then bind data to each view as
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is DVH-> {
val item = getItem(position) as DataItem.RowItem
holder.bind(item)
}
is TitleHolder -> holder.bind(item)
}
}
Finally you need to adjust getItemViewType
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is DataItem.RowItem -> ITEM_VIEW_TYPE_ITEM_ROW
is DataItem.HeaderItem -> ITEM_VIEW_TYPE_ITEM_HEADER
}
}
I have a RecyclerView that has many ViewHolder types. One of those ViewHolder (GoalCardViewHolder) holds a View that is just a container for displaying a Fragment. In my chat app this ViewHolder is used by two different ViewTypes, this is for UserInput and ChatItem view types.
The UserInput type represents when the user needs to act.
User Input
The ChatItemType represent any of the other elements in the Chat, when the user inputs a new goal, a ChatItemType.GoalCard is created: ChatItem.GoalCard
Note that both types use the same ViewHolder.
The problem
When the user is trying to create a new Goal , I expect a new UserInput to be created at the bottom of the chat. However, the previous GoalCardViewHolder is being re-used(this happens a few times, until the list grows long enough that the ViewHolder gets recycled, when that happens the new view is added at the bottom as expected).
The source code
Apologies for the lengthy pasting but the Adapter and MainChatFragment are very complex (please do let me know if you need anything else):
MainChatFragment.kt
class MainChatFragment(
private val viewModel: MainChatViewModel,
private val chatAdapter: ChatAdapter,
private val chatToolbar: ChatToolbar,
private val chatDaySelector: ChatDaySelector,
private val dayOfStudyProvider: DayOfStudyProvider,
private val subjectMenuFragment: SubjectMenuFragment,
private val chatSpeedHelper: ChatSpeedHelper,
) : Fragment() {
private lateinit var dayOfStudySelectorView: DayOfStudySelectorView
private lateinit var binding: FragmentChatBinding
private var scrollDistance: Int = 0
private val cancelOnPauseScope = CancelOnPauseCoroutineScope(this)
private val bufferedChatItems = mutableListOf<IChatItem>()
private val arguments: MainChatFragmentArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentChatBinding.inflate(inflater)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
dayOfStudySelectorView = binding.dashboardView
dayOfStudySelectorView.init(dayOfStudyProvider, chatDaySelector)
setupChatItemList()
subjectMenuFragment.closeMenuCallback = { shouldDiscardItems ->
if (!shouldDiscardItems) {
bufferedChatItems.forEach {
addChatItem(it)
}
}
bufferedChatItems.clear()
}
chatAdapter.removeLastUserInput = this::removeLastUserInput
chatAdapter.subjectMenuButtonOnClick = this::openSubjectMenuFragment
chatToolbar.setMenuButtonOnClickListener {
openSubjectMenuFragment()
}
}
private fun removeLastUserInput() {
viewModel.removeLastUserInputAndLogMessage()
}
private fun openSubjectMenuFragment() {
if (!subjectMenuFragment.isAdded) {
subjectMenuFragment.show(requireActivity().supportFragmentManager, "subjectMenu")
}
}
override fun onResume() {
super.onResume()
observeChatItemActions() // Should be last entry in here
}
#Suppress("IMPLICIT_CAST_TO_ANY")
private fun observeChatItemActions() {
cancelOnPauseScope.launch {
viewModel.chatItemActionFlow()
.onStart { viewModel.onUserEnteredChat(arguments.hasJustActivated) }
.transform {
if (it is ChatItemAction.AddChatItemAction) {
val delayMs = chatSpeedHelper.itemDelay(it.chatItem)
if (delayMs > 0) {
delay(delayMs)
}
if (subjectMenuFragment.isVisible) {
bufferedChatItems.add(it.chatItem)
return#transform
}
}
emit(it)
}
.collect {
if (!isVisible) {
return#collect
}
when (it) {
is ChatItemAction.AddChatItemAction -> {
addChatItem(it.chatItem)
}
is ChatItemAction.SetChatItemsAction -> {
chatAdapter.showWaitingBubble = false
binding.emptyChatText.isVisible =
it.chatItems.isEmpty() && !it.chatEnabled
chatAdapter.items = it.chatItems.map { item -> ChatViewItem(item) }
}
is ChatItemAction.RemoveAllItemsAction -> {
chatAdapter.removeAllItems()
}
is ChatItemAction.RemoveLastItemAction -> {
chatAdapter.conditionallyRemoveLastChatItem(it.predicate)
}
}.let { } // Make when exhaustive
}
}
}
private fun addChatItem(chatItem: IChatItem) {
if (chatItem is ChatItem) {
chatItem.chatBackgroundAction?.let { chatBackgroundAction ->
viewModel.launchChatBackgroundAction(chatBackgroundAction)
}
}
val chatViewItem = ChatViewItem(chatItem)
chatAdapter.showWaitingBubble = chatViewItem.shouldShowPendingBubble()
chatAdapter.addChatItem(chatViewItem)
binding.emptyChatText.isVisible = false
}
private fun setupChatItemList() {
val linearLayoutManager = LinearLayoutManager(requireView().context)
linearLayoutManager.stackFromEnd = true
with(binding.chatList) {
layoutManager = linearLayoutManager
adapter = chatAdapter
addItemDecoration(
DividerItemDecoration(
requireView().context,
linearLayoutManager.orientation
).apply {
setDrawable(
ContextCompat.getDrawable(
requireView().context,
R.drawable.divider_chat
)!!
)
})
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
scrollDistance += dy
if (dy < 0 &&
(scrollDistance.absoluteValue >= measuredHeight || !binding.chatList.canScrollVertically(
-1
))
) {
dayOfStudySelectorView.revealDaySelector()
} else if (dy > 0) {
dayOfStudySelectorView.hideDaySelector()
}
}
})
}
}
}
ChatAdapter.kt
class ChatAdapter(
private val widgetHandler: WidgetHandler,
private val coroutineScope: CoroutineScope,
) : RecyclerView.Adapter<BaseChatItemViewHolder>() {
private lateinit var context: Context
private lateinit var layoutInflater: LayoutInflater
private var recyclerView: RecyclerView? = null
private val lastDivider = ChatViewItem(ChatItemType.LastDivider)
private val pendingMessage = ChatViewItem(ChatItemType.PendingMessage)
private val staticPendingView = false
var showWaitingBubble: Boolean = true
set(value) {
if (value != showWaitingBubble) {
setShowingWaitingBubble(value)
}
field = value
}
var removeLastUserInput: () -> Unit = {}
var subjectMenuButtonOnClick: () -> Unit = {}
lateinit var onGoalSettingStarted: (GoalCardProgressListener) -> Unit
private fun setShowingWaitingBubble(value: Boolean): Int {
var updatedIndex = -1
if (_items.isNotEmpty()) {
if (!value) {
_items.lastIndexOf(pendingMessage)
.takeIf { it >= 0 }
?.let {
_items.removeAt(it)
updatedIndex = it
}
} else {
if (!hasPendingBubble() && shouldShowPendingBubble(getLastRealChatItem().second)) {
val divider = hasLastDivider()
updatedIndex = Math.max(0, _items.size - divider.toBitInteger())
_items.add(updatedIndex, pendingMessage)
}
}
}
if (updatedIndex != -1) {
scrollToBottom()
}
return updatedIndex
}
private var _items = ArrayList<ChatViewItem>()
var items: List<ChatViewItem>
get() = _items
set(value) {
_items.clear()
_items.addAll(value)
if (value.isNotEmpty()) {
setShowingWaitingBubble(showWaitingBubble)
}
_items.add(lastDivider)
notifyDataSetChanged()
scrollToBottom()
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
this.recyclerView = recyclerView
context = recyclerView.context
layoutInflater = LayoutInflater.from(recyclerView.context)
recyclerView.setItemViewCacheSize(20)
}
override fun getItemCount(): Int = items.size
override fun getItemViewType(position: Int): Int {
val chatViewItem = items[position]
return when (chatViewItem.type) {
ChatItemType.UserInput, ChatItemType.DisabledUserInput -> {
val inputType = chatViewItem.inputType ?: npe("chatViewItem.inputType")
when (inputType) {
is InputType.GoalSettingCard -> R.layout.goal_card
else -> -1 // deleted other viewTypes for stackOverflow
}
}
ChatItemType.GoalCard -> R.layout.goal_card
else -> -1 // deleted other viewTypes for stackOverflow
}
}
#SuppressLint("SetJavaScriptEnabled")
override fun onBindViewHolder(holder: BaseChatItemViewHolder, position: Int) {
val chatViewItem = items[position]
when (chatViewItem.type) {
ChatItemType.UserInput, ChatItemType.DisabledUserInput -> {
val inputType = chatViewItem.inputType ?: npe("chatViewItem.inputType")
when (inputType) {
is InputType.GoalSettingCard -> {
val goalCardFragment = GoalCardFragment()
goalCardFragment.initialProgress =
GoalBuilder() // Fixme use goal builder from topic interactor
onGoalSettingStarted.invoke(goalCardFragment)
goalCardFragment.arguments =
bundleOf(GoalCardFragment.FLOW_TYPE_EXTRA to inputType.flowType)
goalCardFragment.onCanceledCallback =
(holder as GoalCardViewHolder).canceledListener
goalCardFragment.onCompletedCallback = holder.completedListener
if (inputType.flowType == FlowType.COMPLETE_GOAL) {
context.activity().supportFragmentManager
.beginTransaction()
.replace(
holder.binding.goalCardContainer.id,
goalCardFragment,
GOAL_CARD_FRAGMENT_AS_USER_INPUT
)
.commit()
} else {
TODO("Need implementation")
}
}
}
}
ChatItemType.GoalCard -> {
gson.fromJson(chatViewItem.text, GoalCardParameters::class.java)
GoalCardFragment().let {
if (chatViewItem.data is GoalSettingData) {
it.initialProgress = (chatViewItem.data as GoalSettingData).goalBuilder
}
context.activity().supportFragmentManager.beginTransaction()
.replace(
(holder as GoalCardViewHolder).binding.goalCardContainer.id,
it as GoalCardFragment,
GOAL_CARD_FRAGMENT_AS_CHAT_ITEM
)
.commit()
}
}
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
#LayoutRes viewType: Int
): BaseChatItemViewHolder {
val itemView = layoutInflater.inflate(viewType, parent, false)
return when (viewType) {
R.layout.goal_card -> {
GoalCardViewHolder(itemView).apply {
setOnCanceledListener {
coroutineScope.launch {
removeLastUserInput()
removeFragmentByTag(GOAL_CARD_FRAGMENT_AS_USER_INPUT)
widgetHandler.onWidgetDone(
userResponse = UserResponse(
WIDGET_DISMISSED_WITH_VALUE + "cancelled"
)
)
}
}
setOnCompletedListener {
coroutineScope.launch {
val goalData = UserResponseData.GoalData(it)
removeLastUserInput()
removeFragmentByTag(GOAL_CARD_FRAGMENT_AS_USER_INPUT)
widgetHandler.onWidgetDone(
userResponse = UserResponse(
WIDGET_DISMISSED_WITH_VALUE + "completed", data = goalData
)
)
// TODO api
}
}
}
}
else -> throw IllegalStateException("View type not mapped to a view holder")
}
}
private fun removeFragmentByTag(tag: String) {
context.activity().supportFragmentManager.apply {
findFragmentByTag(tag)?.let {
beginTransaction()
.remove(it)
.commit()
}
}
}
fun addChatItem(chatItemView: ChatViewItem) {
val shouldShowPendingBubble = shouldShowPendingBubble(chatItemView)
setShowingWaitingBubble(shouldShowPendingBubble)
val hasPendingBubble = hasPendingBubble()
val insertIndex = Math.max(
0,
itemCount - hasLastDivider().toBitInteger() - hasPendingBubble.toBitInteger()
)
_items.add(insertIndex, chatItemView)
// sending notifyItemRangeChanged instead of notifyItemInserted is for a feeling of
// scrolling also the pendingBubble, otherwise it looks like it's static view and it's not part of scrolling content
if (staticPendingView && shouldShowPendingBubble) {
notifyItemInserted(insertIndex)
} else {
notifyItemRangeChanged(insertIndex, 1 + hasPendingBubble.toBitInteger())
}
when (chatItemView.inputType) {
is InputType.FreeText,
is InputType.FreeTextWithLimit ->
// for some reason RecyclerView scrolls up little bit when EditText is shown
// basically aligns to bottom of line, not bottom of EditText =>
// let RV do this nonsense and scroll down little bit later
// however we have to do it sooner, we try to show the keyboard then
recyclerView?.postDelayed(UiConst.keyboardHandlingDelay / 2) {
scrollToBottom()
}
else -> scrollToBottom()
}
}
override fun onViewAttachedToWindow(holder: BaseChatItemViewHolder) {
super.onViewAttachedToWindow(holder)
holder.onAttached()
}
override fun onViewDetachedFromWindow(holder: BaseChatItemViewHolder) {
super.onViewDetachedFromWindow(holder)
holder.onDetached()
}
fun removeAllItems() {
_items.clear()
notifyDataSetChanged()
}
fun notifyItemChanged(chatViewItem: ChatViewItem) {
notifyItemChanged(items.indexOf(chatViewItem))
}
fun conditionallyRemoveLastChatItem(predicate: (ChatViewItem) -> Boolean) {
val (index, item) = getLastRealChatItem()
if (item != null && predicate(item)) {
_items.removeAt(index)
notifyItemRemoved(index)
}
}
private fun hasLastDivider(): Boolean = _items.lastOrNull()?.type == ChatItemType.LastDivider
private fun hasPendingBubble(): Boolean {
return (items.lastOrNull(1)?.type == ChatItemType.PendingMessage) ||
(items.lastOrNull(0)?.type == ChatItemType.PendingMessage)
}
private fun getLastRealChatItem(): Pair<Int, ChatViewItem?> {
if (!items.isEmpty()) {
(items.size - 1 downTo 0).forEach { i ->
val item = items[i]
when (item.type) {
ChatItemType.LastDivider,
ChatItemType.PendingMessage -> {
/*ignore*/
}
else -> return Pair(i, item)
}
}
}
return Pair(-1, null)
}
private val skipPendingBubbleFor = listOf(
ChatItemType.UserInput,
ChatItemType.LastBotMessage
)
private fun shouldShowPendingBubble(chatItemView: ChatViewItem?): Boolean {
return showWaitingBubble && !skipPendingBubbleFor.contains(chatItemView?.type)
}
private fun scrollToBottom() {
(recyclerView?.layoutManager as? LinearLayoutManager)?.scrollToPosition(itemCount - 1)
}
companion object {
const val GOAL_CARD_FRAGMENT_AS_USER_INPUT = "goal-card-fragment-user-input"
const val GOAL_CARD_FRAGMENT_AS_CHAT_ITEM = "goal-card-fragment-chat-item"
}
}
abstract class BaseChatItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
open fun onAttached() {}
open fun onDetached() {}
open fun bind(chatViewItem: ChatViewItem) {}
abstract val binding: ViewBinding?
}
abstract class BaseTextChatItemViewHolder(itemView: View) : BaseChatItemViewHolder(itemView) {
abstract val chatText: TextView
}
class GoalCardViewHolder(itemView: View) : BaseChatItemViewHolder(itemView) {
lateinit var canceledListener: (() -> Unit)
lateinit var completedListener: ((GoalBuilder) -> Unit)
fun setOnCanceledListener(canceledListener: () -> Unit) {
this.canceledListener = canceledListener
}
fun setOnCompletedListener(completedListener: (GoalBuilder) -> Unit) {
this.completedListener = completedListener
}
override val binding: GoalCardBinding = GoalCardBinding.bind(itemView)
}
goal_card.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/goal_card_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The issue and lesson learned was 'never use a Fragment in a RecyclerView.ViewHolders
I converted my fragment to a custom view and the problem is no longer happening.
I think it must have been an issue with the Fragment's lifecycle.
I have set up a fragment with a recyclerView in it and I fetch data from firestore successfully. What I want to know is that if it is possible to add items at a certain position in recyclerView. Suppose, I want to add an item (from a different collection in Firestore) after every 5 items in a recyclervView. Is it possible to do it in Android using Kotlin?
Thank you.
Edit:
DashboardFragment.kt
class DashboardFragment : BaseFragment() {
var srchProductsList: ArrayList<Product> = ArrayList()
var adList: ArrayList<Ads> = ArrayList()
var srchTempProductsList: ArrayList<Product> = ArrayList()
var newView: String = "ListView"
private lateinit var binding: FragmentDashboardBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentDashboardBinding.inflate(inflater, container, false)
binding.fbDashboard.setImageResource(R.drawable.ic_grid_view)
binding.fbDashboard.setOnClickListener {
if (newView=="ListView"){
newView="GridView"
fb_dashboard.setImageResource(R.drawable.ic_list_view)
}else{
newView="ListView"
fb_dashboard.setImageResource(R.drawable.ic_grid_view)
}
onResume()
}
return binding.root
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
when (id) {
R.id.action_settings -> {
startActivity(Intent(activity, SettingsActivity::class.java))
return true
}
R.id.action_cart -> {
startActivity(Intent(activity, CartListActivity::class.java))
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onResume() {
super.onResume()
srchProductsList.clear()
srchTempProductsList.clear()
getDashboardItemsList()
}
private fun getDashboardItemsList() {
showProgressDialog(resources.getString(R.string.please_wait))
getDashboardItemsList2()
}
fun successDashboardItemsList(dashboardItemsList: ArrayList<Product>) {
val adsLists =getListOfAds()
hideProgressDialog()
if (dashboardItemsList.size > 0) {
Toast.makeText(
context,
"Total " + dashboardItemsList.size + " products loaded",
Toast.LENGTH_LONG
).show()
rv_dashboard_items.visibility = View.VISIBLE
tv_no_dashboard_items_found.visibility = View.GONE
rv_dashboard_items.layoutManager =
StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
rv_dashboard_items.setHasFixedSize(true)
val adapter = DashboardItemsListAdapterTest(requireActivity(), dashboardItemsList,adsLists)
rv_dashboard_items.adapter = adapter
//////// I HAVE PROBLEM WITH THE FOLLOWING BLOCK OF CODE WHICH IS WHY I HAVE COMMENTED IT OUT ONLY TO CHECK IF OTHER PART OF THE CODE IS WORKING. I NEED TO FIX THE ERROR FOR THE BELOW BLOCK OF CODE ALSO
/* adapter.setOnClickListener(object :
DashboardItemsListAdapter.OnClickListener {
override fun onClick(position: Int, product: Product) {
val intent = Intent(context, ProductDetailsActivity::class.java)
intent.putExtra(Constants.EXTRA_PRODUCT_ID, product.product_id)
intent.putExtra(Constants.EXTRA_PRODUCT_OWNER_ID, product.user_id)
startActivity(intent)
}
})*/
} else {
rv_dashboard_items.visibility = View.GONE
tv_no_dashboard_items_found.visibility = View.VISIBLE
}
}
fun successDashboardItemsListListView(dashboardItemsList: ArrayList<Product>) {
val adsLists =getListOfAds()
hideProgressDialog()
if (dashboardItemsList.size > 0) {
Toast.makeText(
context,
"Total " + dashboardItemsList.size + " products loaded",
Toast.LENGTH_LONG
).show()
rv_dashboard_items.visibility = View.VISIBLE
tv_no_dashboard_items_found.visibility = View.GONE
rv_dashboard_items.layoutManager =
LinearLayoutManager(context)
rv_dashboard_items.setHasFixedSize(true)
val adapter = DashboardItemsListAdapterTest(requireActivity(), dashboardItemsList,adsLists)
rv_dashboard_items.adapter = adapter
//////// I HAVE PROBLEM WITH THE FOLLOWING BLOCK OF CODE WHICH IS WHY I HAVE COMMENTED IT OUT ONLY TO CHECK IF OTHER PART OF THE CODE IS WORKING. I NEED TO FIX THE ERROR FOR THE BELOW BLOCK OF CODE ALSO
/* adapter.setOnClickListener(object :
DashboardItemsListAdapter.OnClickListener {
override fun onClick(position: Int, product: Product) {
val intent = Intent(context, ProductDetailsActivity::class.java)
intent.putExtra(Constants.EXTRA_PRODUCT_ID, product.product_id)
intent.putExtra(Constants.EXTRA_PRODUCT_OWNER_ID, product.user_id)
startActivity(intent)
}
})*/
} else {
rv_dashboard_items.visibility = View.GONE
tv_no_dashboard_items_found.visibility = View.VISIBLE
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.dashboard_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
val item = menu.findItem(R.id.my_search_bar)
val searchView = item?.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
srchTempProductsList.clear()
val searchKey = query
if (searchKey != null) {
if (searchKey.isNotEmpty()) {
srchProductsList.forEach {
if (it.description.toLowerCase(Locale.getDefault())
.contains(searchKey)
) {
srchTempProductsList.add(it)
}
}
rv_dashboard_items.adapter!!.notifyDataSetChanged()
} else {
srchTempProductsList.clear()
srchTempProductsList.addAll(srchProductsList)
rv_dashboard_items.adapter!!.notifyDataSetChanged()
}
}
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
srchTempProductsList.clear()
val searchText = newText!!.toLowerCase(Locale.getDefault())
if (searchText.isNotEmpty()) {
srchProductsList.forEach {
if (it.description.toLowerCase(Locale.getDefault()).contains(searchText)) {
srchTempProductsList.add(it)
}
}
rv_dashboard_items.adapter!!.notifyDataSetChanged()
} else {
srchTempProductsList.clear()
srchTempProductsList.addAll(srchProductsList)
rv_dashboard_items.adapter!!.notifyDataSetChanged()
}
return false
}
})
}
private fun getDashboardItemsList2() {
val mFireStore = FirebaseFirestore.getInstance()
mFireStore.collection(Constants.PRODUCTS)
.get()
.addOnSuccessListener { document ->
for (i in document.documents) {
val product = i.toObject(Product::class.java)!!
product.product_id = i.id
srchProductsList.add(product)
}
srchTempProductsList.addAll(srchProductsList)
if (newView == "ListView") {
successDashboardItemsListListView(srchTempProductsList)
} else {
successDashboardItemsList(srchTempProductsList)
}
}
.addOnFailureListener {
}
}
private fun getListOfAds() : ArrayList<Ads>{
val mFireStore = FirebaseFirestore.getInstance()
mFireStore.collection("ads")
.get()
.addOnSuccessListener { document ->
for (i in document.documents) {
val ad = i.toObject(Ads::class.java)!!
ad.ad_id = i.id
adList.add(ad)
}
}
.addOnFailureListener {
}
return adList
}
}
DashboardItemListAdapterTest.kt
open class DashboardItemsListAdapterTest(
private val context: Context,
private var prodlist: ArrayList<Product>,
private var adslist: ArrayList<Ads>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val produc= 1
const val ads= 2
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == produc) {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_dashboard_list_view_layout, parent, false)
Collection1Holder(view)
} else {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_dashboard_ad_view_layout, parent, false)
Collection2Holder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val promodel = prodlist[position]
val adsmodel = adslist[position]
if(getItemViewType(position) == produc) {
holder.itemView.tv_item_name.text = promodel.title
}else{
holder.itemView.tv_item_name.text = adsmodel.title
}
}
override fun getItemCount(): Int {
return prodlist.size + adslist.size
}
override fun getItemViewType(position: Int): Int {
return if(position%5 == 0) ads else produc
}
inner class Collection1Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
inner class Collection2Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
}
Model class
Ads.kt
data class Ads(
val title: String = "",
var ad_id: String = ""
)
You can use two viewholder for two different collections of data.
Change your adapter class like this.
class YourAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val COLLCTION1= 1
const val COLLCTION2= 2
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == COLLCTION1) {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.collection1, viewGroup, false)
Collection1Holder(view)
} else {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.collection2, viewGroup, false)
Collection2Holder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if(getItemViewType(position) == COLLCTION1) {
holder.name.text = collection1.text
}else{
holder.name.text = collection2.text
}
}
override fun getItemCount(): Int {
return collection1.size + collection2.size
}
override fun getItemViewType(position: Int): Int {
return if(position%5 == 0) COLLCTION2 else COLLCTION1
}
inner class Collection1Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
inner class Collection2Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
}
There are two Recyclerview in same activity rv1 and rv2. Selected item of rv1 will show in rv2
rv1:
Here I select multiple items
rv2:
it should shows like this
Adapter class of rv1:
class ServiceJobAdapter(val context: Context, var list:ArrayList<JobRespo>,var listener:OnItemClick):
RecyclerView.Adapter<ServiceJobAdapter.ItemsCarrier>(){
var currentSelectedIndex= -1
var selectedItem = SparseBooleanArray()
var animationItemsIndex = SparseBooleanArray()
private val reverseAllAnimations = false
var holder:ItemsCarrier? =null
class ItemsCarrier(itemView: View) : RecyclerView.ViewHolder(itemView)
{
var jobName = itemView.findViewById<TextView>(R.id.job_name)
var jobPrice = itemView.findViewById<TextView>(R.id.job_price)
var iconBack = itemView.findViewById(R.id.icon_back) as RelativeLayout
var iconFront = itemView.findViewById(R.id.icon_front) as RelativeLayout
var iconContainer = itemView.findViewById(R.id.icon_container) as CardView
fun binde(jobRespo: JobRespo) {
jobName.text = jobRespo.repDesc
jobPrice.text="₹"+jobRespo.repRice
if (iconFront.visibility == View.VISIBLE)
{
jobRespo.setChecked(false)
}else{
jobRespo.setChecked(true)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemsCarrier {
val view = LayoutInflater.from(context).inflate(R.layout.job_row,parent,false)
return ItemsCarrier(view)
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: ItemsCarrier, position: Int) {
this.holder = holder
holder.itemView.isActivated = selectedItem[position,false]
holder.binde(list[position])
applyIconAnimation(holder, position)
// apply click events
applyClickEvents(holder, position)
}
private fun applyClickEvents(holder: ItemsCarrier, position: Int) {
holder.iconContainer.setOnClickListener {
listener.onIconClicked(position)
listener.AllItem(list[position]) }
}
private fun applyIconAnimation(holder: ItemsCarrier, position: Int)
{
if (selectedItem.get(position, false)) {
holder.iconFront!!.visibility = View.GONE
resetIconYAxis(holder.iconBack)
holder.iconBack.visibility = View.VISIBLE
holder.iconBack.alpha = 1f
if (currentSelectedIndex == position) {
FlipAnimator.flipView(context, holder.iconBack, holder.iconFront, true)
resetCurrentIndex()
}
} else {
holder.iconBack.visibility = View.GONE
resetIconYAxis(holder.iconFront)
holder.iconFront.visibility = View.VISIBLE
holder.iconFront.alpha = 1f
if (reverseAllAnimations && animationItemsIndex.get(
position,
false
) || currentSelectedIndex == position
) {
FlipAnimator.flipView(context, holder.iconBack, holder.iconFront, false)
resetCurrentIndex() } } }
private fun resetIconYAxis(view: RelativeLayout?)
{
if (view!!.rotationY != 0f) {
view.rotationY = 0f
}
}
private fun resetCurrentIndex() {
currentSelectedIndex = -1
}
fun toggleSelection(pos: Int) {
currentSelectedIndex = pos
if (selectedItem.get(pos, false)) {
selectedItem.delete(pos)
animationItemsIndex.delete(pos)
} else {
selectedItem.put(pos, true)
animationItemsIndex.put(pos, true)
}
notifyItemChanged(pos)
}
fun getSelectedItems(): ArrayList<JobRespo>
{
val selectItems = ArrayList<JobRespo>()
for(item in list)
{
if (!item.isChecked())
{
selectItems.add(item)
}
}
return selectItems}}
Using Interface
interface OnItemClick{
fun onIconClicked(position: Int)
fun AllItem(jobs:JobRespo)}
Implement interface in Activity
override fun onIconClicked(position: Int) {
mAdapter!!.toggleSelection(position)
}
override fun AllItem(jobs: JobRespo) {
val selectedItems:ArrayList<JobRespo>
=mAdapter!!.getSelectedItems()
selectIte.addAll(selectedItems)
}
Adapter class of 2nd Recyclerview:
lass SelectItemAdapter(val context: Context, var list:List<JobRespo>): RecyclerView.Adapter<SelectItemAdapter.ItemsCarriers>(){
class ItemsCarriers(itemView: View) : RecyclerView.ViewHolder(itemView) {
var itemName = itemView.findViewById<TextView>(R.id.item_name)
var itemPrice= itemView.findViewById<TextView>(R.id.item_peice)
var itmDelet =itemView.findViewById<LinearLayout>(R.id.item_delete)
fun bind(job: JobRespo) {
itemName.text = job.repDesc
itemPrice.text = job.repRice }
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemsCarriers {
val root = LayoutInflater.from(context).inflate(R.layout.select_item_row,parent,false)
return ItemsCarriers(root)
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: ItemsCarriers, position: Int) {
holder.bind(list[position])}}
Now how can i pass multiselected data from one recyclerview to anathor recyclerview of same activity?
Thanks your consideration and guidded to me :)
You can create a method in a second adapter to update the selected value like below and use it when you getting selected items.
fun setSelectedData(data:ArrayList<JobRespo>){
this.list.clear()
this.list.addAll(data)
notifyDataSetChanged ();
}