I am developing a new android app but My app getting showing empty data in recycler view in MainActivity and I have put a breakpoint there are also showing null but I am already initializing data I want to know where I am making mistake what I am missing?
below my RestClient.kt
class RestClient {
internal val httpLoggingInterceptor: HttpLoggingInterceptor
get() {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return httpLoggingInterceptor
}
internal fun getOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.build()
}
companion object {
private val ROOT_URL = "http://www.mocky.io"
/**
* Get Retrofit Instance
*/
private val retrofitInstance: Retrofit
get() = Retrofit.Builder()
.baseUrl(ROOT_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
/**
* Get API Service
*
* #return API Service
*/
val apiService: RestInterface
get() = retrofitInstance.create(RestInterface::class.java)
}
}
below my Post.kt data class
data class Post(
#SerializedName("description")
val description: String,
#SerializedName("id")
val id: Int,
#SerializedName("image")
val image: String,
#SerializedName("published_at")
val publishedAt: String,
#SerializedName("title")
val title: String,
#SerializedName("user_id")
val userId: Int
)
below RestList.kt
data class RestList(
#SerializedName("posts")
val posts: List<Post>
)
below my interface
interface RestInterface {
#get:GET("/v2/59f2e79c2f0000ae29542931")
val getPosts: Call<RestList>
}
below Adapter
class RestAdapter(val post: List<Post>?,val restList: RestList?) : RecyclerView.Adapter<RestAdapter.PostHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostHolder {
val itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_list, null)
return PostHolder(itemView)
}
override fun getItemCount(): Int {
return post?.size!!
}
override fun onBindViewHolder(holder: PostHolder, position: Int) {
val posts = post?.get(position)
Picasso
.get() // give it the context
.load(posts?.image) // load the image
.into(holder.postImage)
holder.userId.text = posts?.userId.toString()
holder.postTitle.text = posts?.title
holder.postTime.text = posts?.publishedAt
holder.postDescription.text = posts?.description
}
class PostHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val postImage: ImageView
val userId: TextView
val postTitle: TextView
val postTime: TextView
val postDescription: TextView
init {
postImage = itemView.findViewById(R.id.postImage)
userId = itemView.findViewById(R.id.userId)
postTitle = itemView.findViewById(R.id.postTitle)
postTime = itemView.findViewById(R.id.postTime)
postDescription = itemView.findViewById(R.id.postDescription)
}
}
}
I am calling retrofit in followingly in mainactivity.kt
class MainActivity : AppCompatActivity() {
companion object{
const val TAG = "internet"
}
private var recyclerView: RecyclerView? = null
private var restAdapter: RestAdapter? = null
private var restInterface: RestInterface? = null
private var postList: List<Post>? = null
private var restList: RestList? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recycler_view)
restInterface = RestClient.apiService
val call = restInterface?.getPosts
call?.enqueue(object : Callback<RestList> {
override fun onResponse(call: Call<RestList>, response: Response<RestList>) {
if (response.body() != null) {
restList = response.body()
val layoutManager = LinearLayoutManager(applicationContext)
recyclerView?.layoutManager = layoutManager
// initialize postList with posts
postList = restList?.posts
restAdapter = RestAdapter(postList, restList)
//this should be come later
recyclerView?.adapter = restAdapter
}
}
override fun onFailure(call: Call<RestList>, t: Throwable) {
Log.e(TAG, "There is no internet connection")
}
})
}
}
below my activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
below post_list.xml
<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"
android:background="#color/colorWhite">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.55" />
<ImageView
android:id="#+id/postImage"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toEndOf="#id/guideline"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="#+id/userId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
android:layout_marginLeft="25dp"
android:layout_marginTop="10dp"
android:text="Placeholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/postTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="Secondary"
app:layout_constraintEnd_toStartOf="#id/postImage"
app:layout_constraintStart_toStartOf="#id/userId"
app:layout_constraintTop_toBottomOf="#id/userId" />
<TextView
android:id="#+id/postTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
android:text="Tertiary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="#id/userId"
app:layout_constraintTop_toBottomOf="#id/postTitle" />
<TextView
android:id="#+id/postDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
android:text="Tertiary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="#id/userId"
app:layout_constraintTop_toBottomOf="#id/postTime" />
</androidx.constraintlayout.widget.ConstraintLayout>
There are multiple problems in your implementation. Check below:
Problem - 1: You didn't initialize you restInterface
restInterface = RestClient.apiService
val call = restInterface?.getPosts
Problem - 2: You didn't initialize your postList after getting from API
postList = restList?.posts
Problem - 3: Initialize your RecyclerView with null adapter as you assign adapter before initialize it
if (response.body() != null) {
restList = response.body()
val layoutManager = LinearLayoutManager(applicationContext)
recyclerView?.layoutManager = layoutManager
// initialize postList with posts
postList = restList?.posts
restAdapter = RestAdapter(postList, restList)
//this should be come later
recyclerView?.adapter = restAdapter
}
Problem - 4: Add usesCleartextTraffic in your AndroidManifest.xml to support http request
<application
android:usesCleartextTraffic="true"
... >
I think you should define adapter before set adapter to recyclerview, swap the lines like this.
restAdapter = RestAdapter(postList, restList)
recyclerView?.adapter = restAdapter
Related
RecyclerView
class RecyclerViewAdapter: RecyclerView.Adapter<RecyclerViewAdapter.RepoViewHolder>(){
inner class RepoViewHolder(val binding: RepoelementBinding): RecyclerView.ViewHolder(binding.root)
private val diffCallBack = object : DiffUtil.ItemCallback<userReposItem>(){
override fun areItemsTheSame(oldItem: userReposItem, newItem: userReposItem): Boolean {
return oldItem.url == newItem.url
}
override fun areContentsTheSame(oldItem: userReposItem, newItem: userReposItem): Boolean {
return oldItem == newItem
}
}
private val differ= AsyncListDiffer(this, diffCallBack)
var repos: List<userReposItem>
get()=differ.currentList
set(value) {differ.submitList(value)}
override fun getItemCount() = repos.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepoViewHolder {
return RepoViewHolder(RepoelementBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
))
}
override fun onBindViewHolder(holder: RepoViewHolder, position: Int) {
holder.binding.apply{
val repo = repos[position]
urlOfRepo.text = repo.url
nameOfRepo.text= repo.description
}
}
}
RepoService
private const val BASE_URL = "https://api.github.com/"
//private const val ENDPOINT_URL= "https://api.github.com/users/${username}/repos"
interface GitHubReposAPIService{
//using only GetUserRepos
#GET("/users/{username}/repos")
suspend fun getUserRepos(#Path("username") username: String) : Response<List<userReposItem>>
#GET(BASE_URL+"users/{username}/{repo}/language")
suspend fun getLanguages(#Path("username") username:String,
#Path("repo") repo: String) : String
}
object UserRepoAPI{
private val moshi= Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val retrofitService : GitHubReposAPIService by lazy {
Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
.create(GitHubReposAPIService::class.java)
}
//val retrofitService : GitHubReposAPIService by lazy {
retrofit.create(GitHubReposAPIService::class.java)}
}
Activity with List
const val TAG= "Repos"
class UserProfile : AppCompatActivity() {
private lateinit var userName: String
private lateinit var Repos : RecyclerViewAdapter
private lateinit var binding: ActivityUserProfileBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// val binding: ViewDataBinding? = DataBindingUtil.setContentView(this,
R.layout.activity_user_profile)
binding= ActivityUserProfileBinding.inflate(layoutInflater)
setContentView(binding.root)
userName = intent.getStringExtra("username").toString()
val viewUserName= findViewById<TextView>(R.id.userDisplay)
viewUserName.text=userName
setupRepos()
lifecycleScope.launchWhenCreated {
val response=try{
UserRepoAPI.retrofitService.getUserRepos(userName)
} catch (e: IOException){
Log.e(TAG, "Might not have internet connection")
return#launchWhenCreated
} catch (e: HttpException){
Log.e(TAG, "HttpException, invalid response")
return#launchWhenCreated
}
if(response.isSuccessful && response.body()!=null){
Repos.repos=response.body()!!
}else{
Log.e(TAG, "Response not succesful")
}
}
}
private fun setupRepos() = binding.recyclerView?.apply{
Repos = RecyclerViewAdapter()
adapter = Repos
layoutManager = LinearLayoutManager(this#UserProfile)
}
}
Data class
I only want two parameters ( url of repo and its description). There is many more information, but I only need those 2 for every repo.
#JsonClass(generateAdapter = true)
#JsonIgnoreProperties(ignoreUnknown = true)
data class userReposItem(
#Json(name = "description")
val description: String? = "",
#Json(name = "url")
val url: String? = "",
) : Parcelable{
constructor(parcel: Parcel) : this(
parcel.readString()!!,
parcel.readString()!!
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(description)
parcel.writeString(url)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<userReposItem> {
override fun createFromParcel(parcel: Parcel): userReposItem {
return userReposItem(parcel)
}
override fun newArray(size: Int): Array<userReposItem?> {
return arrayOfNulls(size)
}
}
}
Layouts:
Main layout for printing list of repos. At the top of layout we have name of user and below should be list of repos (but as I mentioned it doesnt work)
<?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=".UserProfile">
<TextView
android:id="#+id/userDisplay"
android:layout_width="374dp"
android:layout_height="38dp"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="21dp"
android:text="#string/username"
app:layout_constraintBottom_toTopOf="#+id/recyclerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="409dp"
android:layout_height="547dp"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
List item
I would like to print url of repo and its name ("description" as shown in github API)
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/urlOfRepo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="89dp"
android:layout_marginBottom="489dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/nameOfRepo" />
<TextView
android:id="#+id/nameOfRepo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/urlOfRepo" />
</LinearLayout>
I am developing a news app and I am following MVVM with data binding in recycler view I am trying to bind items but I am just stuck below my recyclerview items xml file
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="article"
type="yodgorbek.komilov.musobaqayangiliklari.model.Article">
</variable>
</data>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp">
<ImageView
android:text="#{article.urlToImage}"
android:id="#+id/imageView"
android:layout_width="100dp"
android:layout_height="85dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:contentDescription="bbc"
tools:background="#color/colorPrimary" />
<TextView
android:id="#+id/articleTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="#id/imageView"
android:layout_toRightOf="#id/imageView"
android:ellipsize="end"
android:lines="3"
android:maxLines="3"
android:text="#{article.title}" />
<ImageView
android:id="#+id/imageCategory"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_toEndOf="#id/imageView"
android:layout_toRightOf="#id/imageView"
android:src="#drawable/ic_espn"
tools:background="#color/colorPrimary" />
<TextView
android:id="#+id/articleSourceName"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_toEndOf="#id/imageCategory"
android:layout_toRightOf="#id/imageCategory"
android:gravity="center|start"
android:text="#{article.source.name}" />
<TextView
android:id="#+id/articleTime"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="#id/articleSourceName"
android:layout_toRightOf="#id/articleSourceName"
android:gravity="center|start"
android:text="#{article.publishedAt}"
android:textColor="#android:color/darker_gray"
tools:ignore="NotSibling" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</layout>
below TopHeadlinesAdapter.kt where I am trying to implement data binding logic
#Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
class TopHeadlinesAdapter(val context: Context, private val article: List<Article>) :
RecyclerView.Adapter<TopHeadlinesAdapter.MyViewHolder>() {
private var articleList: List<Article> by Delegates.observable(emptyList()) { _, _, _ ->
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater =
LayoutInflater.from(parent.context)//.inflate(R.layout.news_list, parent, false)
// val binding = Article.inflate(inflater)
return MyViewHolder(article, parent)
}
override fun getItemCount(): Int {
return articleList.size
}
#SuppressLint("NewApi")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) =
holder.bind(articleList[position])
//holder.articleTitle.text = articleList.get(position).title
//holder.articleSourceName.text = articleList.get(position).source.name
//Picasso.get().load(articleList.get(position).urlToImage).into(holder.image)
// val input = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")
// val output = SimpleDateFormat("dd/MM/yyyy")
// var d = Date()
// try {
// d = input.parse(articleList[5].publishedAt)
// } catch (e: ParseException) {
// try {
// val fallback = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
// fallback.timeZone = TimeZone.getTimeZone("UTC")
// d = fallback.parse(articleList[5].publishedAt)
// } catch (e2: ParseException) {
// // TODO handle error
// val formatted = output.format(d)
// val timelinePoint = LocalDateTime.parse(formatted)
// val now = LocalDateTime.now()
//
// var elapsedTime = Duration.between(timelinePoint, now)
//
// println(timelinePoint)
// println(now)
// elapsedTime.toMinutes()
//
// holder.articleTime.text = "${elapsedTime.toMinutes()}"
//
//
fun updateData(newList: List<Article>) {
articleList = newList
Log.e("articleListSize", articleList?.size.toString())
}
inner class MyViewHolder(private val binding: Article) : RecyclerView.ViewHolder(binding.root) {
fun bind(article: Article) {
binding.article = article
// val image: ImageView = itemView!!.findViewById(R.id.imageView)
// val articleTitle: TextView = itemView!!.findViewById(R.id.articleTitle)
// val articleSourceName: TextView = itemView!!.findViewById(R.id.articleSourceName)
// val imageCategory: ImageView = itemView!!.findViewById(R.id.imageCategory)
// val articleTime: TextView = itemView!!.findViewById(R.id.articleTime)
}
}
}
below my Article.kt data class
data class Article(
val author: String,
val content: String,
val description: String,
val publishedAt: String,
val source: Source,
val title: String,
val url: String,
val urlToImage: String
)
below fragment_top_headlines.xml where I am hosting recyclerview
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="#+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
below TopHeadlinesFragment.kt
class TopHeadlinesFragment : Fragment() {
private val viewModel by viewModel<MainViewModel>()
private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
// private val newsRepository: NewsRepository by inject()
private lateinit var article:List<Article>
//3
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(
R.layout.fragment_top_headlines
, container, false
)
val recyclerView = view.findViewById(R.id.recyclerView) as RecyclerView
val pb = view.findViewById(R.id.pb) as ProgressBar
topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context, article)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()
return view
}
private fun initViewModel() {
viewModel?.sportList?.observe(this, Observer { newList ->
topHeadlinesAdapter.updateData(newList)
})
viewModel?.showLoading?.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
viewModel?.showError?.observe(this, Observer { showError ->
(showError)
})
viewModel?.loadNews()
}
}
I want to know what I have to do in order to bind correctly recyclerview items and show correctly in my app?
try this in
class yourAdapterName(val context: Context, var listData:
MutableList<Article>) :
RecyclerView.Adapter<LanguageAdapter.ViewHolder>() {
lateinit var bindind: YourBinding
fun onRefresh(listData: MutableList<Article>) {
this.listData = listData
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
ViewHolder {
bindind = YourBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bindind)
}
override fun getItemCount(): Int {
return listData.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.setData(listData[position])
}
inner class ViewHolder(private val binding: LangugaeItemBinding) : RecyclerView.ViewHolder(binding.getRoot()) {
fun setData(model: Article) {
with(binding) {
artical = model
executePendingBindings()
}
}
}
}
and call these into onViewCreated in fragment
val recyclerView = view.findViewById(R.id.recyclerView) as
RecyclerView
val pb = view.findViewById(R.id.pb) as ProgressBar
topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context,
article)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()
I am using FirestorePagingAdapter to show data in my app but when I use Swipe Refresh Layout did not work with FirestorePagingAdapter, swipe refresh layout.isVisible = true in LOAD_MORE when I don't scroll
how to work with a Swipe Refresh Layout in FirestorePagingAdapter?
ViewHolde.class:
class ViewHolde(item: View) : RecyclerView.ViewHolder(item){
val username = item.findViewById(R.id.iduser) as TextView
val title = item.findViewById(R.id.idtitle) as TextView
val decription = item.findViewById(R.id.iddes) as TextView
}
User.class:
class User {
lateinit var userphoto: StorageReference
lateinit var username:String
lateinit var title:String
lateinit var notes: String
lateinit var id: String
var isSelected:Boolean = false
constructor(){
}
constructor( id: String?,username:String?,title: String?, notes: String?) {
this.title= title!!
this.id= id!!
this.username= username!!
this.notes= notes!!
}
}
RecyclerActivity.class:
class RecyclerActivity : AppCompatActivity(),SwipeRefreshLayout.OnRefreshListener {
var mylist=ArrayList<User>()
var rv:RecyclerView? = null
var currentuser = FirebaseAuth.getInstance().currentUser!!.uid
val str= FirebaseStorage.getInstance().reference
private var mAuthStateListener: FirebaseAuth.AuthStateListener? = null
var db = FirebaseFirestore.getInstance();
private val notebookRef2 = db.collection("ProfileData")
private val mQuery = notebookRef2.orderBy("title", Query.Direction.ASCENDING)
val config = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(2)
.setPageSize(6)
.build()
val options = FirestorePagingOptions.Builder<User>()
.setLifecycleOwner(this)
.setQuery(mQuery, config, User::class.java)
.build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler)
rec()
var swipeContainer = findViewById(R.id.swipeContainer)as SwipeRefreshLayout
swipeContainer.setOnRefreshListener(this);
swipeContainer = findViewById(R.id.swipeContainer)as SwipeRefreshLayout
swipeContainer.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
swipeContainer.setOnRefreshListener {
rv?.invalidate();
swipeContainer.setRefreshing(false);
}
}
override fun onRefresh() {
Handler().postDelayed( Runnable() {
swipeContainer.setRefreshing(false);
}, 3000);
}
fun rec(){
rv=findViewById(R.id.idrecycler) as? RecyclerView
rv!!.setHasFixedSize(true)
rv!!.layoutManager = LinearLayoutManager(this)
db.collection("ProfileData").orderBy("title", Query.Direction.DESCENDING)
.addSnapshotListener { querySnapshot, firebaseFirestoreException ->
if (querySnapshot != null) {
for(da in querySnapshot) {
val note = da.toObject(Note::class.java)
var title = note.title
var description = note.notes
var username = note!!.username
var id = note!!.id
mylist.add(User(id!!,username!!, title!!, description!!))
}
}
}
setupadapter()
}
fun setupadapter(){
rv=findViewById(R.id.idrecycler) as? RecyclerView
var mAdapter = object : FirestorePagingAdapter<User, ViewHolde>(options) {
override fun onLoadingStateChanged(state: LoadingState) {
swipeRefreshLayout2.isVisible = false
when (state) {
LoadingState.LOADING_INITIAL -> {
swipeRefreshLayout2.isVisible = true
}
LoadingState.LOADING_MORE -> {
swipeRefreshLayout.isVisible = true
}
LoadingState.LOADED -> {
swipeRefreshLayout.isVisible = false
}
LoadingState.ERROR -> {
swipeRefreshLayout.isVisible = false
}
LoadingState.FINISHED -> {
swipeRefreshLayout.isVisible = false
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolde {
var v = LayoutInflater.from(parent.context)
.inflate(R.layout.iktem_list, parent, false)
return ViewHolde(v)
}
override fun onBindViewHolder(holder: ViewHolde, position: Int, p2: User) {
holder.username.text = mylist.get(position).username
holder.title.text = mylist.get(position).title
holder.decription.text = mylist.get(position).notes
}
}
rv!!.adapter = mAdapter
}
}
ActivityRecycler.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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"
android:id="#+id/iddrawer2"
tools:context=".RecyclerActivity">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_height="match_parent"
android:layout_width="match_parent" >
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#4C00CF"
android:id="#+id/idtoolbar2"
tools:ignore="MissingConstraints"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swipeContainer"
android:layout_width="match_parent"
android:layout_marginTop="50dp"
android:layout_height="460dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/idrecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="#+id/swipeRefreshLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="340dp"
android:visibility="gone"
/>
<ProgressBar
android:id="#+id/swipeRefreshLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="450dp"
android:visibility="gone"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:headerLayout="#layout/header"
android:id="#+id/idnavigatuonv2"
android:layout_gravity="start"
app:menu="#menu/draw_menu"/>
</androidx.drawerlayout.widget.DrawerLayout >
So I'm using the adapter to show transaction history and I have a button to show the filtered transaction history which has status == success.
On button click, I'm updating the adapter the following way:
baseQuery = firestore.collection("transactions")
.whereEqualTo("status", "success")
.orderBy("__name__", Query.Direction.DESCENDING);
options = new FirestorePagingOptions.Builder<ItemTxn>()
.setLifecycleOwner(this)
.setQuery(baseQuery, config, ItemTxn.class)
.build();
adapter.updateOptions(options);
https://github.com/firebase/FirebaseUI-Android/issues/1726#issuecomment-573033035
I have an exisitng code which I integrate with Live data by using retrofit.
Now if I want to integrate databinding, where are all changes to be done to make the code looks perfect?
Here is my code.
activity_main.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:background="#000000"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Android Versions"
android:textColor="#ffffff"
android:textSize="20sp" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
items.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"
android:layout_width="match_parent"
android:background="#445566"
android:layout_margin="5dp"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvFname"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top|start"
android:layout_marginTop="8dp"
android:textColor="#ffffff"
android:layout_weight="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="#+id/tvLname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|start"
android:padding="5dp"
android:layout_marginTop="10dp"
android:layout_weight="1"
android:ellipsize="end"
android:textColor="#ffffff"
android:maxLines="5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvFname"/>
</android.support.constraint.ConstraintLayout>
Here is view model class which I integrated
class AndroidViewModel:ViewModel() {
private val mService = RetrofitService()
fun getAndroidData():MutableLiveData<List<AndroidData>>?{
return mService.loadAndroidData()
}
}
Pojo class generated is just a simple one:
data class AndroidData (val name:String, val apiLevel:String)
Service integration:
class RetrofitService {
val liveUserResponse:MutableLiveData<List<AndroidData>> = MutableLiveData()
companion object Factory {
var gson = GsonBuilder().setLenient().create()
fun create(): ApiInterface {
Log.e("retrofit","create")
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl("https://learn2crack-json.herokuapp.com/api/")
.build()
return retrofit.create(ApiInterface::class.java)
}
}
fun loadAndroidData(): MutableLiveData<List<AndroidData>>? {
Log.e("loadAndroidData","yes")
val retrofitCall = create().getAndroid()
retrofitCall.enqueue(object : Callback<List<AndroidData>> {
override fun onFailure(call: Call<List<AndroidData>>, t: Throwable?) {
Log.e("on Failure :", "retrofit error")
}
override fun onResponse(call: Call<List<AndroidData>>, response: retrofit2.Response<List<AndroidData>>) {
val list = response.body()
for (i in list.orEmpty()){
Log.e("on response 1:", i.name)
}
liveUserResponse.value = list
Log.e("hasActiveObservers 1", liveUserResponse.hasActiveObservers().toString()+" check")
Log.e("on response 2 :", liveUserResponse.toString()+" check")
}
})
return liveUserResponse
}
Main Activity:
class MainActivity : AppCompatActivity(){
private lateinit var linearLayoutManager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
linearLayoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
getAndroidVersion()
}
private fun getAndroidVersion() {
Log.e("getAndroidVersion", "yes")
val mAndroidViewModel = ViewModelProviders.of(this#MainActivity).get(AndroidViewModel::class.java)
mAndroidViewModel.getAndroidData()?.observe(this, Observer<List<AndroidData>> { androidList ->
Log.e("list", androidList?.size.toString())
recyclerView.adapter = EmpAdapter(this#MainActivity, androidList as ArrayList<AndroidData>, object :
ItemClickListener {
override fun onItemClick(pos: Int, name:String) {
Toast.makeText(applicationContext, "item "+pos+ "clicked"+ name, Toast.LENGTH_LONG).show()
}
})
})
}
}
Adapter class:
class EmpAdapter(
var context: MainActivity,
var mEmpList: ArrayList<AndroidData>,
private val itemClick:ItemClickListener
) :
RecyclerView.Adapter<EmpAdapter.EmpHolder>() {
override fun getItemCount(): Int {
return mEmpList.size
}
companion object {
var mItemClickListener : ItemClickListener? = null
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmpHolder {
val view = LayoutInflater.from(context).inflate(R.layout.items, parent, false)
return EmpHolder(view)
}
override fun onBindViewHolder(holder: EmpHolder, position: Int) {
mItemClickListener = itemClick
holder.tvFname?.text = mEmpList[position].name
holder.tvLname?.text = mEmpList[position].apiLevel
RxView.clicks(holder.mView).subscribe {
mItemClickListener!!.onItemClick(position,mEmpList[position].name)
}
}
class EmpHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvFname = view.tvFname
val tvLname = view.tvLname
val mView = view
}
}
interfaces for retrofit:
interface ApiInterface {
#GET("android")
fun getAndroid(): Call<List<AndroidData>>
}
Now my question is, if I integrate tag and in xml, which variable i need to specify for textview to integrate to connect for textview in adapter? Kindly guide me where are all I need to change to apply, if I use live data
First of all, i strongly recommend to use a Retrofit Adapter that converts the response into LiveData automatically for you. See this example
https://github.com/yasiralijaved/android-architecture-components/blob/master/component_http/src/main/java/com/yasiralijaved/android/arc/component/http/BackendService.java
Secondly there are detailed tutorials out there which can surely help you how to implement Data Bindings in RecyclerView Adapter. A few tutorials are:
https://medium.com/androiddevelopers/android-data-binding-recyclerview-db7c40d9f0e4
https://android.jlelse.eu/how-to-bind-a-list-of-items-to-a-recyclerview-with-android-data-binding-1bd08b4796b4
Do let me know if it is still not clear and i will share some more details.
Happy coding!!
First of all, I'm new to Android/Java/Kotlin development. I'm using Retrofit2 to retrieve data from Udacity API. I can see the response in the Logcat, but when I try to display it in RecyclerView there is just a blank screen. I think the error is in the MainActivity, but I'm still clueless and need help.
My model classes
class Course(var title: String,
var subtitle: String,
var key: String,
var instructors: List<Instructor>,
var expected_learning: String,
var required_knowledge: String)
class Instructor(var name: String,
var bio: String)
My interface
interface ApiServiceInterface {
#GET("courses")
fun list() : Call<UdacityCatalog>
}
My adapter
class CourseAdapter(val listCourses: ArrayList<Course?>, val context: Context) : RecyclerView.Adapter<CourseAdapter.CourseViewHolder>() {
class CourseViewHolder(viewItem: View) : RecyclerView.ViewHolder(viewItem) {
val courseTitle = viewItem.findViewById<TextView?>(R.id.tv_title) as TextView
val courseSubtitle = viewItem.findViewById<TextView?>(R.id.tv_subtitle) as TextView
val courseKey = viewItem.findViewById<TextView?>(R.id.tv_course_key) as TextView
val instructorName = viewItem.findViewById<TextView?>(R.id.tv_instructor_name) as TextView
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): CourseViewHolder {
val viewHolder = LayoutInflater.from(parent?.context).inflate(R.layout.activity_item_list, parent, false)
val itemViewHolder = CourseViewHolder(viewHolder)
return itemViewHolder
}
override fun onBindViewHolder(holder: CourseViewHolder?, position: Int) {
holder?.courseTitle?.text = listCourses[position].title
holder?.courseSubtitle?.text = listCourses[position].subtitle
holder?.courseKey?.text = listCourses[position].key
holder?.instructorName?.text = listCourses[position].instructors.toString()
}
override fun getItemCount(): Int = listCourses.size
}
My MainActivity -- I think the error is in here, but I haven't figured it out yet
class MainActivity : AppCompatActivity() {
internal val TAG = "Testing Retrofit2 API"
lateinit var mRecyclerView: RecyclerView
lateinit var mCourseAdapter: RecyclerView.Adapter<CourseAdapter.CourseViewHolder>
val listCourse = arrayListOf<Course?>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_recyclerview)
mRecyclerView = findViewById<RecyclerView?>(R.id.id_recycler_view) as RecyclerView
val mLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
mRecyclerView.layoutManager = mLayoutManager
mRecyclerView.setHasFixedSize(true)
mCourseAdapter = CourseAdapter(listCourse, applicationContext)
mRecyclerView.adapter = mCourseAdapter
mCourseAdapter.notifyDataSetChanged()
val call = RetrofitInitializer().createService().list()
call.enqueue(object : Callback<UdacityCatalog> {
override fun onResponse(call: Call<UdacityCatalog>, response: Response<UdacityCatalog>) {
response.body()
if (!response.isSuccessful) {
Log.i(TAG, "[ ERROR ] " + response.code())
} else {
if (listCourse.isEmpty()) {
Toast.makeText(applicationContext, "Empty list!", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(applicationContext, "Full list!", Toast.LENGTH_LONG).show()
}
val catalog = response.body()
for (c in catalog!!.courses!!) {
Log.i(TAG, String.format("%s: %s", c.title, c.subtitle, c.key, c.required_knowledge, c.expected_learning))
for (i in c.instructors!!) {
Log.i(TAG, i.name)
}
Log.i(TAG, "\n****************************************************************************************************\n")
}
}
}
override fun onFailure(call: Call<UdacityCatalog>?, t: Throwable?) {
Log.d(TAG, "onFailure() : " + t?.message)
}
})
}
}
An initializer class for Retrofit instance
class RetrofitInitializer {
companion object Factory {
val BASE_URL = "https://www.udacity.com/public-api/v0/"
}
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun createService(): ApiServiceInterface = retrofit.create(ApiServiceInterface::class.java)
}
My RecyclerView xml file
<?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="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
tools:context="activities.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/id_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
My CardView xml file
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#string/udacity_catalog"
android:textAppearance="?android:textAppearanceLarge"
android:textColor="#9C27B0"
android:textStyle="bold" />
<TextView
android:id="#+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="start"
android:hint="#string/hint_title"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="#000000" />
<TextView
android:id="#+id/tv_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="start"
android:hint="#string/hint_subtitle"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="#000000" />
<TextView
android:id="#+id/tv_course_key"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="start"
android:hint="#string/hint_key"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="#000000" />
<TextView
android:id="#+id/tv_instructor_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="start"
android:hint="#string/hint_name"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="#000000" />
</LinearLayout>
</android.support.v7.widget.CardView>
You have an empty list of courses (listCourse) in your Activity. You pass that empty list to the adapter via it's constructor.
That list stays empty even after you fetched the courses. So the adapter / RecyclerView stays empty.
Try creating the adapter after you fetched the courses, with
mCourseAdapter = CourseAdapter(catalog!!.courses!!, applicationContext)
or have a courses property in your adapter lile
YourAdapter(...) {
var courses = listOf(Course)
}
and then in your Activity (after fetching the courses):
adapter.courses = catalog!!.courses!!
adapter.notifyDataSetChanged()