Here's the minimal test case what I've managed to make.
It combines with a RecyclerView and TextView, the activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="#+id/textViewBatteryInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hi, I'm watching you!"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/my_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
With the MainActivity.kt:
package kot.bignerd.recyclerview101
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val items = arrayListOf<String>()
for (i in 10..50) {
items.add("Here's the $i th")
}
my_rv.layoutManager = LinearLayoutManager(this)
my_rv.adapter = MyListAdapter(items, this)
//my_rv.visibility = View.GONE
}
}
With a very simple adapter(MyListAdapter):
package kot.bignerd.recyclerview101
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
class MyListAdapter(val datas: List<String>, val context: Context) : RecyclerView.Adapter<MyListAdapter.InnerHolder>() {
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): MyListAdapter.InnerHolder {
var itemView: View = LayoutInflater.from(context).inflate(R.layout.item_rv, p0, false)
return InnerHolder(itemView)
}
override fun getItemCount(): Int = datas.size
class InnerHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var itemText: TextView = itemView.findViewById(R.id.item_tv)
}
override fun onBindViewHolder(p0: MyListAdapter.InnerHolder, p1: Int) {
p0?.itemText?.text = datas[p1]
}
}
The R.layout.item_rv mentioned in the adapter is (item_rv.xml):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="#+id/item_cl"
android:layout_height="wrap_content">
<TextView
android:id="#+id/item_tv"
android:layout_width="0dp"
android:layout_height="50dp"
android:text="TestRV"
android:textSize="18sp"
android:gravity="center"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" tools:layout_editor_absoluteY="16dp"
app:layout_constraintHorizontal_bias="0.0"/>
<Button
android:text="Close"
android:layout_width="86dp"
android:layout_height="wrap_content" tools:layout_editor_absoluteY="16dp"
android:id="#+id/button" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"/>
</android.support.constraint.ConstraintLayout>
I was wondering if maybe we could make the RecyclerView disappeared when I click any one of the button inside it? Just like the code my_rv.visibility = View.GONE in the MainActivity.kt:
Your adapter need to receive a Listener as an object in order to react to clicks.
Here's an example:
package kot.bignerd.recyclerview101
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
class MyListAdapter(val datas: List<String>, val context: Context, val clickListener: ClickListener) : RecyclerView.Adapter<MyListAdapter.InnerHolder>() {
public interface ClickListener {
fun onItemClicked()
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): MyListAdapter.InnerHolder {
var itemView: View = LayoutInflater.from(context).inflate(R.layout.item_rv, p0, false)
return InnerHolder(itemView)
}
override fun getItemCount(): Int = datas.size
class InnerHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var itemText: TextView = itemView.findViewById(R.id.item_tv)
var button: Button = itemView.findViewById(R.id. button)
fun bindView(text: String, clickListener: ClickListener) {
itemText.text = text
button.setOnClickListner {
clickListner.onItemClicked()
}
}
}
override fun onBindViewHolder(holder: MyListAdapter.InnerHolder, position: Int) {
holder.bindView(datas[position], clickListener)
}
}
Then change the signature of the activity as follow:
class MainActivity : AppCompatActivity(), ClickListener {
and implement the function as follow:
override fun onItemClicked() {
my_rv.visibility = View.GONE
}
Last, but not least, change how you initialise your adapter:
MyListAdapter(items, this, this)
Related
I'm hoping to create a checkbox for items that I have in a list.
I was looking at applying this by using a boolean value for my items i.e if attribute 'favourite' = true, then checkbox is highlighted and vice versa if false.
Unfortunately, so far I am unsure of the logic for this and how to connect it to my view (Checkbox).
My view is as follows:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
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"
android:layout_marginBottom="8dp"
android:elevation="24dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:id="#+id/imageIcon"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:contentDescription="#string/change_hike_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/hikeName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textAlignment="viewStart"
android:textSize="30sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.048"
app:layout_constraintStart_toEndOf="#+id/imageIcon"
app:layout_constraintTop_toTopOf="parent"
tools:text="A Title" />
<TextView
android:id="#+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textAlignment="viewStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.052"
app:layout_constraintStart_toEndOf="#+id/imageIcon"
app:layout_constraintTop_toBottomOf="#id/hikeName"
tools:text="A Description" />
<CheckBox
android:id="#+id/favourite"
style="?android:attr/starStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Adapter:
package org.wit.hikingtrails.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import org.wit.hikingtrails.databinding.CardHikeBinding
import org.wit.hikingtrails.models.HikeModel
interface HikeListener {
fun onHikeClick(hike: HikeModel)
}
class HikeAdapter constructor(private var hikes: List<HikeModel>,
private val listener: HikeListener) :
RecyclerView.Adapter<HikeAdapter.MainHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainHolder {
val binding = CardHikeBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
return MainHolder(binding)
}
override fun onBindViewHolder(holder: MainHolder, position: Int) {
val hike = hikes[holder.adapterPosition]
holder.bind(hike, listener)
}
override fun getItemCount(): Int = hikes.size
class MainHolder(private val binding : CardHikeBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(hike: HikeModel, listener: HikeListener) {
binding.hikeName.text = hike.name
binding.description.text = hike.description
binding.favourite.text = hike.favourite.toString()
if (hike.image != ""){
Picasso.get()
.load(hike.image)
.resize(200, 200)
.into(binding.imageIcon)
}
binding.root.setOnClickListener { listener.onHikeClick(hike) }
}
}
}
View:
package org.wit.hikingtrails.views.hikeList
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import org.wit.hikingtrails.R
import org.wit.hikingtrails.adapters.HikeAdapter
import org.wit.hikingtrails.adapters.HikeListener
//import org.wit.hikingtrails.databinding.ActivityHikeListBinding
import org.wit.hikingtrails.main.MainApp
import org.wit.hikingtrails.models.HikeModel
import android.content.Intent
import android.view.*
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.wit.hikingtrails.databinding.ActivityHikeListBinding
import org.wit.hikingtrails.views.BaseView
import timber.log.Timber
class HikeListView : BaseView(), HikeListener {
lateinit var app: MainApp
lateinit var binding: ActivityHikeListBinding
lateinit var presenter: HikeListPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHikeListBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.toolbar.title = title
setSupportActionBar(binding.toolbar)
presenter = HikeListPresenter(this)
val layoutManager = LinearLayoutManager(this)
binding.recyclerView.layoutManager = layoutManager
updateRecyclerView()
}
// override fun showHikes(hikes: List<HikeModel>) {
// recyclerView.adapter = HikeAdapter(hikes, this)
// recyclerView.adapter?.notifyDataSetChanged()
// }
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onResume() {
//update the view
super.onResume()
updateRecyclerView()
binding.recyclerView.adapter?.notifyDataSetChanged()
Timber.i("recyclerView onResume")
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item?.itemId) {
R.id.item_add -> presenter.doAddHike()
R.id.item_map -> presenter.doShowHikesMap()
R.id.logout -> presenter.doLogout()
}
return super.onOptionsItemSelected(item)
}
override fun onHikeClick(hike: HikeModel) {
presenter.doEditHike(hike)
}
private fun updateRecyclerView(){
GlobalScope.launch(Dispatchers.Main){
binding.recyclerView.adapter =
HikeAdapter(presenter.getHikes(), this#HikeListView)
}
}
}
Add favorite property to your HikeModel like this
data class HikeModel(
... code
// use mutable property bcs you will update it later when checkbox is checked or uncheck.
var favorite: Boolean = false // false by default
)
and then
fun bind(hike: HikeModel, listener: HikeListener) {
...
binding.favourite.isChecked = hike.favorite
binding.favourite.setOnClickListener {
hike.favorite = binding.favourite.isChecked
listener.onHikeClick(hike)
}
...
}
i'd like to know what i'm doing wrong over here. I'm building a CRUD app in Kotlin, and i'm using the recyclerview to make the readData page. The problem is, when i'm on the readData page doesn't show me anything, just the text views, so i debugged and when i join in the page, show this message: "Recyclerview No adapter attached; skipping layout".
Here's my code:
(i'm brazilian, so, some words are in portuguese, but you'll get it.)
verDados.kt
package com.nicolas.csrd
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.google.firebase.database.*
import kotlinx.android.synthetic.main.activity_ver_database.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class verDados : AppCompatActivity() {
private lateinit var database: FirebaseDatabase
private lateinit var reference: DatabaseReference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ver_database)
database = FirebaseDatabase.getInstance()
reference = database.getReference("usuarios")
verDados()
btn_voltar.setOnClickListener() {
startActivity(Intent(this#verDados, Dashboard::class.java))
finish()
}
}
private fun verDados() {
reference.addValueEventListener(object: ValueEventListener{
override fun onCancelled(p0: DatabaseError) {
Log.e("cancelar", p0.toString())
}
override fun onDataChange(p0: DataSnapshot) {
//Colocando os usuarios numa lista
var list = ArrayList<DatabaseModelo>()
for (data in p0.children) {
val model = data.getValue(DatabaseModelo::class.java)
list.add(model as DatabaseModelo)
}
if (list.size > 0) {
val ususariosModelo = usuariosModelo(list)
recyclerview.adapter = ususariosModelo
}
}
})
}
}
DatabaseModel
package com.nicolas.csrd
class DatabaseModelo() {
lateinit var email: String
lateinit var senha: String
constructor(email: String, senha: String) : this() {
this.email = email
this.senha = senha
}
}
usuariosModelo.kt (recyclerview adapter)
package com.nicolas.csrd
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.menu.ActionMenuItemView
import androidx.appcompat.view.menu.MenuView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.usuarios_modelo.view.*
class usuariosModelo(val list: ArrayList<DatabaseModelo>): RecyclerView.Adapter<usuariosModelo.ViewHolder>() {
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val email = itemView.campo_email
val senha = itemView.campo_senha
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.usuarios_modelo, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.email.text = list[position].email
holder.senha.text = list[position].senha
}
override fun getItemCount(): Int {
return list.size
}
}
----- XML FILES -----
activity_ver_database.xml (read data page)
<?xml version="1.0" encoding="utf-8"?>
<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"
android:background="#color/dark"
tools:context=".Login">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal" >
<ImageView
android:id="#+id/btn_voltar"
android:layout_width="50dp"
android:layout_height="30dp"
android:layout_gravity="start"
android:layout_marginStart="5dp"
android:layout_marginTop="23dp"
android:src="#drawable/back_arrow" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DADOS"
android:textColor="#7ec1d1"
android:textSize="30sp"
android:textStyle="bold"
android:layout_marginTop="20dp"
android:letterSpacing="0.15"
android:fontFamily="#font/montserrat_medium"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Todos os usuários cadastrados"
android:textColor="#7ec1d1"
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginTop="0dp"
android:fontFamily="#font/montserrat_thin"
/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/recyclerview"
android:layout_margin="10sp"/>
<TextView
android:id="#+id/btn_cadastrar"
android:layout_width="350dp"
android:layout_height="50dp"
android:layout_marginTop="30dp"
android:backgroundTint="#7ec1d1"
android:gravity="center_horizontal"
android:fontFamily="#font/montserrat_regular"
android:text="Deseja cadastrar alguém? Clique aqui."
android:textColor="#color/white2"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
You are getting this error because you have no defined an adaptor for the RecyclerView that you are using. Somewhere inn your onCreate method, create the adaptor and set it to the RecyclerView. Something like:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ver_database)
recyclerView = findViewById(R.id.recyclerview)
recyclerView.apply {
adapter = usuariosModelo(listOfItems)
}
database = FirebaseDatabase.getInstance()
reference = database.getReference("usuarios")
verDados()
btn_voltar.setOnClickListener() {
startActivity(Intent(this#verDados, Dashboard::class.java))
finish()
}
}
i solve the problem just adding the tag below inside the recyclerview tag in the .xml file:
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
I hope that help y'all!
I followed a tutorial to create an application that displays data from a mysql table, if i run the app everything works without error, the application opens with the header but without content, blank page instead of the content list.
activity_main.xml :
package com.example.bingmada
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import org.json.JSONArray
class MainActivity : AppCompatActivity() {
var adapter:ArticleAdapter ?=null
var articles:ArrayList<Article> ?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
articles= ArrayList()
getArticlesFromServer()
adapter = ArticleAdapter(articles!!)
var recyclerView = findViewById<RecyclerView>(R.id.recyclevirtuel)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter=adapter
}
fun getArticlesFromServer(){
var url="https://www.exemplesite.com/android/bingmada.php"
var stringRequest=StringRequest(Request.Method.GET,url,Response.Listener { response -> parseData(response) }, Response.ErrorListener { error ->
Toast.makeText(this,"Erreur de connexion ", Toast.LENGTH_SHORT).show()
})
var req=Volley.newRequestQueue(this)
req.add(stringRequest)
}
fun parseData(response:String){
var arrayJson = JSONArray(response)
for(i in 0..arrayJson.length()-1){
var currentObject=arrayJson.getJSONObject(i)
var article = Article(currentObject.getInt("id"),
currentObject.getString("nom"),
currentObject.getString("lienimg"),
currentObject.getString("ifram"))
articles?.add(article)
}
}
}
the page https://www.exemplesite.com/android/bingmada.php show :
[{"id":"1","nom":"tesmon","lienimg":"bingo1","ifram":"car1"},{"id":"2","nom":"testeds","lienimg":"bingo","ifram":"car2"},{"id":"3","nom":"test1","lienimg":"bingo3","ifram":"car3"},{"id":"4","nom":"testr","lienimg":"bingo4","ifram":"car4"},{"id":"5","nom":"test2","lienimg":"bingo5","ifram":"car5"},{"id":"6","nom":"letest","lienimg":"bingo6","ifram":"car6"},{"id":"7","nom":"test3","lienimg":"bingo7","ifram":"car7"},{"id":"8","nom":"testo","lienimg":"bingo8","ifram":"car8"},{"id":"9","nom":"test4","lienimg":"bingo9","ifram":"car9"},{"id":"10","nom":"testad","lienimg":"bingo10","ifram":"car10"},{"id":"11","nom":"test5","lienimg":"bingo11","ifram":"car11"},{"id":"12","nom":"testd","lienimg":"bingo12","ifram":"car12"},{"id":"13","nom":"test6","lienimg":"bingo13","ifram":"car13"},{"id":"14","nom":"testiu","lienimg":"bingo14","ifram":"car14"},{"id":"15","nom":"teste","lienimg":"bingo15","ifram":"car15"}]
in my page listevirtuelles.kt :
package com.example.bingmada
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class Article(var id:Int,var nom:String,var lienimg:String, var ifram:String){
}
class ArticleAdapter (var articles:ArrayList<Article>) : RecyclerView.Adapter<ArticleAdapter.MyViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
var vue=LayoutInflater.from(parent.context).inflate(R.layout.activity_listevirtuelles, parent, false)
return MyViewHolder(vue)
}
override fun getItemCount(): Int {
return articles.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
var article = articles.get(position)
holder.idvirtuelleli.setText(article.id)
holder.nomvisite.setText(article.nom)
holder.lieimgvisite.setText(article.lienimg)
holder.ifram.setText(article.ifram)
}
class MyViewHolder(var vue:View):RecyclerView.ViewHolder(vue){
var idvirtuelleli=vue.findViewById<TextView>(R.id.idvirtuelleli)
var nomvisite=vue.findViewById<TextView>(R.id.nom_virtuelleli)
var lieimgvisite=vue.findViewById<TextView>(R.id.lienimg)
var ifram=vue.findViewById<TextView>(R.id.ifram)
}
}
activity_main.xml page :
<?xml version="1.0" encoding="utf-8"?>
<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"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclevirtuel"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
a page activity_listevirtuelles.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="#+id/idvirtuelleli"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="id"
android:textSize="25dp"
/>
<TextView
android:id="#+id/nom_virtuelleli"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Lenom"
android:textSize="25dp"
/>
<TextView
android:id="#+id/lienimg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="lien dimage"
android:textSize="25dp"
/>
<TextView
android:id="#+id/ifram"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="codeifram"
android:textSize="25dp"
/>
</LinearLayout>
can you help me find the problem please?
If you can see the relevant data in the activity. Since it is an asynchronous process, can you apply the structure I added to connect the data with recyclerview?
class MainActivity : AppCompatActivity() {
var adapter:ArticleAdapter ?=null
var articles:ArrayList<Article> ?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
articles= ArrayList()
adapter = ArticleAdapter(articles!!) // 1 line up to changed
getArticlesFromServer()
var recyclerView = findViewById<RecyclerView>(R.id.recyclevirtuel)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter=adapter
}
fun getArticlesFromServer(){
var url="https://www.exemplesite.com/android/bingmada.php"
var stringRequest=StringRequest(Request.Method.GET,url,Response.Listener { response -> parseData(response) }, Response.ErrorListener { error ->
Toast.makeText(this,"Erreur de connexion ", Toast.LENGTH_SHORT).show()
})
var req=Volley.newRequestQueue(this)
req.add(stringRequest)
}
fun parseData(response:String){
var arrayJson = JSONArray(response)
for(i in 0..arrayJson.length()-1){
var currentObject=arrayJson.getJSONObject(i)
var article = Article(currentObject.getInt("id"),
currentObject.getString("nom"),
currentObject.getString("lienimg"),
currentObject.getString("ifram"))
articles?.add(article)
}
adapter.notifydatasetchanged() // added
}
}
I am making a Kotlin recipe app and I have a list of dishes name with a button to view the dish that will be generated whenever a new dish is added in a list view. The button is supposed to navigate users to a new activity where the recipe of the dish will be shown. However, the button does not work. I have tried a few suggested solutions but still does not work.
xml file of the list:
<?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"
android:background="#android:color/darker_gray"
tools:context=".ViewRecipe">
<ListView
android:id="#+id/recipeList"
android:layout_width="367dp"
android:layout_height="565dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="39dp"
android:text="Recipe List"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
xml for button:
<?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=".Recipes">
<TextView
android:id="#+id/recipeView2"
android:layout_width="228dp"
android:layout_height="28dp"
android:layout_marginStart="23dp"
android:layout_marginTop="47dp"
android:text="TextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/recipeDetails"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="33dp"
android:layout_marginEnd="20dp"
android:text="View"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Kotlin file for the list view:
package com.example.recipeapp
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.ListView
import com.google.firebase.database.*
class ViewRecipe : AppCompatActivity() {
lateinit var ref: DatabaseReference
lateinit var recipeList: MutableList<AddRecipeModelClass>
lateinit var listView: ListView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_recipe)
recipeList = mutableListOf()
ref = FirebaseDatabase.getInstance().getReference("addRecipes")
listView = findViewById(R.id.recipeList)
ref.addValueEventListener(object: ValueEventListener {
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
recipeList.clear()
for(h in snapshot.children){
val recipe = h.getValue(AddRecipeModelClass::class.java)
recipeList.add(recipe!!)
}
val adapter = RecipeAdapter(applicationContext, R.layout.activity_recipes, recipeList)
listView.adapter = adapter
}
}
});
}
}
Kotlin file for the button:
package com.example.recipeapp
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
class Recipes : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recipes)
val navView = findViewById<Button>(R.id.recipeDetails)
navView.setOnClickListener{
val intent = Intent(this, RecipeDetails::class.java)
startActivity(intent)
}
}
}
RecipeAdapter.kt
package com.example.recipeapp
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.TextView
class RecipeAdapter(val mCtx: Context, val layoutResId: Int, val recipeList: List<AddRecipeModelClass>)
: ArrayAdapter<AddRecipeModelClass>(mCtx, layoutResId, recipeList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val layoutInflater: LayoutInflater = LayoutInflater.from(mCtx)
val view: View = layoutInflater.inflate(layoutResId, null)
val recipeView = view.findViewById<TextView>(R.id.recipeView2)
val recipe = recipeList[position]
recipeView.text = recipe.dishName
return view
}
}
Edit:
I have found that the issue is occurring because the "view" button is inside a list view. I tried using a "button" placed outside the listview and it works just fine. I am unable to solve it using the solutions found on the internet.
The image shows the 2 types of buttons. View is inside a listview, button is outside listview.
Any help will be greatly appreciated.
I haven't read your entire code but one problem that I found is this:
navView.setOnClickListener{
val intent = Intent(this, RecipeDetails::class.java)
// ^^^^
startActivity(intent)
}
The this in that scope refers to the View.OnClickListener object passed to setOnclickListener function. Not to the application context.
From kotlin docs :
To access this from an outer scope (a class, or extension function, or
labeled function literal with receiver) we write this#label where
#label is a label on the scope this is meant to be from
So you should be doing this instead:
navView.setOnClickListener{
val intent = Intent(this#Recipes, RecipeDetails::class.java)
// ^^^^ note the label #Recipes
startActivity(intent)
}
Alternatively you can call Intent constructor from outside the object of View.OnClickListener, in the onCreate function:
val intent = Intent(this, RecipeDetails::class.java)
val navView = findViewById<Button>(R.id.recipeDetails)
navView.setOnClickListener{
startActivity(intent)
}
Put this in your activity class and make sure you AddRecipeModelClass is parcelable
private val onItemAction: (AddRecipeModelClass) -> Unit = { it ->
val intent = Intent(this#ViewRecipe , RecipeDetails::class.java)
intent.putExtra("recipe", it)
startActivity(intent)
}
get a recipe from intent in a new activity
Add a click listener to your adapters getView
class RecipeAdapter(val mCtx: Context, val layoutResId: Int,
val recipeList: List<AddRecipeModelClass>,private val onItemSelected: (AddRecipeModelClass) -> Unit?)
: ArrayAdapter<AddRecipeModelClass>(mCtx, layoutResId, recipeList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val layoutInflater: LayoutInflater = LayoutInflater.from(mCtx)
val view: View = layoutInflater.inflate(layoutResId, null)
val recipeView = view.findViewById<TextView>(R.id.recipeView2)
val btn= findViewById<Button>(R.id.recipeDetails)
val recipe = recipeList[position]
btn.setOnClickListener{
onItemSelected(recipe)
}
recipeView.text = recipe.dishName
return view
}
}
add function to the constructor
val adapter = RecipeAdapter(applicationContext, R.layout.activity_recipes, recipeList,onItemAction)
I was trying to implement a RecyclerView to populate with posts saved in a Firebase Database.
When I run my application I get the error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.price, PID: 21096
android.view.InflateException: Binary XML file line #2: Binary XML file line #2: Error inflating class androidx.constraintlayout.widget.ConstraintLayoutaintLayout
Caused by: android.view.InflateException: Binary XML file line #2: Error inflating class androidx.constraintlayout.widget.ConstraintLayoutaintLayout
Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.constraintlayout.widget.ConstraintLayoutaintLayout" on path: DexPathList[[zip file "/data/app/com.example.price-RzfjA-3DV5y8h2WztAw65A==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.price-RzfjA-3DV5y8h2WztAw65A==/lib/x86, /data/app/com.example.price-RzfjA-3DV5y8h2WztAw65A==/base.apk!/lib/x86, /system/lib, /vendor/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
All other posts I read recommend updating to implementation 'androidx.constraintlayout:constraintlayout:1.1.3' and replacing 'androidx.constraintlayout.ConstraintLayout' with 'androidx.constraintlayout.widget.ConstraintLayout', both of which I have done.
My Activity is:
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageButton
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import com.google.firebase.storage.FirebaseStorage
class CommunityDisplay : AppCompatActivity() {
companion object {
private val TAG = "CommunityDisplay"
}
private lateinit var fStorage: FirebaseStorage
private lateinit var fDatabase: FirebaseDatabase
private lateinit var postAdapter: PostAdapter
private var postList : ArrayList<Post> = ArrayList<Post>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.comm_display)
// Set up button listeners
val homeButton = findViewById(R.id.home_button) as ImageButton
homeButton.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
val intent = Intent(this#CommunityDisplay, HomeDisplay::class.java)
startActivity(intent)
}
})
fStorage = FirebaseStorage.getInstance()
val qsRef = fStorage.getReference("Audio/Questions")
fDatabase = FirebaseDatabase.getInstance()
val postsRef = fDatabase.getReference("posts")
var postRecyclerView : RecyclerView = findViewById(R.id.postRV)
postRecyclerView.setLayoutManager(LinearLayoutManager(this))
postRecyclerView.hasFixedSize()
postsRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(ds: DataSnapshot) {
for (postSnap in ds.getChildren()) {
var post : Post = postSnap.getValue(Post::class.java)!!
postList.add(post)
}
postAdapter = PostAdapter(applicationContext, postList)
postRecyclerView.setAdapter(postAdapter)
}
override fun onCancelled(p0: DatabaseError) {
Log.d("DB Error", p0.toString())
}
})
}
}
My PostAdapter is:
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class PostAdapter(
var mContext: Context,
var mData: List<Post>
) :
RecyclerView.Adapter<PostAdapter.MyViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): MyViewHolder {
val row: View =
LayoutInflater.from(mContext).inflate(R.layout.row_post_item, parent, false)
return MyViewHolder(row)
}
override fun onBindViewHolder(
holder: MyViewHolder,
position: Int
) {
holder.tvTitle.text = mData[position].title
}
override fun getItemCount(): Int {
return mData.size
}
inner class MyViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
var tvTitle: TextView
init {
tvTitle = itemView.findViewById(R.id.row_post_title)
itemView.setOnClickListener {
val postDetailActivity =
Intent(mContext, QuestionDisplay::class.java)
val position = adapterPosition
postDetailActivity.putExtra("title", mData[position].title)
postDetailActivity.putExtra("audioURL", mData[position].audioURL)
postDetailActivity.putExtra("audioPath", mData[position].audioPath)
mContext.startActivity(postDetailActivity)
}
}
}
}
My layout code is:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".CommunityDisplay">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_above="#+id/home_button">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/PageTitle"
android:text="Questions"
android:layout_gravity="center"
android:textSize="30sp"
android:textAlignment="center"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/postRV"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<ImageButton
android:id="#+id/home_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginLeft="20dp"
android:layout_marginBottom="20dp"
android:background="#mipmap/home" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
My recycler view element is:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayoutaintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/row_post_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayoutaintLayout>
Where am I going wrong?
Thank you!