Kotlin Fragment RecyclerView Adapter ...trying to make ItemClickOnListener to work - android-fragments

This is making me go bananas !! Swear i will never eat bananas again !!
I am trying work on a Kotlin/Fragment/RecyclerViewAdapter/OnItemClickListener but it is not working
I am trying to make the ItemOnClickListener to work between the RecyclerView Adapter and the Fragment. I have indicated in the code where the problems are.
FRAGMENT
class VCTask : Fragment() {
private val TAG = "VCTask"
private lateinit var mContext: Context
private var _binding: FTaskBinding? = null
private val binding get() = _binding!!
private val exampleList = generateDummyList(500)
***private val adapter = ExampleAdapter(exampleList,this) <-----ERROR "this" in Fragment is rejected obviously
it refers to Activity..tried to use context etc but it is expecting ContentListener
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FTaskBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
private fun fragbind(view:View) {
mContext = this.requireContext()
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(mContext)
binding.recyclerView.setHasFixedSize(true)
}
fun onItemClicked(position: Int) { <------------------------ERROR: I want this to work
mContext = this.requireContext()
Toast.makeText(mContext, "Item $position clicked", Toast.LENGTH_SHORT).show()
val clickedItem = exampleList[position]
clickedItem.text1 = "Clicked"
adapter.notifyItemChanged(position)
}
}
RECYCLER ADAPTER
class ExampleAdapter(private val exampleList: List<ExampleItem>,listener: ContentListener) : <---LISTENER
ADDED AS VARIABLE..WHAT TO SHOW IN FRAGMENT ??
RecyclerView.Adapter<ExampleAdapter.ExampleViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExampleViewHolder {
val itemView = ExampleItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ExampleViewHolder(itemView)
}
override fun onBindViewHolder(holder: ExampleViewHolder, position: Int) {
val currentItem = exampleList.get(position)
holder.binding.imageView.setImageResource(currentItem.imageResource)
holder.binding.textView1.text = currentItem.text1
holder.binding.textView2.text = currentItem.text2
}
override fun getItemCount(): Int = exampleList.size
inner class ExampleViewHolder(val binding: ExampleItemBinding) : RecyclerView.ViewHolder(binding.root) {
val imageView: ImageView = binding.imageView
val textView1: TextView = binding.textView1
val textView2: TextView = binding.textView2
fun bind(listOfData:
ArrayList<ExampleItem>, listener: ContentListener) { <-----------IS THIS CORRECT ??
Because adapterPosition is deprecated
val item = listOfData[adapterPosition]
itemView.setOnClickListener {
listener.onItemClicked(listOfData.get(adapterPosition))
}
}
}
public interface ContentListener {
fun onItemClicked(item: ExampleItem)
}
}
Appreciate your input...i have spent too much time on this...

You need to declare and implement your interface in your fragment, like this:
binding.recyclerView.adapter.setOnclickListiner(mOnAdapterClickListener)
and then define and implement like this..
private val mOnAdapterClickListener = object : ContentListener {
override fun onItemClicked(item: ExampleItem) {
//do your stuff
}

Related

Kotlin Fragment Duplicate Data In RecyclerView

Hello everyone i know that my fragment problem is about ı don't have the method for clearing the data which came from firebase but i tried to solve this problem and i can't
My problem is when i am swiching between two fragments my recycler view is duplicating itself i was trying to admob to my app and i realise this problem. Do i need a safe destroy method or clear data func ı don't know.
Here is my fragment :
class DashboardFragment : Fragment() {
private var mAdView: AdView? = null
private var fragmentView: View? = null
private var postArrayList: ArrayList<Post> = arrayListOf()
private var db: FirebaseFirestore ? = null
private var recyclerView: RecyclerView? = null
private var feedAdapter: FeedAdapter? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (fragmentView == null) {
fragmentView = inflater.inflate(R.layout.fragment_dashboard, container, false)
}
return fragmentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
db = FirebaseFirestore.getInstance()
recyclerView = view.findViewById(R.id.recyclerView)
recyclerView?.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
feedAdapter = context?.let { FeedAdapter(it, arrayListOf()) }
recyclerView?.adapter = feedAdapter
MobileAds.initialize(this#DashboardFragment.requireContext()) {
}
mAdView = view.findViewById(R.id.adView)
val adRequest = AdRequest.Builder().build()
mAdView?.loadAd(adRequest)
eventChangeListener()
}
#SuppressLint("NotifyDataSetChanged")
private fun eventChangeListener() {
db?.collection("Posts")?.addSnapshotListener { value, _ ->
if (value != null) {
if (!value.isEmpty) {
value.documents.forEach {
val comment: String = (it.get("comment") ?: "").toString()
val post = Post(comment)
postArrayList.add(post)
}
Log.d("avsArray", postArrayList.toString())
feedAdapter?.userList = this.postArrayList
feedAdapter?.notifyDataSetChanged()
}
}
}
}
}

