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.
Related
I have an activity (ProductList) contain listview to view all products in my database, and a custom adapter for the list (MyListAdapter) that contains update button when I click on it a dialoge open and update the product info.
I want that when I update the product info and close the dialog the listview updated in the same time.
ProductsList Activity
class ProductsList : AppCompatActivity() {
private lateinit var databaseHandler:DatabaseHandler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_products_list)
databaseHandler = DatabaseHandler(this)
val product: ArrayList<Product> = databaseHandler.listProduct()
val productArrayId = Array(product.size){"0"}
val productArrayName = Array(product.size){"null"}
val productArrayQuantity = Array(product.size){"null"}
val allInfo = Array(product.size){"null"}
for((index, e) in product.withIndex()){
productArrayId[index] = e.getProductID().toString()
productArrayName[index] = e.getProductName()
productArrayQuantity[index] = e.getProductQuantity().toString()
allInfo[index] = e.getProductID().toString() + " " + e.getProductName() + " " + e.getProductQuantity().toString()
}
var myListAdapter = MyListAdapter(this,product)
lv_products.adapter = myListAdapter
}
}
MyListAdapter class
class MyListAdapter(private val context: Activity, private val products: ArrayList<Product>) : BaseAdapter() {
private var databaseHandler = DatabaseHandler(this.context)
override fun getCount(): Int {
return products.size
}
override fun getItem(position: Int): Any {
return products[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
#SuppressLint("ViewHolder")
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val inflater = LayoutInflater.from(context)
val customView = inflater.inflate(R.layout.custom_list, parent, false)
customView.tv_product_id.text = products[position].getProductID().toString()
customView.tv_product_name.text = products[position].getProductName()
customView.tv_product_quantity.text = products[position].getProductQuantity().toString()
customView.btn_edit_product.setOnClickListener {
val layout = LayoutInflater.from(context)
val view = layout.inflate(R.layout.update_dialog, null)
view.et_product_id.setText(products[position].getProductID().toString())
view.et_product_name.setText(products[position].getProductName())
view.et_product_quantity.setText(products[position].getProductQuantity().toString())
AlertDialog.Builder(context).setView(view).setTitle("update product info")
.setPositiveButton("yes") { _, _ ->
val status = databaseHandler.updateProduct(
view.et_product_id.text.toString().toInt(),
view.et_product_name.text.toString(),
view.et_product_quantity.text.toString().toInt()
)
if (status > -1) {
Toast.makeText(this.context, "record updated", Toast.LENGTH_LONG).show()
this.notifyDataSetChanged()
} else {
Toast.makeText(this.context, "update error", Toast.LENGTH_LONG).show()
}
}
.setNegativeButton("no") { _, _ -> }
.setIcon(R.drawable.ic_baseline_warning_24)
.show()
this.notifyDataSetChanged()
}
return customView
}
}
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()
}
}
}
}
}
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
}
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!
This is a simple chat messaging app, using Firebase Realtime Database and the Authentication. I have successfully created the database, but I cannot fetch the message from one user to another. Because, when I send the message/click the send button, it creates different nodes, I think its a string node with quotation mark in the name. The node structure I created is user-messages --> fromId(current user uid) --> toUid(to whom the messages are sent).
To make more clear explanation, this is the screenshot of what happened.
I think the problem is, it creates a different nodes with the "uid", which means string. So the database has two nodes, one with the "...", and the other the normal one. For some info, I use some library such as Picasso image load, groupie, and parcelize.
This is the source code for the ChatLogActivity
import...
class ChatLogActivity : AppCompatActivity() {
companion object {
val TAG = "ChatLog"
}
val adapter = GroupAdapter<ViewHolder>()
var toUser: User? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_log)
recyclerview_chat_log.adapter = adapter
toUser = intent.getParcelableExtra(NewMessageActivity.USER_KEY)
titleChatLog.text = toUser?.nama
listenForMessages()
arrowBack.setOnClickListener {
finish()
}
send_button_chat_log.setOnClickListener {
Log.d(TAG, "Attempt to send message.....")
performSendMessage()
}
}
private fun listenForMessages() {
val fromId = FirebaseAuth.getInstance().uid
val toId = toUser?.uid
val ref = FirebaseDatabase.getInstance().getReference("/user-messages/$fromId/$toId")
ref.addChildEventListener(object : ChildEventListener {
override fun onChildAdded(p0: DataSnapshot, p1: String?) {
val chatMessage = p0.getValue(ChatMessage::class.java)
if (chatMessage != null) {
Log.d(TAG, chatMessage.text)
if (chatMessage.fromId == FirebaseAuth.getInstance().uid) {
val currentUser = LatestMessageActivity.currentUser?: return
adapter.add(ChatFromItem(chatMessage.text, currentUser))
} else {
adapter.add(ChatToItem(chatMessage.text, toUser!!))
}
}
}
override fun onCancelled(p0: DatabaseError) {
}
override fun onChildChanged(p0: DataSnapshot, p1: String?) {
}
override fun onChildMoved(p0: DataSnapshot, p1: String?) {
}
override fun onChildRemoved(p0: DataSnapshot) {
}
})
}
private fun performSendMessage() {
val text = edittext_chat_log.text.toString()
val fromId = FirebaseAuth.getInstance().uid
val user = intent.getParcelableExtra<User>(NewMessageActivity.USER_KEY)
val toId = user.uid
val ref = FirebaseDatabase.getInstance().getReference("/user-messages/$fromId/$toId").push()
val toRef = FirebaseDatabase.getInstance().getReference("/user-messages/$toId/$fromId ").push()
if (fromId == null) return
val chatMessage = ChatMessage(ref.key!!, text, fromId, toId, System.currentTimeMillis() / 1000)
ref.setValue(chatMessage)
.addOnSuccessListener {
Log.d(TAG, "Save our chat message: ${ref.key}")
edittext_chat_log.text.clear()
recyclerview_chat_log.scrollToPosition(adapter.itemCount - 1)
}
toRef.setValue(chatMessage)
}
}
class ChatFromItem(val text: String, val user: User) : Item<ViewHolder>() {
override fun getLayout(): Int {
return R.layout.chat_from_row
}
override fun bind(viewHolder: ViewHolder, position: Int) {
val uri = user.profileImageUrl
val targetImageView = viewHolder.itemView.imageViewFrom
Picasso.get().load(uri).into(targetImageView)
viewHolder.itemView.textViewFrom.text = text
}
}
class ChatToItem(val text: String, val user: User) : Item<ViewHolder>() {
override fun getLayout(): Int {
return R.layout.chat_to_row
}
override fun bind(viewHolder: ViewHolder, position: Int) {
val uri = user.profileImageUrl
val targetImageView = viewHolder.itemView.imageViewTo
Picasso.get().load(uri).into(targetImageView)
viewHolder.itemView.textViewTo.text = text
}
}
And this is the ChatMessage, class that store the messages.
class ChatMessage(val id:String, val text: String, val fromId: String, val toId: String, val timeStamp: Long) {
constructor(): this("", "", "", "", -1)
}
I'm very glad if anyone can help this problem. Thank you