How to Read Data from RecyclerView and send in BottomSheet - android

I am using recyclerView to show list of apps installed in device
image - Link
and for more details I use bottomSheet on LongPress i.e. in ViewHolder Class, but How to send data of selected tab to bottomSheet with more details(such as package name, API level etc.)...for reference see images
I want - Link
I get from below coding - Link
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.adapter = Adapter // I set adapter here with function getApps()
recyclerView.layoutManager = LinearLayoutManager(this)
private fun getApps(): List<DataClass> {
// here I get apps icon,name,size and return list<DataClass>
return list
}
Adapter.kt
class Adapter(private val listOfApps: List<AppData>) :
RecyclerView.Adapter<Adapter.ViewHolder>() {
class ViewHolder(appView: View) : RecyclerView.ViewHolder(appView), View.OnClickListener,
View.OnLongClickListener {
init {
appView.setOnClickListener(this)
appView.setOnLongClickListener(this)
}
val icon: ImageView = appView.App_icon
val name: TextView = appView.App_name
val size: TextView = appView.App_size
override fun onClick(v: View?) {
Toast.makeText(v?.context, "OnClick", Toast.LENGTH_SHORT).show()
}
override fun onLongClick(v: View?): Boolean {
// I want here on Long press BottomSheet appears with details
val bottomSheetDialog = BottomSheetDialog()
// Show bottomSheet on LongPress
bottomSheetDialog.show(
(v?.context as FragmentActivity).supportFragmentManager, bottomSheetDialog.tag
)
return true
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.list_apps, parent, false
)
return ViewHolder(view)
}
override fun getItemCount() = listOfApps.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = listOfApps[position]
holder.icon.setImageDrawable(currentItem.icon)
holder.name.text = currentItem.name
holder.size.text = currentItem.size
}
}
BottomSheetDialog.kt
class BottomSheetDialog: BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
DataClass
data class AppData(
val icon: Drawable,
val name: String,
val size: String,
)