avoid fragment recreation BottomNavigationView

i want to avoid fragment recreation by using BottomNavigationView
i read about that FragmentTransaction.replace is the problem and changing to add would help, but that didn't work for me..
maybe you can see here where i'm wrong
this is my Host Activity which hosting 3 fragments with BottomNavigationView
'''
class Host : AppCompatActivity() {
var fragment: Fragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_host)
//for hide the actionBar (contains Fragment name) on top of the screen
val actionBar = supportActionBar
actionBar?.hide()
val navController = Navigation.findNavController(this, R.id.nav_host_fragment)
val navView = findViewById<BottomNavigationView>(R.id.nav_view)
navView?.setupWithNavController(navController)
NavigationUI.setupWithNavController(navView, navController)
mAdapter = NfcAdapter.getDefaultAdapter(this)
fragmentStayAlive()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
for (fragment in supportFragmentManager.fragments) {
fragment.onActivityResult(requestCode, resultCode, data)
}
}
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
private fun fragmentStayAlive(){
val fragment1: Fragment = LobbyFragment()
val fragment2: Fragment = GameSessionFragment()
val fragment3: Fragment = UserStatusFragment()
val fm: FragmentManager = supportFragmentManager
var active = fragment1
fm.beginTransaction().add(R.id.nav_host_fragment, fragment3, "UserStatusFragment").hide(
fragment3
).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment2, "GameSessionFragment").hide(
fragment2
).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment1, "LobbyFragment").commit();
val mOnNavigationItemSelectedListener: BottomNavigationView.OnNavigationItemSelectedListener =
object : BottomNavigationView.OnNavigationItemSelectedListener {
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when (item.getItemId()) {
R.id.lobbyFragment -> {
fm.beginTransaction().hide(active).show(fragment1).commit()
active = fragment1
return true
}
R.id.gameSessionFragment -> {
fm.beginTransaction().hide(active).show(fragment2).commit()
active = fragment2
return true
}
R.id.userStatusFragment -> {
fm.beginTransaction().hide(active).show(fragment3).commit()
active = fragment3
return true
}
}
replaceFragment(active, null, true, true)
return false
}
}
}
fun replaceFragment(
fragment: Fragment,
#Nullable bundle: Bundle?,
popBackStack: Boolean,
findInStack: Boolean
) {
val fm = supportFragmentManager
val ft: FragmentTransaction = fm.beginTransaction()
val tag = fragment.javaClass.name
val parentFragment: Fragment?
parentFragment = if (findInStack && fm.findFragmentByTag(tag) != null) {
fm.findFragmentByTag(tag)
} else {
fragment
}
// if user passes the #bundle in not null, then can be added to the fragment
if (bundle != null) {
parentFragment!!.arguments = bundle
} else {
parentFragment!!.arguments = null
}
// this is for the very first fragment not to be added into the back stack.
if (popBackStack) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
} else {
ft.addToBackStack(parentFragment.javaClass.name + "")
}
ft.add(R.id.nav_view, parentFragment, tag)
ft.commit()
fm.executePendingTransactions()
}
'''
thanks for all the helpers!

How to save LocalData in Firebase Realtime database?

