RecyclerView Button-only item onClick - android

So I'm doing RecyclerView of buttons. And I don't know how/where to write the onClick for the button. I have a JSONArray to pass, and the text of the button will have the cName.
So this is my item_unit.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="#+id/btn_unit"
android:background="#000000"
android:textColor="#ffffff"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
This is my ItemAdapter
//Here the stype is <CategoryItem> because this button will open
//new fragment to show the list of the items.
//So it's recyclerview item opens another recyclerview.
class CatAdapter(var btnList: ArrayList<CategoryItem>, var context: Context): RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
var v: View = LayoutInflater.from(context).inflate(R.layout.item_unit, parent, false)
return ViewHolder(v)
}
override fun getItemCount( ): Int {
return btnList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val data = btnList[position]
holder.itemBtn.text = data.name
}
}
class ViewHolder(view: View): RecyclerView.ViewHolder(view){
var itemBtn = view.findViewById<Button>(R.id.item_btn)
}
This recycler view is in a fragment. I need to
class MainActFrag : Fragment() {
var btnList = ArrayList<CategoryItem>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
var view = inflater.inflate(R.layout.fragment_main_act, container, false)
//Recycler view
view.recyclerView.layoutManager = LinearLayoutManager(context)
//I have the url
var stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener {
var jsonObject = JSONObject(it)
var jsonArrayCates =jsonObject.getJSONArray("category")
for(i in 0 until jsonArrayCats.length()){
var btns =jsonArrayCates.getJSONObject(i)
var iD = btns.getString("id")
var name = btns.getString("name")
var des = btns.getString("description")
btnList.add(CategoryItem(cID, cName, cDes))
}
val adapter = ItemAdapter(btnList, view.context)
recyclerView.adapter = adapter
},
Response.ErrorListener {}
)
Volley.newRequestQueue(view.context).add(stringRequest)
return view
}
}
So I don't know where to call each item onClick. If it's just a normal TextView item, I can do do it, but since I chose to make the item button, I don't know the specific way to handle this. I thought it would be easy because the button is... clickable by nature?
Thank you!

add a bind function to the ViewHolder Class then you can put your click handler on each button
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val data = btnList[position]
holder.itemBtn.text = data.name
holder.bindButtons()
}
class ViewHolder(view: View): RecyclerView.ViewHolder(view){
var itemBtn = view.findViewById<Button>(R.id.item_btn)
fun bindButtons()
itemBtn.setOnClickListener{
//do you click event stuff
}
}

Related

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

RecyclerView kotlin don't remember selected items, after add next items

Hello i have problem with my shopping List. I have RoomDatabase and recycler view to adding and reading my list. I create action when user click on button row change background, but after i add new items then don't see selected items. Where is a problem?
My Adapter:
lass ListAdapter() : RecyclerView.Adapter<ListAdapter.MyViewHolder>() {
private var itemList = emptyList<Items>()
lateinit var viewModel: ItemsViewModel
inner class MyViewHolder(itemview: View) : RecyclerView.ViewHolder(itemview) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.items_row, parent, false)
)
}
override fun getItemCount(): Int {
return itemList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = itemList[position]
holder.itemView.name_rec.text = currentItem.itemsName
holder.itemView.amount_rec.text = currentItem.amountItems.toString()
holder.itemView.rowLayout.setOnClickListener {
val action = ListFragmentDirections.actionListFragmentToUpdateFragment(currentItem)
holder.itemView.findNavController().navigate(action)
}
holder.itemView.btnDeleteee.setOnClickListener {
val action = ListFragmentDirections.actionListFragmentToDeleteFragment(currentItem)
holder.itemView.findNavController().navigate(action)
}
holder.itemView.btncheckbox.setOnClickListener {
holder.itemView.rowLayout.setBackgroundColor(Color.parseColor("#FFBA5F"))
}
}
fun setData(items: List<Items>) {
this.itemList = items
notifyDataSetChanged()
}
My ListFragment:
class ListFragment : Fragment() {
lateinit var itemsViewModel: ItemsViewModel
private val args by navArgs<UpdateFragmentArgs>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_list, container, false)
//menu
setHasOptionsMenu(true)
//RecyclerView
val recyclerView = view.recycler_view_list
val adapter = ListAdapter()
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
//ViewWModel
itemsViewModel = ViewModelProvider(this).get(ItemsViewModel::class.java)
itemsViewModel.readAllData.observe(viewLifecycleOwner, Observer { items ->
adapter.setData(items)
})
view.floatingActionButton2.setOnClickListener {
findNavController().navigate(R.id.action_listFragment_to_addFragment)
}
Where is problem with save all actions who do user in list?
Thanks for help
You have to store the selected item and in onBindViewHolder you have to check that the item for this position is previously selected item or not. If this is the selected item then change the background or make if default.
Actually the problem is as you are changing the background in onClickListener so when you are updating the itemList with setData(items: List<Items>) method RecyclerView reload all the visible items and your previously selected item loses its background

How to Read Data from RecyclerView and send in BottomSheet

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.

kotlin.TypeCastException: null cannot be cast to non-null type android.widget.TextView

