I'm trying to make an app that shows id and title and whenever some user is clicked, it should go to next activity and show id, userId, title, body, all this coming from implementing REST API from https://jsonplaceholder.typicode.com/post.
I know I have to past it with the intent, but do not know quite how to use it
this is the MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val retrofit= Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.addConverterFactory(create())
.build()
val api = retrofit.create(ApiService:: class.java)
api.fetchAllPosts().enqueue(object : Callback<List<Post>> {
override fun onResponse(call: Call<List<Post>>, response: Response<List<Post>>) {
showData(response.body()!!)
Log.d("jv", "onResponse")
}
override fun onFailure(call: Call<List<Post>>, t: Throwable) {
Log.d("jv", "onFailure")
}
})
}
private fun showData(posts: List<Post>) {
recyclerView.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = PostsAdapter(posts)
}
}
}
this is the adapter
class PostsAdapter(private val posts: List<Post>) : RecyclerView.Adapter<PostsAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.post_row, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = posts.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.userId.text= "Userid:" + posts[position].userId.toString()
holder.id1.text= "id:" + posts[position].id.toString()
holder.title.text= "Title:" + posts[position].title
holder.body.text= "Body:" + posts[position].body
}
class ViewHolder (itemView:View): RecyclerView.ViewHolder(itemView){
val userId: TextView = itemView.userid
val id1: TextView = itemView.id1
val title: TextView = itemView.title
val body: TextView = itemView.body
}
}
this is the post_row layout
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:src="#drawable/ic_check_circle_black_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent">
</ImageView>
<TextView
android:id="#+id/id1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:textColor="#000"
android:textSize="32sp"
app:layout_constraintBottom_toTopOf="#+id/userid"
app:layout_constraintLeft_toRightOf="#+id/photo"
app:layout_constraintTop_toTopOf="parent"
tools:text="1" />
<TextView
android:id="#+id/userid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:textColor="#000"
android:textSize="32sp"
app:layout_constraintBottom_toTopOf="#+id/title"
app:layout_constraintLeft_toRightOf="#+id/photo"
app:layout_constraintTop_toBottomOf="#+id/id1"
tools:text="1" />
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:textColor="#000"
android:textSize="32sp"
app:layout_constraintBottom_toTopOf="#+id/body"
app:layout_constraintLeft_toRightOf="#+id/photo"
app:layout_constraintTop_toBottomOf="#+id/userid"
tools:text="JV" />
<TextView
android:id="#+id/body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:textColor="#000"
android:textSize="32sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="#+id/photo"
app:layout_constraintTop_toBottomOf="#+id/title"
tools:text="JV" />
</androidx.constraintlayout.widget.ConstraintLayout>
You can pass the Post object to the next activity as a Serializable as following.
• At the first step, the Post class should implement Serializable interface:
class Post : Serializable {
// ...
}
• Second step: When the user clicks on each item, put the related Post object to the Intent extras:
val intent = Intent(applicationContext, NextActivity::class.java)
intent.putExtra("post", post)
startActivity(intent)
• Third step: In onCreate of NextActivity, get the object from Intent extras:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val post = intent.getSerializableExtra("post") as Post
// ...
}
* EDIT
I have changed your code to listen to the RecyclerView item clicks. In this way, some changes happened to MainActivity and PostsAdapter classes as well as adding OnRecyclerViewItemClickListener. Notice that Post class should implement Serializable.
MainActivity.kt
class MainActivity : AppCompatActivity(), OnRecyclerViewItemClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val retrofit = Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.addConverterFactory(create())
.build()
val api = retrofit.create(ApiService::class.java)
api.fetchAllPosts().enqueue(object : Callback<List<Post>> {
override fun onResponse(call: Call<List<Post>>, response: Response<List<Post>>) {
showData(response.body()!!)
Log.d("jv", "onResponse")
}
override fun onFailure(call: Call<List<Post>>, t: Throwable) {
Log.d("jv", "onFailure")
}
})
}
override fun onItemClick(post: Post) {
val intent = Intent(applicationContext, NextActivity::class.java)
intent.putExtra("post", post)
startActivity(intent)
}
private fun showData(posts: List<Post>) {
recyclerView.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = PostsAdapter(posts, this#MainActivity)
}
}
}
PostsAdapter.kt
class PostsAdapter(
private val posts: List<Post>,
private val itemClickListener: OnRecyclerViewItemClickListener
) : RecyclerView.Adapter<PostsAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.post_row, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = posts.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.userId.text = "Userid:" + posts[position].userId.toString()
holder.id1.text = "id:" + posts[position].id.toString()
holder.title.text = "Title:" + posts[position].title
holder.body.text = "Body:" + posts[position].body
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
init {
itemView.setOnClickListener {
itemClickListener.onItemClick(posts[adapterPosition])
}
}
val userId: TextView = itemView.userid
val id1: TextView = itemView.id1
val title: TextView = itemView.title
val body: TextView = itemView.body
}
}
OnRecyclerViewItemClickListener.kt
interface OnRecyclerViewItemClickListener {
fun onItemClick(post: Post)
}
Post.kt
import com.google.gson.annotations.SerializedName
import java.io.Serializable
data class Post(
#SerializedName("userId") val userId: Int = 0,
#SerializedName("id") val id: Int = 0,
#SerializedName("title") val title: String? = null,
#SerializedName("body") val body: String? = null
) : Serializable
NextActivity.kt
class NextActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_next)
val post = intent.getSerializableExtra("post") as Post
// do whatever you want with post!
}
}
To pass an object from one activity to another, your class Post should implement either Serializable or Parcelable interfaces.
data class Post(val title: String, ... ): Serializable
Then you can put object inside the intent. In your RecyclerAdapter
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
...
holder.body.text= "Body:" + posts[position].body
holder.setOnClickListener {
val intent = Intent(holder.context, ActivityYouArePassingTo::class.java)
intent.putExtra("Post", posts[position])
startActivity(intent)
}
}
In the activity that you are navigating to (here ActivityYouArePassingTo)
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
...
// Here is the post from previous activity
val post = intent.getSerializableExtra("Post") as Post
}
Related
I can't view my data from api in recyclerview. Can you help me what is the problem?
MyCode:
Adapter:
class NewsAdapter(private val data: List<AllData>) :
RecyclerView.Adapter<NewsAdapter.MyViewHolder>() {
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val title = view.findViewById<TextView>(R.id.news_info)
val imageView = view.findViewById<ImageView>(R.id.news_img)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.main_activity_cardview_card_menu, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val news:AllData=data[position]
holder.title.text = news.haberler[position].title
Glide.with(holder.imageView.context).load(news.haberler[position].imageUrl).into(holder.imageView)
}
override fun getItemCount(): Int {
return data.size
}
}
activity_main.xml
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/news_recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/view" />
MainActivity:
class MainActivity : AppCompatActivity() {
private val itemList = ArrayList<MenuCardModel>()
private lateinit var menuCardAdapter: MenuCardAdapter
private lateinit var newsAdapter: NewsAdapter
val data = ArrayList<AllData>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadNews()
}
private fun loadNews() {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(ApiInterface::class.java)
val call = service.getNews()
call.enqueue(object :Callback<AllData>{
override fun onResponse(
call: Call<AllData>,
response: Response<AllData>
) {
response.body()?.let {
data.add(it)
newsAdapter = NewsAdapter(data)
news_recyclerView.adapter = newsAdapter
}
}
override fun onFailure(call: Call<AllData>, t: Throwable) {
t.printStackTrace()
}
})
}
}
Model :
data class Haberler(
val content: String,
val id: Int,
val imageUrl: String,
val orderNo: Int,
val parentId: Any,
val title: String,
val videoUrl: String
)
AllData :
data class AllData(
val haberler: List<Haberler>,
val istatistik: Istatistik,
val konferans: Konferans,
val kvkk: String,
val liveChat: String,
val pratikBilgiler: List<PratikBilgiler>,
val sgkTv: List<SgkTv>,
val sss: List<Ss>,
val state: Boolean,
val subjects: List<Subject>
)
Hello, I can't view my data from api in recyclerview. Can you help me what is the problem?
Hello, I can't view my data from api in recyclerview. Can you help me what is the problem?
Hello, I can't view my data from api in recyclerview. Can you help me what is the problem?
It looks like you forgot to set layout manager to your recyclerview before you set adapter
There are two ways you can set layout manager
First way by xml
<androidx.recyclerview.widget.RecyclerView
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
...
/>
second way is by code
newsAdapter = NewsAdapter(data)
news_recyclerView.layoutManager = LinearLayoutManager(context) // line which you forgot
news_recyclerView.adapter = newsAdapter
for more understanding about layout managers , you can refer below links
https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.LayoutManager
Just add layoutManager this line in RecyclerView as follow:
<androidx.recyclerview.widget.RecyclerView
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
...
/>
You are missing layout manager for recycler view
I've tried to display the join data from two entity into the recyclerview. However, when I add new data that has the same idBarang (FK) as the old data that has the same idBarang, recyclerview display the same data row.
This is the code snippet:
ENTITY
#Entity(
foreignKeys = [
ForeignKey(entity = Item::class, parentColumns = ["id"], childColumns = ["id_barang"])
],
indices = [Index("id_barang")]
)
data class Purchaseorder(
#PrimaryKey(autoGenerate = true)
val id: Int = 0,
#ColumnInfo(name = "no_po")
val noPO: String,
#ColumnInfo(name = "id_barang")
val idBarang: Int,
#ColumnInfo(name = "tanggal_po")
val tanggalPO: String,
#ColumnInfo(name = "jumlah_po")
val jumlahPO: Int,
#ColumnInfo(name = "harga_po")
val hargaPO: Double
)
#Entity
data class Item(
#PrimaryKey(autoGenerate = true)
val id: Int = 0,
#ColumnInfo(name = "kode_barang")
val kodeBarang: String,
#ColumnInfo(name = "nama")
val namaBarang: String,
#ColumnInfo(name = "min_stok")
val minStok: Int,
val stok: Int,
val unit: String
)
data class PurchaseorderAndItem(
#Embedded val item: Item,
#Relation(
parentColumn = "id",
entityColumn = "id_barang"
)
val purchaseorder : List<Purchaseorder>,
// val purchaseorder : Purchaseorder,
)
/**
* Returns the passed in price in currency format.
*/
fun Purchaseorder.getFormattedPrice(): String =
NumberFormat.getCurrencyInstance().format(hargaPO)
fun Purchaseorder.getUnitPrice(): String =
NumberFormat.getCurrencyInstance().format(hargaPO/jumlahPO)
fun Purchaseorder.getLongDate() : String {
val month = arrayOf("Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember")
val dateTemp = tanggalPO.split("/")
return dateTemp[0] + " " +month[dateTemp[1].toInt()-1] + " " +dateTemp[2]
}
DAO
#Dao
interface PurchaseorderDao {
#Transaction
#Query("SELECT * from purchaseorder as po INNER JOIN item ON item.id = po.id_barang WHERE po.id = :id")
fun retrievePurchaseorder(id: Int): Flow<PurchaseorderAndItem>
#Transaction
#Query("SELECT * FROM purchaseorder as po INNER JOIN item ON item.id = po.id_barang ORDER BY po.id DESC")
fun getPurchaseorders(): Flow<List<PurchaseorderAndItem>>
}
VIEWMODEL
class PurchaseorderViewModel(private val purchaseorderDao: PurchaseorderDao) : ViewModel() {
var allPurchaseorders: LiveData<List<PurchaseorderAndItem>> = purchaseorderDao.getPurchaseorders().asLiveData()
fun retrievePurchaseorder(id: Int): LiveData<PurchaseorderAndItem> {
return purchaseorderDao.retrievePurchaseorder(id).asLiveData()
}
...
class PurchaseorderViewModelFactory(
private val purchaseorderDao: PurchaseorderDao
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(PurchaseorderViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return PurchaseorderViewModel(purchaseorderDao) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
LIST FRAGMENT
class PurchaseorderListFragment : Fragment() {
private val viewModel: PurchaseorderViewModel by activityViewModels {
PurchaseorderViewModelFactory(
(activity?.application as InventoryApplication).database.purchaseorderDao(),
)
}
private var _binding: FragmentPurchaseorderListBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentPurchaseorderListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter = PurchaseorderListAdapter {
val action =
PurchaseorderListFragmentDirections.actionPurchaseorderListFragmentToPurchaseorderDetailFragment(it.purchaseorder[0].id)
this.findNavController().navigate(action)
}
binding.recyclerViewPurchaseorder.layoutManager = object : LinearLayoutManager(this.context) {
override fun checkLayoutParams(lp: RecyclerView.LayoutParams): Boolean {
lp.width = width
return true
}
}
binding.recyclerViewPurchaseorder.adapter = adapter
viewModel.allPurchaseorders.observe(this.viewLifecycleOwner) { purchaseorder ->
purchaseorder.let {
adapter.submitList(it)
}
}
binding.floatingActionButton.setOnClickListener {
val action = PurchaseorderListFragmentDirections.actionPurchaseorderListFragmentToAddPurchaseorderFragment(
getString(R.string.add_fragment_title)
)
this.findNavController().navigate(action)
}
}
}
LIST ADAPTER
class PurchaseorderListAdapter(private val onPurchaseorderClicked: (PurchaseorderAndItem) -> Unit) :
ListAdapter<PurchaseorderAndItem, PurchaseorderListAdapter.PurchaseorderViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PurchaseorderViewHolder {
return PurchaseorderViewHolder(
PurchaseorderListItemBinding.inflate(
LayoutInflater.from(
parent.context
)
)
)
}
override fun onBindViewHolder(holder: PurchaseorderViewHolder, position: Int) {
val current = getItem(position)
holder.itemView.setOnClickListener {
onPurchaseorderClicked(current)
}
holder.bind(current)
}
class PurchaseorderViewHolder(private var binding: PurchaseorderListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(purchaseorder: PurchaseorderAndItem) {
val purchaseorders = purchaseorder.purchaseorder[0]
val item = purchaseorder.item
binding.rvNamaBarangNNoPO.text = binding.rvNamaBarangNNoPO.context.getString(R.string.kode_cate,item.namaBarang,purchaseorders.noPO)
binding.rvHargaPO.text = purchaseorders.getFormattedPrice()
binding.rvJumlahPO.text = purchaseorders.jumlahPO.toString()
binding.rvTanggalPO.text = purchaseorders.tanggalPO
// Log.i("ininya: ", purchaseorder)
}
}
companion object {
private val DiffCallback = object : DiffUtil.ItemCallback<PurchaseorderAndItem>() {
override fun areItemsTheSame(
oldItem: PurchaseorderAndItem,
newItem: PurchaseorderAndItem,
): Boolean {
return oldItem.purchaseorder[0].id == newItem.purchaseorder[0].id
}
override fun areContentsTheSame(
oldItem: PurchaseorderAndItem,
newItem: PurchaseorderAndItem,
): Boolean {
return oldItem.purchaseorder[0].noPO == newItem.purchaseorder[0].noPO
}
}
}
}
XML RECYCLER ROW
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="5.7">
<TextView
android:id="#+id/rv_namaBarang_N_noPO"
style="#style/Widget.Inventory.ListItemTextView"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="2"
android:fontFamily="sans-serif"
tools:text="Terigu (P001)" />
<TextView
android:id="#+id/rv_tanggalPO"
style="#style/Widget.Inventory.ListItemTextView"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1.2"
android:layout_marginHorizontal="#dimen/margin_between_elements"
android:fontFamily="sans-serif-medium"
android:textAlignment="textStart"
tools:text="21/12/2022" />
<TextView
android:id="#+id/rv_jumlahPO"
style="#style/Widget.Inventory.ListItemTextView"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:fontFamily="sans-serif-medium"
android:textAlignment="textEnd"
tools:text="100Kg" />
<TextView
android:id="#+id/rv_hargaPO"
style="#style/Widget.Inventory.ListItemTextView"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1.5"
android:fontFamily="sans-serif-medium"
android:textAlignment="textEnd"
tools:text="110.000.000" />
</LinearLayout>
As described in the title, I have a firestore collection with two documents which I want to show in my recycler View. I can see in logs that data is accessed correctly and my recycler view adapter size also shows correct number but recycler view does not show up. I thought it might be due to me using view binding but I doubt that is the issue. I am out of ideas, any help is appreciated!
This is my RecyclerView adapter:
open class BoardItemsAdapter(private val context: Context, private val list: ArrayList<Board>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var boardItemClickListener: BoardItemClickInterface? = null;
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding = ItemBoardBinding.inflate(LayoutInflater.from(parent.context),parent,false);
return MyViewHolder(binding);
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val model = list[position];
if(holder is MyViewHolder){
Glide.with(context)
.load(model.image)
.centerCrop()
.placeholder(R.drawable.ic_board_place_holder)
.into(holder.binding.ivBoardImageItemBoard);
holder.binding.tvNameItemBoard.text = model.name;
holder.binding.tvCreatedByItemBoard.text = "Created by: ${model.createdBy}";
holder.itemView.setOnClickListener {
if(boardItemClickListener != null){
boardItemClickListener!!.onClick(position, model);
}
}
}
}
override fun getItemCount(): Int {
return list.size;
}
inner class MyViewHolder(val binding:ItemBoardBinding):RecyclerView.ViewHolder(binding.root){
}
}
A snippet of FirestoreClass
fun getBoardsList(activity: MainActivity){
mFirestore.collection(Constants.BOARDS)
.whereArrayContains(Constants.ASSIGNED_TO,getCurrentUserID())
.get()
.addOnSuccessListener {
document ->
Log.i(activity.javaClass.simpleName, document.documents.toString()); //logs correct data
val boardList: ArrayList<Board> = ArrayList();
for(i in document.documents){
val board = i.toObject(Board::class.java)!!;
board.documentID = i.id;
boardList.add(board)
}
activity.populateBoardsListToUI(boardList);
}.addOnFailureListener {
e ->
activity.hideProgressDialog();
Log.e(activity.javaClass.simpleName,"Error while creatng a board",e);
}
}
A snippet of MainActivity:
fun populateBoardsListToUI(boardsList: ArrayList<Board>){
mainContentBinding = MainContentBinding.inflate(layoutInflater);
hideProgressDialog();
if(boardsList.size > 0){
mainContentBinding.rvBoards.visibility = View.VISIBLE;
mainContentBinding.tvNoBoardsAvailable.visibility = View.GONE;
mainContentBinding.rvBoards.layoutManager = LinearLayoutManager(this);
mainContentBinding.rvBoards.setHasFixedSize(true);
val adapter = BoardItemsAdapter(this#MainActivity, boardsList);
mainContentBinding.rvBoards.adapter = adapter;
Log.i("POPUI","Board adapter size: ${adapter.itemCount}");
}
else{
mainContentBinding.rvBoards.visibility = View.GONE;
mainContentBinding.tvNoBoardsAvailable.visibility = View.VISIBLE;
}
}
MainContent.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ic_background"
android:gravity="center"
android:orientation="vertical"
android:padding="#dimen/main_screen_content_padding"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".activities.MainActivity">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:elevation="#dimen/card_view_elevation"
app:cardCornerRadius="#dimen/card_view_corner_radius"
>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_boards"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
/>
<TextView
android:id="#+id/tv_no_boards_available"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/no_boards_available"
android:gravity="center"
android:textColor="#color/secondary_text_color"
android:textSize="#dimen/no_boards_available_text_size"
/>
</androidx.cardview.widget.CardView>
</LinearLayout>
Board class
import android.os.Parcel
import android.os.Parcelable
import kotlinx.parcelize.Parceler
import kotlinx.parcelize.Parcelize
#Parcelize
data class Board(
val name: String = "",
val image : String = "",
val createdBy: String = "",
val assignedTo: ArrayList<String> = ArrayList(),
var documentID: String = ""
): Parcelable{
constructor(parcel: Parcel): this(
parcel.readString()!!,
parcel.readString()!!,
parcel.readString()!!,
parcel.createStringArrayList()!!,
parcel.readString()!!
)
override fun describeContents() = 0
companion object : Parceler<Board> {
override fun Board.write(dest: Parcel, flags: Int) = with(dest) {
writeString(name);
writeString(image);
writeString(createdBy);
writeStringList(assignedTo);
writeString(documentID);
}
override fun create(parcel: Parcel): Board = TODO()
}
}
Database screenshot
Database screenshot
I am developing an Android application in Kotlin and I implemented a RecyclerView.
Each item of this RecyclerView contains one of the 3 combinations below:
a TextView + a TextView
a TextView + a Button
a TextView + a Spinner
In this activity, I have initialized my parameter list and I can add parameters of one of the 3 types mentioned above by clicking on the TEST button.
My problem is a UI problem: when there are a lot of parameters containing Spinners such that you have to scroll to see them, an arrow of a spinner is displayed at the very top of the RecyckerView (or at the very bottom sometimes). This spinner arrow starts flashing when I scroll and finally stops after a few seconds. Please note that I can't click on this arrow. I don't understand my mistake, can you help me?
Here is a link to a Youtube video recording of the behavior I have described that will help you better understand it.
Here is my view when I am on top of my RecyclerView:
This is my view when I scrolled down in the RecyclerView and there are parameters containing Spinners that are only visible when scrolling up again.
Here is my CustomAdapter class:
class CustomAdapter(private var parameterList: List<Parameter>) :
RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
sealed class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
class TextViewHolder(itemView: View) : ViewHolder(itemView) {
val textViewName: TextView = itemView.findViewById(R.id.parameterName)
val textViewValue: TextView = itemView.findViewById(R.id.parameterValue)
}
class ButtonViewHolder(itemView: View) : ViewHolder(itemView) {
var textViewName: TextView = itemView.findViewById(R.id.parameterName)
val buttonViewValue: Button = itemView.findViewById(R.id.parameterButton)
}
class SpinnerViewHolder(itemView: View) : ViewHolder(itemView) {
val textViewName: TextView = itemView.findViewById(R.id.parameterName)
val spinnerViewValue: Spinner = itemView.findViewById(R.id.parameterSpinner)
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
return when (viewType) {
0 -> {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.parameter_text, viewGroup, false)
ViewHolder.TextViewHolder(view)
}
1 -> {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.parameter_button, viewGroup, false)
ViewHolder.ButtonViewHolder(view)
}
2 -> {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.parameter_spinner, viewGroup, false)
ViewHolder.SpinnerViewHolder(view)
}
else -> throw IllegalArgumentException("Invalid view type")
}
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
when (viewHolder) {
is ViewHolder.TextViewHolder -> {
viewHolder.textViewName.text = parameterList[position].parameterName
viewHolder.textViewValue.text = parameterList[position].parameterValue as CharSequence?
}
is ViewHolder.ButtonViewHolder -> {
viewHolder.textViewName.text = parameterList[position].parameterName
viewHolder.buttonViewValue.text = parameterList[position].parameterValue as CharSequence?
}
is ViewHolder.SpinnerViewHolder -> {
viewHolder.textViewName.text = parameterList[position].parameterName
viewHolder.spinnerViewValue.adapter = parameterList[position].parameterValue as SpinnerAdapter?
}
}
}
override fun getItemViewType(position: Int) : Int {
return parameterList[position].parameterType
}
override fun getItemCount() = parameterList.size
fun addNewList(newList: List<Parameter>){
parameterList = newList;
notifyDataSetChanged();
}
}
Each of the different types of parameters inherited from the class Parameter:
open class Parameter(open var parameterName: String? = "", open var parameterType: Int = 0, open var parameterValue: Any) {
}
ParameterText class:
class ParameterText(override var parameterName: String?, override var parameterValue: Any = "") : Parameter(parameterName, parameterValue = parameterValue!!) {
override var parameterType: Int = 0
}
ParameterButton class:
class ParameterButton(override var parameterName: String?, override var parameterValue: Any = "") : Parameter(parameterName, parameterValue = parameterValue!!) {
override var parameterType: Int = 1
}
ParameterSpinner class:
class ParameterSpinner(override var parameterName: String?, override var parameterValue: Any) : Parameter(parameterName, parameterValue = parameterValue) {
override var parameterType: Int = 2
}
Here is my NFCActivity:
class NFCActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_nfc)
buttonTest.setOnClickListener() {
parametersList = parametersList + ParameterSpinner(
"Led", ArrayAdapter(
this,
android.R.layout.simple_spinner_item, resources.getStringArray(R.array.LedState)
)
)
(rv_parameters2.adapter as CustomAdapter).addNewList(parametersList)
}
rv_parameters2.layoutManager = LinearLayoutManager(this)
rv_parameters2.adapter = CustomAdapter(parametersList)
}
private var parametersList : List<Parameter> = listOf<Parameter> (
ParameterText("Temperature", "24°C"),
ParameterText("Temperature", "24°C")
)
companion object {
fun getStartIntent(context: Context): Intent {
return Intent(context, NFCActivity::class.java)
}
}
}
NFCActivity's layout:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.ble.NFCActivity">
<Button
android:id="#+id/buttonTest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TEST"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_parameters2"
tools:listitem="#layout/parameter_text"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/buttonTest"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
It is a note taking app. I am using Cloud Firestore to store data. Data got added into database but recycler view is not showing anything. Below are the code snippets.
My Data Model Class:
class Notes {
var id:String?= null
var title: String? = null
var description: String? = null
var created: Timestamp? =null
constructor() {}
constructor(id:String,title: String?, description:String?,created:Timestamp?) {
this.id=id
this.title = title
this.description=description
this.created=created
}
}
AddNotesActivity
class AddNoteActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note)
btn_add_note.setOnClickListener{
val noteTitle = note_title.text.toString()
val noteDescription =note_description.text.toString()
if(noteTitle.isNotEmpty() && noteDescription.isNotEmpty()){
addNotes(noteTitle,noteDescription)
Toast.makeText(this,"note added successfully",Toast.LENGTH_SHORT).show()
onBackPressed()
}
}
}
private fun addNotes(title:String, description:String){
val currentUserId = FirebaseAuth.getInstance().currentUser!!.uid
var note = Notes(currentUserId,title,description, Timestamp(Date()))
FirebaseFirestore.getInstance().collection("notes").add(note).addOnSuccessListener {
Log.i("AddNoteActivity","Note added")
}
}
}
NotesActivity(showing recyclerview):
class NotesActivity : AppCompatActivity() {
lateinit var notesAdapter: NotesAdapter
lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_notes)
recyclerView = findViewById(R.id.rv_notes)
setUpRecyclerView()
floating_btn.setOnClickListener {
startActivity(Intent(this, AddNoteActivity::class.java))
}
}
private fun setUpRecyclerView() {
val query:Query= FirebaseFirestore.getInstance().collection("notes").whereEqualTo("id",FirebaseAuth.getInstance().currentUser!!.uid)
val options:FirestoreRecyclerOptions<Notes> = FirestoreRecyclerOptions.Builder<Notes>().setQuery(query,Notes::class.java).build()
notesAdapter = NotesAdapter(options)
recyclerView.adapter = notesAdapter
notesAdapter!!.startListening()
}
}
Adapter class:
class NotesAdapter(options:FirestoreRecyclerOptions<Notes>):FirestoreRecyclerAdapter<Notes,NotesAdapter.MyViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotesAdapter.MyViewHolder {
val itemView= LayoutInflater.from(parent.context).inflate(R.layout.each_note_view,parent,false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, p1: Int, NotesModel: Notes) {
holder.title.text = NotesModel.title
holder.description.text= NotesModel.description
val date=DateFormat.getDateInstance(DateFormat.MEDIUM).format(NotesModel.created)
holder.date.text = date.toString()
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val title : TextView = itemView.title_text
val description: TextView =itemView.description_text
val date : TextView =itemView.date_created
}
}
But when getting data from querysnapshot, logcat is showing perfect data from firestore database:
val query = FirebaseFirestore.getInstance().collection("notes").whereEqualTo("id", FirebaseAuth.getInstance().currentUser!!.uid).get().addOnSuccessListener {
val doc = it.documents
for (i in doc) {
Log.i("NotesActivity", i.data.toString())
}
}
Logcat: {created=Timestamp(seconds=1611116973, nanoseconds=14000000),
description=day, id=wLxCTMLGZpaWNs1b8Uhf3HoRUgz2, title=go}
I spend two days on this, but not getting any solution. I would be thankful if anybody can solve the issue.
Below is XML file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_notes"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="7"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/floating_btn"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginEnd="8dp"
android:backgroundTint="#color/light_blue"
android:src="#drawable/floating_btn"/>