I am very new using Kotlin and programming and I am currently making a calendar with events. My problem comes when I want to connect these events to firebase.
I am using an example that I found in git (https://github.com/kizitonwose/CalendarView) that uses the ThreeTen library for dates. This is the Event object:
class Event (val id: String, val text: String, val date: LocalDate) : Serializable
The data variable is of the LocalData type and this is what is causing me problems since it seems that Firebase only accepts variables of type String, Int, etc ...
I tried to pass the variable to String with toString and with Gson (), without success.
Here is the code if it helps
private val inputDialog by lazy {
val editText = AppCompatEditText(requireContext())
val layout = FrameLayout(requireContext()).apply {
// Setting the padding on the EditText only pads the input area
// not the entire EditText so we wrap it in a FrameLayout.
setPadding(20, 20, 20, 20)
addView(editText, FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT))
}
AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.example_3_input_dialog_title))
.setView(layout)
.setPositiveButton(R.string.save) { _, _ ->
saveEvent(editText.text.toString())
// Prepare EditText for reuse.
editText.setText("")
}
.setNegativeButton(R.string.close, null)
.create()
.apply {
setOnShowListener {
// Show the keyboard
editText.requestFocus()
context.inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
}
setOnDismissListener {
// Hide the keyboard
context.inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0)
}
}
}
private var selectedDate: LocalDate? = null
private val today = LocalDate.now()
private val titleSameYearFormatter = DateTimeFormatter.ofPattern("MMMM")
private val titleFormatter = DateTimeFormatter.ofPattern("MMM yyyy")
private val selectionFormatter = DateTimeFormatter.ofPattern("yyyy MM dd")
private val events = mutableMapOf<LocalDate, List<Event>>()
private var prueba = Gson().toJson(events)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_calendar, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mDatabaseReference = mDatabase!!.reference.child("events")
exThreeRv.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
exThreeRv.adapter = eventsAdapter
exThreeRv.addItemDecoration(DividerItemDecoration(requireContext(), RecyclerView.VERTICAL))
val daysOfWeek = daysOfWeekFromLocale()
val currentMonth = YearMonth.now()
exThreeCalendar.setup(currentMonth.minusMonths(10), currentMonth.plusMonths(10), daysOfWeek.first())
exThreeCalendar.scrollToMonth(currentMonth)
if (savedInstanceState == null) {
exThreeCalendar.post {
// Show today's events initially.
selectDate(today)
}
}
class DayViewContainer(view: View) : ViewContainer(view) {
lateinit var day: CalendarDay // Will be set when this container is bound.
val textView = view.exThreeDayText
val dotView = view.exThreeDotView
init {
view.setOnClickListener {
if (day.owner == DayOwner.THIS_MONTH) {
selectDate(day.date)
}
}
}
}
exThreeCalendar.dayBinder = object : DayBinder<DayViewContainer> {
override fun create(view: View) = DayViewContainer(view)
override fun bind(container: DayViewContainer, day: CalendarDay) {
container.day = day
val textView = container.textView
val dotView = container.dotView
textView.text = day.date.dayOfMonth.toString()
if (day.owner == DayOwner.THIS_MONTH) {
textView.makeVisible()
when (day.date) {
today -> {
textView.setTextColorRes(R.color.white)
textView.setBackgroundResource(R.drawable.today_bg)
dotView.makeInVisible()
}
selectedDate -> {
textView.setTextColorRes(R.color.white)
textView.setBackgroundResource(R.drawable.selected_bg)
dotView.makeInVisible()
}
else -> {
textView.setTextColorRes(R.color.black)
textView.background = null
dotView.isVisible = events[day.date].orEmpty().isNotEmpty()
}
}
} else {
textView.makeInVisible()
dotView.makeInVisible()
}
}
}
exThreeCalendar.monthScrollListener = {
requireActivity().home.text = if (it.year == today.year) {
titleSameYearFormatter.format(it.yearMonth)
} else {
titleFormatter.format(it.yearMonth)
}
// Select the first day of the month when
// we scroll to a new month.
selectDate(it.yearMonth.atDay(1))
}
class MonthViewContainer(view: View) : ViewContainer(view) {
val legendLayout = view.legendLayout
}
exThreeCalendar.monthHeaderBinder = object : MonthHeaderFooterBinder<MonthViewContainer> {
override fun create(view: View) = MonthViewContainer(view)
override fun bind(container: MonthViewContainer, month: CalendarMonth) {
// Setup each header day text if we have not done that already.
if (container.legendLayout.tag == null) {
container.legendLayout.tag = month.yearMonth
container.legendLayout.children.map { it as TextView }.forEachIndexed { index, tv ->
tv.text = daysOfWeek[index].name.first().toString()
tv.setTextColorRes(R.color.black)
}
}
}
}
exThreeAddButton.setOnClickListener {
inputDialog.show()
}
}
private fun selectDate(date: LocalDate) {
if (selectedDate != date) {
val oldDate = selectedDate
selectedDate = date
oldDate?.let { exThreeCalendar.notifyDateChanged(it) }
exThreeCalendar.notifyDateChanged(date)
updateAdapterForDate(date)
}
}
private fun saveEvent(text: String) {
if (text.isBlank()) {
Toast.makeText(requireContext(),
R.string.example_3_empty_input_text, Toast.LENGTH_LONG).show()
} else {
selectedDate?.let {
events[it] = events[it].orEmpty().plus(
Event(
UUID.randomUUID().toString(),
text,
it
)
)
uploadFirebase()
updateAdapterForDate(it)
}
}
}
private fun deleteEvent(event: Event) {
val date = event.date
events[date] = events[date].orEmpty().minus(event)
updateAdapterForDate(date)
}
private fun updateAdapterForDate(date: LocalDate) {
eventsAdapter.events.clear()
eventsAdapter.events.addAll(events[date].orEmpty())
eventsAdapter.notifyDataSetChanged()
exThreeSelectedDateText.text = selectionFormatter.format(date)
}
fun uploadFirebase(){
val newEvent = mDatabaseReference.push()
newEvent.setValue(events)
}
override fun onStart() {
super.onStart()
}
override fun onStop() {
super.onStop()
}
}
There is no way you can add a property of type LocalDate in a Firebase Realtime database because it is not a supported data-type. However, there are two ways in which you can solve this:
You save the date as a ServerValue.TIMESTAMP, which basically means that you save the number of seconds that have elapsed since the Unix epoch. In this case, the server writes the current date in the database. To achieve this, please see my answer from the following post:
How to save the current date/time when I add new value to Firebase Realtime Database
You specify a custom long value for your date field. In this case, it's up to you to determine what date is written.
Unfortunately, there is no way you can combine these two options, you can use one or the other.
When talking about a LocalDate, we usually talk about an offset, in which case, this what I'll do. I'll store a Timestamp property, as explained at point one, that will let the server populate with the server Timestamp, as well as an offset property, that should be populated with the offset in days/hours.

