The App is working fine The JSON data has also been fetched but the recycler view not showing the data even the recycler view is also not showing.
Here Im Sharing my code below please help me to find out the problem,
My app does not show any error while compiling, all app working completely fine.
course_rv_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
enter code herexmlns: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_gravity="center"
android:layout_margin="5dp"
app:cardCornerRadius="5dp"
app:cardElevation="4dp">
<!--on below line we are creating a
linear layout for grid view item-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!--on below line we are creating a simple image view-->
<ImageView
android:id="#+id/idIVCourse"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:layout_margin="8dp"
android:padding="5dp"
android:src="#mipmap/ic_launcher" />
<!--on below line we are creating a simple text view-->
<TextView
android:id="#+id/idTVCourse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:padding="4dp"
android:text="#string/app_name"
android:textAlignment="textStart"
android:textColor="#color/black"
android:textStyle="bold"
tools:ignore="RtlCompat" />
</LinearLayout>
</androidx.cardview.widget.CardView>
activity_contact.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".ContactActivity">
<ProgressBar
android:id="#+id/idPBLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/idRVCourses"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</RelativeLayout>
Contact_Activity.kt
import android.app.DownloadManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
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.JSONObject
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.toolbox.JsonArrayRequest
import com.denzcoskun.imageslider.models.SlideModel
import androidx.recyclerview.widget.LinearLayoutManager;
import com.android.volley.RequestQueue;
import com.android.volley.VolleyError;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.ArrayList;
class ContactActivity : AppCompatActivity() {
private val BASE_URL = "MyURL"
lateinit var courseRV: RecyclerView
lateinit var loadingPB: ProgressBar
lateinit var courseRVAdapter: CourseRVAdapter
lateinit var courseList: ArrayList<CourseRVModal>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contact)
// on below line we are initializing
// our variable with their ids.
courseRV = findViewById(R.id.idRVCourses)
loadingPB = findViewById(R.id.idPBLoading)
// on below line we are initializing our list
courseList = ArrayList()
// on below line we are initializing our adapter.
courseRVAdapter = CourseRVAdapter(courseList)
// on below line we are setting
// adapter to recycler view.
courseRV.adapter = courseRVAdapter
// on below line we are calling
// get data method to get data.
getData()
}
private fun getData() {
// on below line we are creating a variable for url
var url = BASE_URL + "getSlider.php"
// on below line we are creating a
// variable for our request queue
val queue = Volley.newRequestQueue(this#ContactActivity)
// on below line we are creating a request
// variable for making our json object request.
val request =
// as we are getting json object response and we are making a get request.
JsonArrayRequest(Request.Method.GET, url, null, { response ->
// this method is called when we get successful response from API.
loadingPB.visibility = View.GONE
try {
for (i in 0 until response.length()) {
// on below line we are extracting
// data from each json object
val respObj = response.getJSONObject(i)
val langName = respObj.getString("sliderTitle")
val langImg = respObj.getString("sliderImage")
// on below line we are adding data to our list
courseList.add(CourseRVModal(langName, langImg))
// on below line we are notifying
// our adapter that data has updated.
courseRVAdapter.notifyDataSetChanged()
}
// on below line we
// are handling exception
} catch (e: Exception) {
e.printStackTrace()
}
}, { error ->
// in this case we are simply displaying a toast message.
Toast.makeText(this#ContactActivity, "Fail to get response", Toast.LENGTH_SHORT)
.show()
})
// at last we are adding our
// request to our queue.
queue.add(request)
}
}
CourseRVModel.kt
data class CourseRVModal(
// on below line we are creating a two variable
// one for course name and other for course image.
var courseName: String,
var courseImg: String
)
CourseRVAdaptor.kt
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
class CourseRVAdapter(
// on below line we are passing variables as course list and context
private var courseList: ArrayList<CourseRVModal>,
) : RecyclerView.Adapter<CourseRVAdapter.CourseViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CourseRVAdapter.CourseViewHolder {
// this method is use to inflate the layout file
// which we have created for our recycler view.
// on below line we are inflating our layout file.
val itemView = LayoutInflater.from(parent.context).inflate(
R.layout.course_rv_item,
parent, false
)
// at last we are returning our view
// holder class with our item View File.
return CourseViewHolder(itemView)
}
override fun onBindViewHolder(holder: CourseRVAdapter.CourseViewHolder, position: Int) {
// on below line we are setting data to our text view and our image view.
holder.courseNameTV.text = courseList.get(position).courseName
Picasso.get().load(courseList.get(position).courseImg).into(holder.courseIV)
}
override fun getItemCount(): Int {
// on below line we are returning
// our size of our list
return courseList.size
}
class CourseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// on below line we are initializing our course name
// text view and our image view.
val courseNameTV: TextView = itemView.findViewById(R.id.idTVCourse)
val courseIV: ImageView = itemView.findViewById(R.id.idIVCourse)
}
}
build.gradle
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.firebase:firebase-database:20.0.6'
implementation 'com.google.firebase:firebase-config-ktx:21.1.2'
implementation 'com.google.firebase:firebase-config:21.1.2'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'com.github.denzcoskun:ImageSlideshow:0.1.0'
implementation("com.android.volley:volley:1.2.1")
implementation 'com.squareup.picasso:picasso:2.71828'
In activity_contact.xml your recycler view idRVCourses has attribute android:visibility="gone". So it is hidden when the screen first loads. You need make it visible once you load the data. for that add courseRV.visibility = View.GONE after loadingPB.visibility = View.GONE in getData function.
Related
I'm trying to create an application and considering my level it's not easy! I hope you could help me since I didn't succeed with the many links I found on the internet.
I can't add the onClick function of View.OnClickListener, each time the Intent function is not recognized. I tried to implement it in the UserViewHolder and FirestoreRecyclerAdapter class but it doesn't work.
Here is my current code:
---------- kotlin part ---------
package edu.stanford.rkpandey.emojistatus
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.*
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.firebase.ui.firestore.FirestoreRecyclerAdapter
import com.firebase.ui.firestore.FirestoreRecyclerOptions
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import kotlinx.android.synthetic.main.activity_main.*
data class User(
val displayName: String? = "",
val emojis: String? = ""
)
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class MainActivity : AppCompatActivity() {
private val db = Firebase.firestore
private lateinit var auth: FirebaseAuth
// Query the users collection
private val query = db.collection("users")
val options = FirestoreRecyclerOptions.Builder<User>()
.setQuery(query, User::class.java)
.setLifecycleOwner(this).build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
auth = Firebase.auth
val adapter = object: FirestoreRecyclerAdapter<User, UserViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(this#MainActivity).inflate(R.layout.item_pack, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(
holder: UserViewHolder,
position: Int,
model: User
) {
val tvName: TextView = holder.itemView.findViewById(R.id.title)
val tvEmojis: TextView = holder.itemView.findViewById(R.id.excerpt)
tvName.text = model.displayName
tvEmojis.text = model.emojis
}
}
rvUsers.adapter = adapter
rvUsers.layoutManager = LinearLayoutManager(this)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.miLogout) {
Log.i("MainActivity", "Logout")
auth.signOut()
val logoutIntent = Intent(this, LoginActivity::class.java)
logoutIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(logoutIntent)
}
return super.onOptionsItemSelected(item)
}
}
------- xml part -------
=> activity_main
<?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=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvUsers"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
=> item_pack
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="100sp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.cardview.widget.CardView
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="12sp"
android:layout_marginTop="12sp"
android:layout_marginEnd="12sp"
android:focusable="true"
android:clickable="true"
app:cardCornerRadius="10dp"
android:foreground="?android:attr/selectableItemBackground">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:background="#color/colorPack">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5sp"
style="#style/NoteTitleFont"
android:textColor="#color/colorTitle"
tools:text="Note 1" />
<TextView
android:id="#+id/excerpt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12sp"
android:layout_below="#id/title"
android:maxLines="1"
android:ellipsize="end"
android:textStyle="italic"
android:textColor="#color/colorDescribe"
tools:text="test text va se trouver ici, ça va contenir le début de la description du package." />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</RelativeLayout>
This code gives this result :
I would like that when I click on one of the carviews it can go to the activity_pack_detail.
Do you know how to do Intent to PackDetailActivity?
I get this error no matter what I do =>
You're getting that error because you are calling Intent's class constructor with a wrong argument. The first argument should be a Context and not a View. The keyword this is referring in your code to a View and not to a context, hence the error.
To solve this, you have to pass a Context object like this:
val i = Intent(view.getContext(), PackDetailActivity::class.java)
And your error will go away.
So I'm building this Carousel project that shows an image and some text on the app. I've gotten the structure down by building a prototype. I was able to use Retrofit to call the information from the RestAPI on my terminal as well. But where I have been really stuck at is trying to get the info to show in the container, where I want it to show. I've tried a slew of ways to approach it, but to no avail. Below is the code I have now, which is showing images and text that I put in it. Along with the Retrofit classes. I've been at it for three days and I'm not sure what to do. If anyone has an idea or can help, I'll be extremely thankful.
The API (Retrofit)
package com.examples.carousel.api
import com.examples.carousel.CarouselItem
import com.examples.carousel.utli.Constants.Companion.BASE_URL
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
interface FlickrApi {
#GET("services/rest/?method=flickr.interestingness.getList" +
"&api_key=(private)" +
"&format=json" +
"&nojsoncallback=1" +
"&extras=url_s")
fun getCarouselItem() : Call<List<CarouselItem>>
companion object {
fun create() : FlickrApi {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
return retrofit.create(FlickrApi::class.java)
}
}
}
The Model (The comment out code is for the info I want from the api)
package com.examples.carousel
data class CarouselItem internal constructor(
// var title: String,
// var id: String,
// var url_s: String
var image: Int,
var title2: String
) {
}
The Adapter
package com.examples.carousel
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide
import com.makeramen.roundedimageview.RoundedImageView
class ItemAdapter internal constructor(carouselItems: MutableList<CarouselItem>, viewPager2: ViewPager2) : RecyclerView.Adapter<ItemAdapter.ItemPagerViewHolder>() {
private val carouselItems: List<CarouselItem>
private val viewPager2: ViewPager2
init {
this.carouselItems = carouselItems
this.viewPager2 = viewPager2
}
class ItemPagerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val photo: RoundedImageView = itemView.findViewById(R.id.picture)
private val title: TextView = itemView.findViewById(R.id.photo_name)
fun image(carouselItem: CarouselItem) {
photo.setImageResource(carouselItem.image)
}
fun text(carouselItem: CarouselItem) {
title.text = carouselItem.title2
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemPagerViewHolder {
return ItemPagerViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.carousel_item_container,
parent,
false
)
)
}
override fun onBindViewHolder(holder: ItemPagerViewHolder, position: Int) {
holder.image(carouselItems[position])
holder.text(carouselItems[position])
if (position == carouselItems.size - 2) {
viewPager2.post(runnable)
}
}
override fun getItemCount(): Int {
return carouselItems.size
}
private val runnable = Runnable {
carouselItems.addAll(carouselItems)
notifyDataSetChanged()
}
}
The MainActivity
package com.examples.carousel
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.CompositePageTransformer
import androidx.viewpager2.widget.MarginPageTransformer
import androidx.viewpager2.widget.ViewPager2
import com.examples.carousel.api.FlickrApi
import com.makeramen.roundedimageview.RoundedImageView
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import kotlin.math.abs
class CarouselActivity : AppCompatActivity() {
lateinit var viewPager: ViewPager2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_carousel)
viewPager = findViewById(R.id.viewPager_ImageSlider)
supportActionBar?.hide()
val multipleItems: MutableList<CarouselItem> = ArrayList()
multipleItems.add(CarouselItem(R.drawable.bcw_38,"Image 1"))
multipleItems.add(CarouselItem(R.drawable.bcw_39,"Image 2"))
multipleItems.add(CarouselItem(R.drawable.bcw_40,"Image 3"))
multipleItems.add(CarouselItem(R.drawable.bcw_45,"Image 4"))
multipleItems.add(CarouselItem(R.drawable.bcw_50,"Image 5"))
multipleItems.add(CarouselItem(R.drawable.bcw_53,"Image 6"))
viewPager.adapter = ItemAdapter(multipleItems,viewPager)
viewPager.clipToPadding = false
viewPager.clipChildren = false
viewPager.offscreenPageLimit = 3
viewPager.getChildAt(0).overScrollMode = RecyclerView.OVER_SCROLL_NEVER
val compositePageTransformer = CompositePageTransformer()
compositePageTransformer.addTransformer(MarginPageTransformer(30))
compositePageTransformer.addTransformer { page, position ->
val r = 1 - abs(position)
page.scaleY = 0.85f + r * 0.25f
}
viewPager.setPageTransformer(compositePageTransformer)
//
// val apiInterface = FlickrApi.create().getCarouselItem()
//
// apiInterface.enqueue(object: Callback<List<CarouselItem>> {
// override fun onFailure(call: Call<List<CarouselItem>>, t: Throwable) {
//
// }
//
// override fun onResponse(call: Call<List<CarouselItem>>, response: Response<List<CarouselItem>>) {
//
// if (response.body() != null){
// itemAdapter.setItemListItems(response.body()!!)
//
// }
// }
// })
}
}
The XML Files:
The ViewPager2 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" android:background="#232323"
android:id="#+id/linearLayout">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewPager_ImageSlider"
android:layout_height="0dp"
android:layout_width="0dp"
android:paddingStart="70dp"
android:paddingEnd="70dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="80dp"/>
<TextView
android:text="#string/osa_playlist"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="#+id/textView2"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="40dp"
android:textColor="#color/white" android:textSize="25sp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The Container
<?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:id="#+id/container" android:background="#232323">
<com.makeramen.roundedimageview.RoundedImageView
android:layout_width="match_parent"
android:layout_height="550dp" app:layout_constraintTop_toTopOf="parent" android:id="#+id/picture"
android:adjustViewBounds="true"
app:riv_corner_radius="12dp" tools:layout_editor_absoluteX="0dp"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content" android:id="#+id/photo_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp" android:layout_marginRight="8dp" android:layout_marginEnd="8dp"
android:textStyle="bold"
tools:text="TEXT TEXT" android:textSize="36sp" android:textColor="#color/white" android:gravity="center"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintTop_toBottomOf="#+id/picture" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="230dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The Dependencies
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
//For the Carousel Container
implementation 'androidx.viewpager2:viewpager2:1.0.0'
implementation 'com.makeramen:roundedimageview:2.3.0'
//For Retrofit and Gson
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
// implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
// implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
testImplementation 'junit:junit:4.13.2'
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
}
*Its not the Internet permission, it's already there.
itemAdapter.setItemListItems(response.body()!!) instead of calling adapters method, write your own method which updates the list:
private val carouselItems: List,
make this list mutable and write method:
public fun updateList(val items: List<CarouselItem>){
carouselItems.clear()
carouselItems.addAll(items)
notifyDataSetChanged()
}
I am trying to display JSON data from a dummy server (json-server) using Retrofit2 and display it in a RecyclerView placed inside a fragment. So far the following events are taking place (found out using Log infos):
Main fragment CharactersListFragment.kt creation is initiated.
Only getItemCount() is being called. It is returning 0.
OnCreateViewHolder() and onBindViewHolder() are not being called.
Data is successfully fetched from the server and passed to the adapter (from CharactersListFragment.kt)
There are no compile time errors nor does my app crash, just the RecyclerView is empty.
Here are my code files:
Layout for fragment with RecyclerView: fragment_characters_list.xml
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".CharactersListFragment">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycler_view"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Layout for RecyclerView row: fragment_character_recyclerview_list.xml
<?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:id="#+id/character_card_view"
android:layout_width="match_parent"
android:layout_height="300dp"
android:elevation="15dp"
app:cardBackgroundColor="#color/violet_forcard"
app:cardCornerRadius="3dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/char_thumnail_img"
android:layout_width="143dp"
android:layout_height="150dp"
android:layout_marginStart="24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.106"
tools:srcCompat="#tools:sample/avatars" />
<TextView
android:id="#+id/char_id_txt"
android:layout_width="190dp"
android:layout_height="53dp"
android:layout_marginTop="16dp"
android:fontFamily="#font/roboto"
android:text="Character ID: "
android:textAlignment="viewStart"
android:textColor="#color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/char_thumnail_img"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/char_name_txt"
android:layout_width="190dp"
android:layout_height="53dp"
android:layout_marginTop="24dp"
android:fontFamily="#font/roboto"
android:text="Name: "
android:textAlignment="viewStart"
android:textColor="#color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/char_thumnail_img"
app:layout_constraintTop_toBottomOf="#+id/char_id_txt" />
<TextView
android:id="#+id/char_descp_txt"
android:layout_width="359dp"
android:layout_height="wrap_content"
android:fontFamily="#font/roboto"
android:textAlignment="viewStart"
android:textColor="#color/white"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.461"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.918" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Kotlin class for fragment: CharactersListFragment.kt
package com.example.marvelapp
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class CharactersListFragment : Fragment() {
private lateinit var charsAdapter: CharacterListAdapter
private lateinit var apiService: APIService
private var characters: MutableList<Character> = ArrayList()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
Log.i("INSIDE_FRAGMENT_ONCREATEVIEW", "Fragment creation initiated")
val v = inflater.inflate(R.layout.fragment_characters_list, container, false)
apiService = RestClient.client.create(APIService::class.java)
getCharacterList()
val recycler_view = v.findViewById(R.id.recycler_view) as RecyclerView
recycler_view.layoutManager = LinearLayoutManager(activity)
charsAdapter = CharacterListAdapter()
recycler_view.adapter = charsAdapter
return v
}
private fun getCharacterList(){
val call = apiService!!.get_characters()
call.enqueue(object: Callback<List<Character>> {
override fun onResponse(
call: Call<List<Character>>,
response: Response<List<Character>>
) {
val chars = response.body()
Log.i("get_character_succeeded_FOR_CALL", chars.toString())
if (chars != null){
characters.addAll(chars!!)
Log.i("character_ADD_CHECK_INSIDE_FRAGMENT", characters[0].toString())
charsAdapter.submitDataforcharList(characters)
}
}
override fun onFailure(call: Call<List<Character>>, t: Throwable) {
Log.e("get_character_failed", t.localizedMessage)
}
})
}
}
Kotlin class for adapter: CharacterListAdapter.kt
package com.example.marvelapp
import android.text.Layout
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_character_recyclerview_list.view.*
class CharacterListAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var charItems: List<Character> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
Log.i("INSIDE_ONCREATE_VIEWHOLDER", "reached")
return CharacterListViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.fragment_character_recyclerview_list,
parent,
false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(holder){
is CharacterListViewHolder ->{
Log.i("VIEWHOLDER_FOUND", "proceeding to bind")
holder.bind(charItems.get(position))
}
}
}
override fun getItemCount(): Int {
Log.i("INSIDE_ADAPTER_GET_ITEM_COUNT", charItems.size.toString())
return charItems.size
}
fun submitDataforcharList (characterlist: List<Character>){
Log.i("INSIDE_ADAPTER_SUBMIT_DATA", characterlist[0].toString())
charItems = characterlist
}
class CharacterListViewHolder
constructor(view: View): RecyclerView.ViewHolder(view){
val char_id = view.findViewById<TextView>(R.id.char_id_txt)
val char_name = view.findViewById<TextView>(R.id.char_name_txt)
val char_descp = view.findViewById<TextView>(R.id.char_descp_txt)
fun bind(character: Character){
Log.i("INSIDE_VIEWHOLDER_BIND", character.toString())
val id = Integer.toString(character.charId)
val id_mess = "Character ID: $id"
char_id.setText(id_mess)
val char_name_mess = "Name: ${character.charName}"
char_name.setText(char_name_mess)
char_descp.setText(character.charDescp)
}
}
}
the Character model class:
package com.example.marvelapp
data class Character(
val charId: Int,
val charName: String,
val charDescp: String,
){
}
I am an android beginner and have been stuck at this for quite some time, any help will be highly appreciated.
Thanks in advance!
In the onCreateView of CharactersListFragment you are calling getCharacterList() and then you move on with your initialization of the recycler view. After the getCharacterList call completes, you might be getting some data and you are simply updating the data list in your adapter class but you are not informing your recyclerview to refresh the view to display the new data. So I think if you just call notifyDataSetChanged() on your adapter after setting the new data it should work.
The other way to solve the issue could be if you initialize your recyclerview after you've received the data from the getCharacterList() call and just show some loading icon in place of the recycler view while the data is being fetched.
getCharacterList() is called on UI thread (main thread) but communicating (requests and responses) between device and API server is happening in another thread, it means there is no guarantee that onResponse() will come earlier than recycler_view.adapter = charsAdapter and in reality, it mostly does not happens. Therefor, you must call charsAdapter.notifyDataSetChanged() after charsAdapter.submitDataforcharList(characters) to have the recyclerview represent data. Another more elegent way to do that is adding notifyDatasetChanged() inside submitDataforcharList():
fun submitDataforcharList (characterlist: List<Character>){
charItems = characterlist
notifyDatasetChanged()
}
I faced an error that actually mustn't occur, because I do exactly the same thing in my project in another screen, and it works there, but doesn't want to work in another screen.
The problem is the following: from an Activity in a result of some action I open up a DialogFragment which contains an image and other views in its layout file. Now I can't understand why, but it works in the first case (you'll see below) but doesn't work in the second...
First case:
Layout XML file (dialog_character_selector.xml):
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="dialogViewModel"
type="neptun.jxy1vz.cluedo.ui.menu.character_selector.CharacterSelectorViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Spinner
android:id="#+id/spinnerCharacter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/ivCharacterCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="20dp"
android:src="#drawable/szereplo_hatlap"
app:layout_constraintBottom_toTopOf="#+id/btnStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.7"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/spinnerCharacter" />
<Button
android:id="#+id/btnStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/start"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:onClick="#{()->dialogViewModel.startGame()}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
The DialogFragment's Kotlin source code (CharacterSelectorDialog.kt):
package neptun.jxy1vz.cluedo.ui.menu.character_selector
import android.animation.AnimatorInflater
import android.animation.AnimatorSet
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.appcompat.app.AlertDialog
import androidx.core.animation.doOnEnd
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.DialogFragment
import neptun.jxy1vz.cluedo.R
import neptun.jxy1vz.cluedo.databinding.DialogCharacterSelectorBinding
class CharacterSelectorDialog : DialogFragment(), AdapterView.OnItemSelectedListener {
private lateinit var dialogCharacterSelectorBinding: DialogCharacterSelectorBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
dialogCharacterSelectorBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.dialog_character_selector,
null,
false
)
dialogCharacterSelectorBinding.spinnerCharacter.adapter = ArrayAdapter<String>(
context!!,
android.R.layout.simple_spinner_dropdown_item,
resources.getStringArray(R.array.characters)
)
dialogCharacterSelectorBinding.spinnerCharacter.onItemSelectedListener = this
//I do this due to a card flipping animation, it's not important, not part of my problem
val scale = resources.displayMetrics.density
dialogCharacterSelectorBinding.ivCharacterCard.cameraDistance = 8000 * scale
dialogCharacterSelectorBinding.dialogViewModel = CharacterSelectorViewModel(context!!)
return AlertDialog.Builder(context!!, R.style.Theme_AppCompat_Light_Dialog).setView(dialogCharacterSelectorBinding.root).setTitle(resources.getString(R.string.dialog_character_title)).create()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
dialogCharacterSelectorBinding.ivCharacterCard.setImageResource(R.drawable.szereplo_hatlap)
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
dialogCharacterSelectorBinding.ivCharacterCard.setImageResource(R.drawable.szereplo_hatlap)
(AnimatorInflater.loadAnimator(context, R.animator.card_flip) as AnimatorSet).apply {
setTarget(dialogCharacterSelectorBinding.ivCharacterCard)
start()
doOnEnd {
dialogCharacterSelectorBinding.dialogViewModel!!.setPlayer(position)
val img = when (position) {
0 -> R.drawable.szereplo_ginny
1 -> R.drawable.szereplo_harry
2 -> R.drawable.szereplo_hermione
3 -> R.drawable.szereplo_ron
4 -> R.drawable.szereplo_luna
else -> R.drawable.szereplo_neville
}
dialogCharacterSelectorBinding.ivCharacterCard.setImageResource(img)
}
}
}
}
Second case:
Layout XML file (dialog_helper_card.xml):
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="helperCardDialogViewModel"
type="neptun.jxy1vz.cluedo.ui.dice.card_dialog.helper.HelperCardViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/ivHelperCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="20dp"
android:src="#drawable/mento_hatlap"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
I think in the main parts it's just the same as the previous one.
Kotlin source file (HelperCardDialog.kt):
package neptun.jxy1vz.cluedo.ui.dice.card_dialog.helper
import android.animation.AnimatorInflater
import android.animation.AnimatorSet
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import androidx.core.animation.doOnEnd
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.DialogFragment
import neptun.jxy1vz.cluedo.R
import neptun.jxy1vz.cluedo.databinding.DialogHelperCardBinding
class HelperCardDialog(private val cardResource: Int) : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog {
val dialogHelperCardBinding = DataBindingUtil.inflate<DialogHelperCardBinding>(LayoutInflater.from(context), R.layout.dialog_helper_card, null, false)
dialogHelperCardBinding.helperCardDialogViewModel = HelperCardViewModel()
(AnimatorInflater.loadAnimator(context, R.animator.card_flip) as AnimatorSet).apply {
setTarget(dialogHelperCardBinding.ivHelperCard)
start()
doOnEnd {
dialogHelperCardBinding.ivHelperCard.setImageResource(cardResource)
}
}
return AlertDialog.Builder(context!!, R.style.Theme_AppCompat_Dialog).setTitle(resources.getString(R.string.got_helper_card)).setNeutralButton(resources.getString(R.string.ok)
) { dialog, _ ->
dialog.dismiss()
}.create()
}
}
That's it. These are my most important files in my problem. Sorry for the lot of code...
I hope you will see where the problem is and tell me what's the solution for it.
Finally I found the error in my code. I left the setView() function call from the second AlertDialog.Builder().
The correct code snippet is:
return AlertDialog.Builder(context!!, R.style.Theme_AppCompat_Dialog)
.setView(dialogHelperCardBinding.root)
.setTitle(resources.getString(R.string.got_helper_card)).setNeutralButton(
resources.getString(R.string.ok)
) { dialog, _ ->
dialog.dismiss()
}.create()
I am using YoutubeplayerApi in my android app
what I have done is that there is a youtubeplayerView and a recycler view below the player
I want to implement the functionality that once a user click on the item view of recycler view the youtubeplayer should play that particular video but I am not able to implement that
any help would be appreciated
activity_music.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UI.MusicActivity">
<com.google.android.youtube.player.YouTubePlayerView
android:layout_width="450dp"
android:layout_height="384dp"
android:id="#+id/youtube_player"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="24dp"
/>
<android.support.v7.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:id="#+id/songs_recycler_view"
app:layout_constraintTop_toBottomOf="#+id/youtube_player"/>
</android.support.constraint.ConstraintLayout>
MusicActivity.kt
package com.example.ashish.batmn.UI
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.widget.Toast
import com.example.ashish.batmn.Adapters.SongsAdapter
import com.example.ashish.batmn.Config
import com.example.ashish.batmn.Models.Songs
import com.example.ashish.batmn.R
import com.google.android.youtube.player.YouTubeBaseActivity
import com.google.android.youtube.player.YouTubeInitializationResult
import com.google.android.youtube.player.YouTubePlayer
import com.google.android.youtube.player.YouTubeStandalonePlayer
import kotlinx.android.synthetic.main.activity_music.*
class MusicActivity : YouTubeBaseActivity(), YouTubePlayer.OnInitializedListener {
val songsList = listOf<Songs>(
Songs("Girls Like You","cBVGlBWQzuc","${Config.IMAGE_BASE_URL}cBVGlBWQzuc${Config.IMAGE_PIC_END}"),
Songs("The National - Fake Empire","KehwyWmXr3U","${Config.IMAGE_BASE_URL}KehwyWmXr3U${Config.IMAGE_PIC_END}"),
Songs("Halka Halka","nZhLM-FeV9A","${Config.IMAGE_BASE_URL}nZhLM-FeV9A${Config.IMAGE_PIC_END}"),
Songs("Camila Cabello - Real Friends","66rxB7_zzs8","${Config.IMAGE_BASE_URL}66rxB7_zzs8${Config.IMAGE_PIC_END}"),
Songs("Darya","wHHCO29mqiA","${Config.IMAGE_BASE_URL}wHHCO29mqiA${Config.IMAGE_PIC_END}"),
Songs("Naina Da Kya Kasoor","BJWTzYPWINw","${Config.IMAGE_BASE_URL}BJWTzYPWINw${Config.IMAGE_PIC_END}")
)
override fun onInitializationSuccess(provider: YouTubePlayer.Provider?, player: YouTubePlayer?, wasRestored: Boolean) {
if (!wasRestored){
player!!.cueVideo(Config.VIDEO_CODE)
player.setPlayerStyle(YouTubePlayer.PlayerStyle.DEFAULT)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == 1){
getYoutubePlayerProvider().initialize(Config.VIDEO_CODE,this)
}
}
override fun onInitializationFailure(p0: YouTubePlayer.Provider?, errorResult: YouTubeInitializationResult?) {
if (errorResult!!.isUserRecoverableError){
errorResult.getErrorDialog(this,1).show()
}
else{
Toast.makeText(this,"Some unforseen error has occured",Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_music)
youtube_player.initialize(Config.API_KEY,this)
val songAdapter = SongsAdapter(songsList,this){
// what should be added in here?
}
songs_recycler_view.layoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)
songs_recycler_view.adapter = songAdapter
}
fun getYoutubePlayerProvider():YouTubePlayer.Provider{
return youtube_player
}
}
Songs.kt // Model class
package com.example.ashish.batmn.Models
class Songs(val mSongName:String, val mVideoCode:String,val mSongPic:String) {
}
First of all in onInitializationSuccess set player instance to a global field. Then Add a onClickListener to listview. After that in onClick method call player.setCue() method with the video id that you want to play. And at the last play the video with player.play().