i am trying to code my app in kotlin but i am getting null cannot be casted to non-null type and it force my app to close. i tried to reference to other stackoverflow where they shift the init but i do not have any init portion in my current code. any help will be appreciated thank you.
reminder_fragment.kt
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val v = inflater.inflate(R.layout.reminder_fragment, container, false)
//getting recyclerview from xml
val recyclerView = v.findViewById(R.id.reminderrecycler) as RecyclerView
//adding a layoutmanager
recyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL ,false)
//crating an arraylist to store users using the data class user
val reminderlist = ArrayList<reminders>()
//adding some dummy data to the list
reminderlist.add(reminders("Belal Khan"))
reminderlist.add(reminders("Ramiz Khan"))
reminderlist.add(reminders("Faiz Khan"))
reminderlist.add(reminders("Yashar Khan"))
//creating our adapter
val adapter = CustomAdapter(reminderlist)
//now adding the adapter to recyclerview
recyclerView.adapter = adapter
return v;
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
val manager = supportFragmentManager
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_reminders -> {
createReminderFragment()
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bottom_navigation.setItemIconTintList(null);
createReminderFragment()
bottom_navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
/*private fun replaceFragment(fragment: Fragment) {
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.fragmentplaceholder, fragment)
fragmentTransaction.commit();
}*/
fun createReminderFragment() {
val transaction = manager.beginTransaction()
val fragment = reminder_fragment()
transaction.replace(R.id.fragmentplaceholder, fragment)
transaction.addToBackStack(null)
transaction.commit()
}
}
reminder_fragment.xml
<LinearLayout
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"
tools:context=".reminder_fragment"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/reminderrecycler"/>
</LinearLayout>
CustomAdapter.kt
class CustomAdapter(val reminderlist: ArrayList<reminders>) :
RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.reminder_fragment, parent, false)
return ViewHolder(v);
}
override fun getItemCount(): Int {
return reminderlist.size }
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItems(reminderlist[position])
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(reminders: reminders) {
val medicineText = itemView.findViewById(R.id.medicineText) as TextView
medicineText.text = reminders.medicineN
}
}
}
You does not implement your RecyclerView well.
You must have an seperated layout for RecyclerView items .
You set your RecyclerView items layout , your layout which your RecylerView decalared in it now .
Therefor android can not find your TextView with Id medicineText
I recommend you to do these:
1) Create a seperate layout for your RecyclerView items and inflate it to your holder as below :
val v = LayoutInflater.from(parent.context).inflate(R.layout.recylerView_item, parent, false)
2) put in an TextView in RecylerView item layout with id of medicineText
3)Use ? symbol to handle possible NullPonterException when you declaring your TextView
val medicineText = itemView.findViewById(R.id.medicineText) as? TextView

RecyclerView Get view at particular position and retrieve data from it android kotlin

i have recyclerview item which has 4 edittexts. Also i have add new item button which add new item and new edittexts(they need to be populated from the use) i`m trying to retrieve all data from the fields when user click save button. Here is my code:
class SectionsRecyclerAdapter(private val educationList: MutableList<Any>) :
RecyclerView.Adapter<SectionsRecyclerAdapter.ViewHolder>() {
class ViewHolder(val item: View) : RecyclerView.ViewHolder(item)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_new_section, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = educationList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if (holder != null) {
holder.item.deleteBtn.visibility = if (position != 0) View.VISIBLE else View.GONE
holder.item.deleteBtn.setOnClickListener {
educationList.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeRemoved(position, educationList.size)
}
}
}
fun addItem() {
educationList.add(EducationModel())
notifyItemInserted(educationList.size)
}
}
Education Fragment :
class EducationFragment : Fragment(), ValidationInterface {
private var educationList: MutableList<Any> = mutableListOf()
private lateinit var sectionAdapter : SectionsRecyclerAdapter
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_education, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
educationList.add(EducationModel())
sectionAdapter = SectionsRecyclerAdapter(educationList)
educationRv.apply {
layoutManager = LinearLayoutManager(activity)
adapter = ScaleInAnimationAdapter(sectionAdapter)
itemAnimator = LandingAnimator()
}
addSectionBtn.setOnClickListener {
sectionAdapter.addItem()
educationRv.smoothScrollToPosition(educationList.size)
}
}
companion object {
val instance = EducationFragment()
}
override fun validateAndSave(): Boolean {
//Here i want to get data from every child and then parse it to my model EducationModel
val model = educationRv.getChildAt(0)
list.add(model)
CreateResumeActivity.modelInstance.educationList.addAll(list)
return true
}
Of course if there are better solution i will be glad, but for now i`m stuck with this... My main goal is when user click save i need to retrieve value from the edittext ( University, StartDate, EndDate, Summary) and put it in EducationModels
Inside a recycler view each view holder can recycle his views. For that reason you have to keep all edittext values in a public list inside your adapter and access it from component which hold that adapter. Here is an proof of concept:
class MyAdapter: RecyclerView.Adapter(private val educationList: MutableList<Any>) {
val holderList = educationList.map { EditTextValues(...) };
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
//fill edit text just in case that it is recycled
holder.editText.text = holderList[position].editTextValue
holder.editText.onTextChangeLister { holderList[position] = EditTextValues(holder.editText.text) }
}
}
class MyAndroidComponent : Fragment {
val adapter: MyAdapter = ...
fun onCreate() {
saveButton.setOnClickListener {
// you can access your edit texts values here
adapter.holderList
}
}
}

Categories

Resources