With your current code the easiest solution would be:
Modify your BottomSheetDialog to include AppData in the constructor
class BottomSheetDialog(val appData: AppData): BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
Add onBind method inside your ViewHolder class:
fun onBind(appData: AppData) {
icon.setImageDrawable(currentItem.icon)
name.text = currentItem.name
size.text = currentItem.size
}
Modify your onBindViewHolder method inside the Adapter to call that onBind method:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.onBind(listOfApps[position])
}
Inside your ViewHolder add lateinit var currentItem: AppData that will be set inside onBind and we can use it in onLongClick:
class ViewHolder(appView: View) : RecyclerView.ViewHolder(appView), View.OnClickListener,
View.OnLongClickListener {
.
.
.
override fun onLongClick(v: View?): Boolean {
// I want here on Long press BottomSheet appears with details
**val bottomSheetDialog = BottomSheetDialog(currentItem)**
// Show bottomSheet on LongPress
bottomSheetDialog.show(
(v?.context as FragmentActivity).supportFragmentManager, bottomSheetDialog.tag
)
return true
}
**private lateinit var currentItem: AppData**
fun onBind(appData: AppData) {
**currentItem = appData**
icon.setImageDrawable(currentItem.icon)
name.text = currentItem.name
size.text = currentItem.size
}

attach your currentItem list item to ViewHolder inside onBindViewHolder and inside onLongClick pass this data to freshly created BottomSheetDialog, which is extending Fragment (so you may use Bundle - setArgument method). then inside onCreateView set your data to Views

Create an interface
interface OnListItemClicked {
fun onClicked(data : AppData)
}
Implement that interface in BottomSheetDialog
class BottomSheetDialog: BottomSheetDialogFragment(), OnListItemClicked {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun onClicked(data: AppData) {
//show Details here
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
Pass extra callback to the adapter from main activity.
Adapter(private val listOfApps: List<AppData>, val onClick: (AppData) -> Unit)
Invoke the click listener from adapter with listOfAppData[position]
On MainActivity pass a function into the Adapter initialisation.
fun passDataToBottomSheet(data: AppData) {
val listener = OnListItemClicked()
listener.onClicked(data)
}
Adapter(list, ::passDataToBottomSheet)
That's it. It should work now.

Related

recyclerview not visible at run time

I implemented a simple recyclerview using databinding using kotlin language.
In xml, recyclerview seems to be the default,
but when I run it, it doesn't show up as default.
Other buttons and views included in the fragment appear normally when executed, but only the recyclerview is not visible. Which part is wrong?
class WorkFragment : Fragment() {
private var _binding: FragmentWorkBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWorkBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val data = mutableListOf<WorkList>()
val adapter = WorkAdapter()
adapter.data = data
binding.recycler1.adapter = adapter
binding.recycler1.layoutManager = LinearLayoutManager(requireContext())
}
}
class WorkAdapter : RecyclerView.Adapter<WorkAdapter.ViewHolder>() {
var data = mutableListOf<WorkList>()
class ViewHolder(val binding: RecyclerviewWorkItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(workList: WorkList) {
binding.tvStarttime.text = workList.Starttime
binding.tvAdmin.text = workList.Admin
binding.tvPart.text = workList.Part
binding.tvStoptime.text = workList.Stoptime
binding.tvWorkers.text = workList.Workers.toString()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkAdapter.ViewHolder {
val binding = RecyclerviewWorkItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: WorkAdapter.ViewHolder, position: Int) {
holder.bind(data[position])
}
override fun getItemCount(): Int {
return data.size
}
// fun replaceList(newList: MutableList<WorkList>) {
// data = newList.toMutableList()
// notifyDataSetChanged()
// }
}
Is there something wrong with my code?
For reference, I just used a fragment, not an activity.
its typically for the RecyclerView to behave like its hidden but actually its not. the reason is you are passing an empty list so the recycler will not be extended or show because there is no items in the list.

How to display items in RecyclerView on fragment with data from Firestore using firestoreui

I'm having a problem with making a Firestoreui show data from Firestore in RecycleView in a fragment. The app is supposed to show images in a RecycleView after I clicked on an ImageView to change from one RecycleView fragment to another RecycleView fragment but it does not show anything.
I know there are plenty of questions like this but I don't seem to find the correct solution to this problem.
Here's my fragment code
class PhotoGalleryFragment : Fragment() {
private var db: FirebaseFirestore=FirebaseFirestore.getInstance()
lateinit var binding: FragmentPhotoGalleryBinding
lateinit var adapter: GalleryAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_photo_gallery, container, false
)
//set-up recyclerview
setUpRecycler()
return binding.root
}
private fun setUpRecycler() {
val query = db.collection("photos")
val options = FirestoreRecyclerOptions.Builder<Photos>()
.setQuery(query, Photos::class.java)
.build()
adapter = GalleryAdapter(options)
binding.galleryRecyclerView.itemAnimator = DefaultItemAnimator()
binding.galleryRecyclerView.layoutManager = GridLayoutManager(activity, 2)
binding.galleryRecyclerView.adapter = adapter
}
override fun onStart() {
super.onStart()
adapter.startListening()
}
override fun onStop() {
super.onStop()
adapter.stopListening()
}
}
}
Here's my adapter class
class GalleryAdapter(options: FirestoreRecyclerOptions<Photos>) :
FirestoreRecyclerAdapter<Photos, GalleryAdapter.GalleryViewHolder>(options) {
class GalleryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var photo: ImageView = itemView.findViewById(R.id.photo_image)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GalleryViewHolder {
val itemView =
LayoutInflater.from(parent.context)
.inflate(R.layout.item_gallery, parent, false)
return GalleryViewHolder(itemView)
}
/**
* #param model the model object containing the data that should be used to populate the view.
* #see .onBindViewHolder
*/
override fun onBindViewHolder(holder: GalleryViewHolder, position: Int, model: Photos) {
Glide.with(holder.photo.context)
.load(model.imageUrl)
.placeholder(R.drawable.ic_image)
.error(R.drawable.ic_star)
.into(holder.photo)
//set up navigation
holder.itemView.setOnClickListener { view: View ->
view.findNavController().navigate(R.id.action_photoGalleryFragment_to_photoFragment)
}
}
}
Here's my model class
class Photos {
var imageUrl: String? = null
constructor(imageUrl: String?) {
this.imageUrl = imageUrl
}
constructor() {}
}
This the data structure from firestore

android - How to pass listener to an Adapter from a Fragment? (Kotlin)

Im currently working on an app which has a Settings Activity. In this Settings Activity there is a FrameLayout which loads a fragment containing a RecyclerView.
In the fragment, i have to pass to the RecyclerView adapter a listener, which is needed for the OnItemClick function.
I tried using context instead of this, but it doesnt work.
How do you correctly pass a listener to an Adapter?
Code:
FragmentSettings.kt
class FragmentSettingsMain : Fragment(), AdapterSettings.OnItemClickListener {
val settingsList = listOf(
DataItemsSettings(getString(R.string.look), getString(R.string.lookdescription), R.drawable.ic_colored_color_lens),
DataItemsSettings(getString(R.string.playing), getString(R.string.playingdescription), R.drawable.ic_colored_view_carousel),
DataItemsSettings(getString(R.string.images), getString(R.string.imagesdscription), R.drawable.ic_colored_image),
DataItemsSettings(getString(R.string.audio), getString(R.string.audiodescription), R.drawable.ic_colored_volume_up),
DataItemsSettings(getString(R.string.other), getString(R.string.otherdescription), R.drawable.ic_colored_shape),
DataItemsSettings(getString(R.string.about), getString(R.string.aboutdescription), R.drawable.ic_colored_info)
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_settings_main, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rvSettings.apply {
layoutManager = LinearLayoutManager(activity)
adapter = AdapterSettings(settingsList, NEEDS LISTENER HERE)
}
}
override fun OnItemClick(position: Int) {
when(position) {
0 -> //Start new fragment here
1 -> //Start new fragment here
2 -> //Start new fragment here
3 -> //Start new fragment here
4 -> //Start new fragment here
5 -> this.startActivity(Intent(this, ActivityAbout::class.java))
}
}
}
AdapterSettings.kt
class AdapterSettings(
var settingsList: List<DataItemsSettings>,
var listener: OnItemClickListener
) : RecyclerView.Adapter<AdapterSettings.SettingsViewHolder>() {
inner class SettingsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
}
override fun onClick(p0: View?) {
val position : Int = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener.OnItemClick(position)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_settings, parent, false)
return SettingsViewHolder(view)
}
override fun getItemCount(): Int {
return settingsList.size
}
override fun onBindViewHolder(holder: SettingsViewHolder, position: Int) {
holder.itemView.apply {
rvTitle.text = settingsList[position].stringTitle
rvDescription.text = settingsList[position].stringDescription
rvIcon.setImageResource(settingsList[position].itemIcon)
}
}
interface OnItemClickListener {
fun OnItemClick(position: Int)
}
}
As you are in apply block to use this you need to add #FragmentSettingsMain while using this.
AdapterSettings(settingsList,this#FragmentSettingsMain)
Or You can use
val listener = this
rvSettings.apply{
AdapterSettings(settingsList,listener)
}
You can pass lambda to the adapter, call adapter.onAction = {}

System services not available to Activities before onCreate(), with Android RecyclerView inside a fragment Kotlin

I'm having trouble with Android RecyclerView in Kotlin inside a fragment who is in Home activity.
Here's my code:
myFragmentLayout:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvChapterList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
myFragmentCode:
val chaptersList: ArrayList<String> = ArrayList()
private lateinit var layoutManager: RecyclerView.LayoutManager
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.courses_layout, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
chaptersList.add("Android")
chaptersList.add("Kotlin")
chaptersList.add("RecyclerView")
layoutManager = LinearLayoutManager(context)
rvChapterList.layoutManager = layoutManager
rvChapterList.adapter = ChapterAdapter(Home(), chaptersList)
}
and myChapterAdapter:
class ChapterAdapter(private val context: Home, private val chaptersList: ArrayList<String>) : RecyclerView.Adapter<ChapterAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.list_item, parent, false))
}
override fun getItemCount(): Int {
return chaptersList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.chapterName?.text = chaptersList.get(position)
holder.itemView.setOnClickListener {
Toast.makeText(context, chaptersList.get(position), Toast.LENGTH_LONG).show()
}
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val chapterName = view.tvChapterName
}
}
It got this code from this tutorial, it's working inside an activity but not iside a fragment.
rvChapterList.adapter = ChapterAdapter(Home(), chaptersList)
Never create an Activity or other type of Context yourself. You always obtain them from the framework.
Replace that with:
rvChapterList.adapter = ChapterAdapter(requireActivity(), chaptersList)
and change the context property in ChapterAdapter to be a Context or Activity, not Home.