kotlin fragment error NullPointerException

my complete code
class BlankFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.mainex, container, false)
val groupAdapter = GroupAdapter<ViewHolder>().apply {
spanCount = 2
}
recycler_view.apply {
//error NullPointerException in this line
layoutManager = GridLayoutManager(rootView.context, groupAdapter.spanCount).apply {
spanSizeLookup = groupAdapter.spanSizeLookup
}
adapter = groupAdapter
}
var headerTab: ArrayList<mTop>
headerTab = arguments?.getSerializable("headertab") as ArrayList<mTop>
for (h in 0 until headerTab.size) {
val header = headerTab.get(h).kategori
ExpandableGroup(ExpandableHeaderItem(header), true).apply {
for (c in 0 until headerTab[h].sub.size) {
val gambar = (headerTab[h].sub).get(c).gambar
val nama_menu = (headerTab[h].sub).get(c).nama_menu
add(Section(FancyItem(gambar, nama_menu)))
}
groupAdapter.add(this)
}
}
I'm trying to make the recyclerview display in the tablayout fragment
and an error occurred, maybe the problem comes from rootView.context
layoutManager = GridLayoutManager(rootView.context, groupAdapter.spanCount).apply {
spanSizeLookup = groupAdapter.spanSizeLookup
}
thanks :) (sorry, my english is bad)
You are setting LayoutManager to recyclerView before the view is created. You should do this in onViewCreated() method or in the xml file. Use onCreateView() only for inflating the view. And then use onViewCreated() for others settings you need to do to your views.
Done. this my complete fix code with separate onCreateView and onViewCreate. thanks all
class FragBaru : Fragment() {
private lateinit var rv: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.mainex, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
rv = view.findViewById(R.id.recycler_view)
val groupAdapter = GroupAdapter<ViewHolder>().apply {
spanCount = 2
}
rv.apply {
layoutManager = GridLayoutManager(rootView.context, groupAdapter.spanCount).apply {
spanSizeLookup = groupAdapter.spanSizeLookup
}
adapter = groupAdapter
}
var headerTab: ArrayList<mTop>
headerTab = arguments?.getSerializable("headertab") as ArrayList<mTop>
for (h in 0 until headerTab.size) {
val header = headerTab.get(h).kategori
ExpandableGroup(ExpandableHeaderItem(header), true).apply {
for (c in 0 until headerTab[h].sub.size) {
val gambar = (headerTab[h].sub).get(c).gambar
val nama_menu = (headerTab[h].sub).get(c).nama_menu
add(Section(FancyItem(gambar, nama_menu)))
}
groupAdapter.add(this)
}
}
}
companion object {
fun newInstance(headertab: ArrayList<mTop>): FragBaru {
val f = FragBaru()
val args = Bundle()
args.putSerializable("headertab", headertab)
f.setArguments(args)
return f
}
}
}