Kotlin RecycleView does not work correctly

I want to create a small application that once the user clicks on floating button action to add element. It will be added automatically
to the top of the recycle view.
I have created for this A main activity which contains the fragment of the recycler and the add flotten.
Once the user clicks on this button a fragmented dialog is shown to insert the element. Once the user confirms i want to add the new element to the top of the recycle view.
TO did I used a global array which contains the item.
I have not implemented the logic yet for that. But what surprised me once I click on confirm button of the shown dialog the element added correctly to the global array, however, the recycle view on his own without any action on it duplicate the latest element.
I used the model view presenter for that and here are my diff classes
1.MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addFragment(InterFragment(), R.id.container)
fab.setOnClickListener { view ->
val dialog = IntervAddFragment()
val ft = supportFragmentManager.beginTransaction()
dialog.show(ft, ContentValues.TAG)
}
}
}
2.ADDFragmet
class IntervAddFragment : DialogFragment(), AddContract.View
{
val presenter: AddPresenter by lazy { AddPresenter(this) }
var dat=""
var plom=""
var typ=""
override fun onCreate(savedInstanceState: Bundle?) { ....}
override fun onStart() {.... }
override fun onCreateView(inflater: LayoutInflater?, parent: ViewGroup?, state: Bundle?): View? {...}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {....}
fun on_confirmClick()
{
presenter.on_confirmClick(dat,plom,typ)
Log.e("errrr",presenter.getList().toString())
dismiss()
}
fun fillSpinerPlom(view : View?) {.... }
fun fillSpinerType(view : View?) {.... }
private fun updateDateInView(datep:String) {....}
}
3.IntervFragement
InterFragment: Fragment(), InterContract.View {
var views: View? = null
var List_inter_adapter: InterListAdapter? = null
private var recyclerView: RecyclerView? = null
val presenter: InterventionPresenter by lazy { InterventionPresenter(this) }
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {.... }
interface ClickListener {
fun onClick(view: View, position: Int)
fun onLongClick(view: View?, position: Int)
}
internal class RecyclerTouchListener(context: Context, recyclerView: RecyclerView, private val clickListener: ClickListener?) : RecyclerView.OnItemTouchListener {..... }
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {.....}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView = views!!.findViewById<View>(R.id.interv_item_listview) as RecyclerView
List_inter_adapter = InterListAdapter(activity, presenter.getList(true))
recyclerView!!.adapter = List_inter_adapter
recyclerView!!.layoutManager = LinearLayoutManager(activity)
}
}
4.InterPresenter
class InterventionPresenter(val view: InterContract.View)
{
fun getList(firstTime:Boolean): LinkedList<InterItem> {
if(firstTime)
return populateList().list
else
return items
}
fun populateList(): ListInter
{
val item1 = InterItem(1, DateT(2018, Calendar.FEBRUARY, 6).date, plomb.get(0), types.get(0))
val item2 = InterItem(2, DateT(2018, Calendar.MARCH, 8).date, plomb.get(1), types.get(1))
val item3= InterItem(3, DateT(2018, Calendar.MAY, 10).date, plomb.get(2), types.get(2))
val lstint = ListInter()
lstint.list= LinkedList(listOf(item1, item2, item3))
items=lstint.list
return lstint
}
companion object {
val plomb= LinkedList<String>(listOf("Dina", "Lili", "Wiseem"))
val types= LinkedList<String>(listOf("type1","type2","type3"))
lateinit var items: LinkedList<InterItem>
}
}
5.ADD Presenter
class AddPresenter(val view: AddContract.View)
{
fun on_confirmClick(date:String,plom:String,type:String)
{
val item= InterItem(items.size+1,date,plom,type)
items.push(item)
}
fun getList(): LinkedList<InterItem> {
return items
}
fun getplom(): LinkedList<String>
{ return plomb
}
fun getType(): LinkedList<String>
{ return types
}
}
6.InterListAdapter
class InterListAdapter(private val context: Context, linkedList: LinkedList<InterItem>) : RecyclerView.Adapter<InterListAdapter.ViewHolder>()
{
internal var linkedList = LinkedList<InterItem>()
private val inflater: LayoutInflater
//private lateinit var listener: OnTaskSelected
init {... }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {... }
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.numero?.text=(linkedList[position].numero).toString()
holder.date?.text=linkedList[position].date
holder.plom?.text=linkedList[position].plom
holder.type?.text=linkedList[position].type
}
override fun getItemCount(): Int {
return linkedList.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var numero: TextView?=null
var date: TextView?=null
var plom: TextView?=null
var type: TextView?=null
var btn_delete: Button?=null
var btn_update: Button?=null
init {
numero = itemView.findViewById(R.id.numero)
date = itemView.findViewById(R.id.date)
plom = itemView.findViewById(R.id.plom)
type = itemView.findViewById(R.id.type)
btn_delete = itemView.findViewById(R.id.btn_supprimer)
btn_update = itemView.findViewById(R.id.btn_modifier)
}
}
}
and here is the ListInter
class ListInter
{
var list= LinkedList<InterItem>()
get() = field
set(value){field = value}
}
RecyclerView can't handle array change event by itself. So, you should notify it by calling the following after you've added element to array:
recyclerView.getAdapter().notifyItemInserted(items.size() - 1)

Categories

Resources