KotlinNullException when using query

i'm trying to build an app that shows images from FirebaseDatabase into RecyclerView, something like this tutorial but with Kotlin, now I have kotlin.KotlinNullPointerException and it points to this code line
var options = FirebaseRecyclerOptions.Builder<Blog>()
.setQuery(query!!, Blog::class.java)
.build()
I couldn't solve it since tow days, this is the activity code :
class MainActivity : AppCompatActivity() {
private var mDatabase:DatabaseReference? = null
private var mBlogList:RecyclerView?=null
private var query:Query?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navBar.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
mDatabase=FirebaseDatabase.getInstance().getReference().child("mall")
mDatabase?.keepSynced(true)
mBlogList = findViewById(R.id.recyclee)
mBlogList?.setHasFixedSize(true)
mBlogList?.layoutManager = LinearLayoutManager(this)
query = mDatabase?.orderByKey()
}
var options = FirebaseRecyclerOptions.Builder<Blog>()
.setQuery(query!!, Blog::class.java)
.build()
override fun onStart() {
super.onStart()
val mAdapter = object : FirebaseRecyclerAdapter<Blog, BlogViewHolder>(
options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BlogViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.cardview, parent, false)
return BlogViewHolder(view)}
override fun onBindViewHolder(holder: BlogViewHolder, position: Int, model: Blog) {
holder.setTitle(model.title)
holder.setDes(model.des)
holder.setImage(applicationContext, model.image)
}
}
mBlogList?.adapter = mAdapter
}
inner class BlogViewHolder : RecyclerView.ViewHolder{
var mView:View?=null
constructor(itemView: View?) : super(itemView) {
this.mView = itemView
}
fun setTitle(title:String){
var postTitle = mView?.findViewById<TextView>(R.id.post_title)
postTitle?.text = title
}
fun setDes(des:String){
var postDes = mView?.findViewById<TextView>(R.id.post_des)
postDes?.text = des
}
fun setImage(ctx:Context, image:String){
var postImage = mView?.findViewById<ImageView>(R.id.post_title)
Picasso.get().load(image).into(postImage)
}
}
}
Blog class:
class Blog(var title: String?, var des: String?, var image: String?)
My database looks like:
You're trying to make use of query before it's initially assigned in onCreate. You should wait to create options only after query has a non-null value. Notice that your options is sitting at the class member level, which gets evaluated before Android calls your onCreate.

Resources