Getting 'E/RecyclerView: No adapter attached; skipping layout' in Kotlin tabbedActivity - android

Hi I am beginning with Kotlin and tried to create tabbedActicity with parsed list. I am getting data with retrofit from google APIs.
Then I try to pass it to recycleView.
I tried many ways, but I keep getting error: E/RecyclerView: No adapter attached; skipping layout
Corresponding textView is present in score_row.xml layout and RecyclerView in thirdtab.xml layout.
thirdTabFragment.kt :
class thirdtabFragment : AppCompatActivity() {
private lateinit var binding: ThirdtabBinding
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
binding = ThirdtabBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
val apiInterface = APIClient.client.create(ApiInterface::class.java)
val call = apiInterface.getScore()
call.enqueue(object : Callback<List<Item>> {
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
Log.d("Success!", response.toString())
showContenders(response.body()!!)
}
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
Log.e("Failed Query :(", t.toString())
}
})
}
private fun showContenders(contenderList: List<Item>) {
binding.recyclerView.apply{
layoutManager = LinearLayoutManager(this#thirdtabFragment)
adapter = ScoreAdapter(contenderList)
}
}
ScoreAdapter.kt :
class ScoreAdapter(private val contenderList: List<Item>) : RecyclerView.Adapter<ScoreAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.score_row, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = contenderList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val contender = contenderList[position]
holder.contenderName.text = contender.contenderName
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val contenderName: TextView = itemView.findViewById(R.id.contenderName_tv)
}
}

Attach the adapter sooner, in the onCreate method.
For that make contenderList in ScoreAdapter public
var contenderList = listOf<Item>()
set(value){
field = value
notifyDataSetChanged()
}
then
//move this code to onCreate
adapter = ScoreAdapter()
binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = adapter
Then modify the onResponse method
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
Log.d("Success!", response.toString())
adapter.contenderList = response.body()!!
}

Related

how to add onClickListener in recyclerview?

I don't know how to add onclicklistener to here,
I've tried many ways but it doesn't work,
i made this with recyclerview,
I've tried to add a new variable in the adapter section but error,
I've also made activity details so I just need to hit recyclerview and go to details page, I gave the code that has not been added onclicklistener
this MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var adapter: MakeUpAdapter
#SuppressLint("NotifyDataSetChanged")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
rv_kamu.setHasFixedSize(true)
rv_kamu.layoutManager = LinearLayoutManager(this)
adapter = MakeUpAdapter()
adapter.notifyDataSetChanged()
rv_kamu.adapter = adapter
getDataFromApi()
}
private fun getDataFromApi() {
Retrofit.instance.getMakeUpList()
.enqueue(object : Callback<ArrayList<MakeUpModel>>{
override fun onResponse(
call: Call<ArrayList<MakeUpModel>>,
response: Response<ArrayList<MakeUpModel>>
) {
if(response.isSuccessful) {
showData(response.body()!!)
}
}
override fun onFailure(call: Call<ArrayList<MakeUpModel>>, t: Throwable) {
Log.e("Fail", "Be Fail")
}
})
}
private fun showData(data: ArrayList<MakeUpModel>) {
val result = data
adapter.setData(result)
}
}
and this adapter
class MakeUpAdapter : RecyclerView.Adapter<MakeUpAdapter.MakeUpViewHolder>(){
private val data = ArrayList<MakeUpModel>()
inner class MakeUpViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind(data: MakeUpModel) {
//lib3
Glide.with(itemView.context)
.load(data.Picture)
.apply(
RequestOptions()
.override(100, 100)
)
.into(itemView.iv_item)
itemView.tv_Name.text = data.Name
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MakeUpViewHolder {
val view: View =
LayoutInflater.from(parent.context).inflate(R.layout.item_makeup, parent, false)
return MakeUpViewHolder(view)
}
override fun onBindViewHolder(holder: MakeUpViewHolder, position: Int) {
holder.bind(data[position])
}
override fun getItemCount(): Int = data.size
#SuppressLint("NotifyDataSetChanged")
//buat mainac
fun setData(list: ArrayList<MakeUpModel>) {
data.clear()
data.addAll(list)
notifyDataSetChanged()
}
}
This is my way.
first, create a interface to listen click event:
interface RecycleViewOnClickListener {
fun onItemClick(pos :Int)
}
and in your adapter class, put interface in param, then call listener in onBindViewHolder:
class MakeUpAdapter(private val clickListener: RecycleViewOnClickListener) : RecyclerView.Adapter<MakeUpAdapter.MakeUpViewHolder>(){
override fun onBindViewHolder(holder: MakeUpViewHolder, position: Int) {
holder.bind(data[position])
holder.itemView.setOnClickListener {
clickListener.onItemClick(position)
}
}
}
}
In your activity, implement listener like this:
class MainActivity : AppCompatActivity(), RecycleViewOnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
adapter = MakeUpAdapter(this)
}
override fun onItemClick(pos: Int) {
//do anything with your position here, such as get item from result
}
}
Hope it's helpful for you
on onBindViewHolder() you pass the position when calling the callback method:
override fun onBindViewHolder(holder: Holder?, position: Int) {
var item : MyObject = objects[position]
// Calling the clickListener sent by the constructor
holder.vieww.setOnClickListener {
// click event
}
}
There are a few ways one could add a click listener, one way writing this more idiomatically might be:
class MainActivity : AppCompatActivity() {
private val adapter by lazy { MakeUpAdapter() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
rv_kamu.layoutManager = LinearLayoutManager(this)
adapter.notifyDataSetChanged()
adapter.listener = { /* TODO handle click event */ }
rv_kamu.adapter = adapter
getDataFromApi()
}
}
class MakeUpAdapter : RecyclerView.Adapter<MakeUpAdapter.MakeUpViewHolder>(){
var listener: (MakeUpModel) -> Unit = {}
private val data = ArrayList<MakeUpModel>()
inner class MakeUpViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind(data: MakeUpModel) {
itemView.setOnClickListener { listener(data) }
//bind data
}
}
// plug in rest of your code
}
You might also want to consider moving the api call with retrofit into an Android ViewModel so that the network call will survive configuration changes (device rotation). This is a very simple example, but gets the point across and encourage you to check out the official android documentation on the subject.
class MakeUpViewModel( : ViewModel() {
init {
viewModelScope.launch {
val response = Retrofit.instance.getMakeUpList()
// update state with response
}
}
}

Cant seem to get my adapter to add my list to recyclerView

Id like to initially have a list show up on my recyclerView before implementing some kind of edit function to it. However, no matter what I do, the list I made wont show up. I've got a feeling that I'm doing something wrong with the bind function.
Here is my adapter
class WorkoutAdaptor (
var workouts: List<Workout>
) : RecyclerView.Adapter<WorkoutAdaptor.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val workoutCardBinding = WorkoutCardBinding.inflate(
LayoutInflater.from(parent.context), parent, false)
return ViewHolder(workoutCardBinding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(workouts[position])
}
override fun getItemCount(): Int {
return workouts.size
}
inner class ViewHolder(private val workoutCardBinding: WorkoutCardBinding) :
RecyclerView.ViewHolder(workoutCardBinding.root) {
fun bind(workout: Workout) {
workoutCardBinding.apply {
tvWorkoutCard
}
}
}
}
and here is my main activity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnNext.setOnClickListener {
Intent(this, SecondActivity::class.java).also {
startActivity(it)
}
}
// var workout = intent.getSerializableExtra("EXTRA_WORKOUT")
// binding.tvWorkoutCard.text = workout.toString()
var workoutList = mutableListOf(
Workout("a","d","d","d"),
Workout("a","d","d","d"),
Workout("a","d","d","d"),
Workout("a","d","d","d"),
Workout("a","d","d","d"),
)
val adaptor = WorkoutAdaptor(workoutList)
binding.recyclerView.adapter = adaptor
binding.recyclerView.layoutManager = LinearLayoutManager(this)
}
}
Change your bind method to this.
fun bind(workout: Workout) {
workoutCardBinding.textview.text = workout.objectName
}

Delete Item in RecyclerView and Dao with buttonclick

I have a RecyclerView displaying data from a Dao.
In the data that is shown in the recyclerView is a button with which the item should get deleted.
I know the function for that is :
fun deleteReceipt(receipts: Receipts) = viewModelScope.launch {
receiptDao.delete(receipts)
}
But I am not quite sure where to put it and call it. Because if I want to call it in the Adapter where i set the other displayed Item values I have no access to the Dao
Here is the fragment:
#AndroidEntryPoint
class HistoryFragment : Fragment(R.layout.fragment_history) {
private val viewModel: PurchaseViewmodel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentHistoryBinding.bind(view)
val exampleAdapter = ExampleAdapter()
binding.apply{
recyclerView.apply{
layoutManager = LinearLayoutManager(requireContext())
adapter = exampleAdapter
setHasFixedSize(true)
}
}
setFragmentResultListener("add_receipt_request"){_,bundle ->
val result = bundle.getInt("add_receipt_request")
viewModel.onAddResult(result)
}
viewModel.receipts.observe(viewLifecycleOwner){ /// New Items get passed to the List
exampleAdapter.submitList(it)
}
}
}
The ViewModel:
#HiltViewModel
class PurchaseViewmodel #Inject constructor(
private val receiptDao: ReceiptDao
): ViewModel() {
private val tasksEventChannel = Channel<TasksEvent>()
val addTaskEvent = tasksEventChannel.receiveAsFlow()
val receipts = receiptDao.getAllReceipts().asLiveData()
fun onAddResult(result: Int){
when (result){
ADD_RECEIPT_RESULT_OK ->showReceiptSavedConfirmation("Receipt is saved")
}
}
private fun showReceiptSavedConfirmation (text: String) = viewModelScope.launch {
tasksEventChannel.send(TasksEvent.ShowReceiptSavedConfirmation(text))
}
fun deleteReceipt(receipts: Receipts) = viewModelScope.launch {
receiptDao.delete(receipts)
}
sealed class TasksEvent {
data class ShowReceiptSavedConfirmation(val msg: String) : TasksEvent()
}
}
And the Adapter:
class ExampleAdapter : ListAdapter<Receipts,ExampleAdapter.ExampleViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExampleViewHolder { // Basically how to get a new Item from the List and display it
val binding = ReceiptsBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return ExampleViewHolder(binding)
}
override fun onBindViewHolder(holder: ExampleViewHolder, position: Int) {
val currentItem = getItem(position)
holder.bind(currentItem)
}
override fun getItemCount(): Int {
return super.getItemCount()
}
class ExampleViewHolder(private val binding: ReceiptsBinding) : RecyclerView.ViewHolder(binding.root){ //Examples One Row in our list
fun bind (receipts: Receipts) {
binding.apply {
storeHistory.text = receipts.store
amountHistory.text = receipts.total
dateHistory.text = receipts.date
}
}
}
class DiffCallback : DiffUtil.ItemCallback<Receipts>() {
override fun areItemsTheSame(oldItem: Receipts, newItem: Receipts) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Receipts, newItem: Receipts) =
oldItem == newItem
}
}
Create a listener using Interface
Implement the listener in Fragment
Create listener variable in Adapter
pass the listener from fragment to adapter
pass the listener to viewHolder
on Delete button click of that item call the listener method, which will trigger the implementation in Fragment where you have access to your ViewModel
delete the receipt using viewModel.deleteReceipt

I am trying to infalte the activitymain.xml file but i don't know why adapter class is not being inflated

This my adapter class which is trying to inflate the recycler view with the video_View and the arraylist is ending up with the value 0. I have tried a lot of method to make it working but every time it is everytime not inflating the recyclerView.
Adapter class:
class Video_adapter(var videolist: ArrayList<String>):RecyclerView.Adapter<Video_adapter.ViewHolder>(){
class ViewHolder(view: CardView):RecyclerView.ViewHolder(view){
val video:VideoView = view.findViewById(R.id.video)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.video_view,parent,false) as CardView
Log.i("OnCreateView","OnCreateViewHolder is working")
return ViewHolder(view)
}
override fun getItemCount(): Int {
return videolist.size
Log.i("Video_list1",videolist.size.toString())
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
var video = videolist[position]
holder.video.setVideoURI(video.toUri())
holder.video.setOnTouchListener( View.OnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN){
holder.video.start()
}
true
})
}
}
This is my main kotlin file
class MainActivity : AppCompatActivity() {
lateinit var toolbar: androidx.appcompat.widget.Toolbar
lateinit var recyclerView: RecyclerView
lateinit var layoutManager: RecyclerView.LayoutManager
lateinit var recycleradpter:Video_adapter
lateinit var videolist: ArrayList<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
toolbar = findViewById(R.id.toolbar)
recyclerView = findViewById(R.id.recycler_view)
layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
videolist = ArrayList()
getJson()
Log.i("Video_list",videolist.size.toString())
recycleradpter = Video_adapter(videolist)
Log.i("Video_list",videolist.size.toString())
recyclerView.adapter = recycleradpter
}
fun getJson(){
val url = "https://raw.githubusercontent.com/bikashthapa01/myvideos-android-app/master/data.json"
val queue = Volley.newRequestQueue(this)
//volley request
val request = JsonObjectRequest(Request.Method.GET,url,null,Response.Listener<JSONObject>{ response ->
try {
val categories = response.getJSONArray("categories")
for (i in 0 until categories.length()){
val category = categories.getJSONObject(i)
val videos = category.getJSONArray("videos")
for (i in 0 until videos.length()){
val video = videos.getJSONObject(i)
val source = video.getString("sources")
videolist.add(source)
Log.i("Url",source)
Toast.makeText(this,"Json request working",Toast.LENGTH_LONG).show()
}
}
}
catch (e:JSONException){
Log.i("error ",e.toString())
}
},Response.ErrorListener {error ->
Log.i("JSon_request_error",error.toString())
})
Log.i("Video_list",videolist.size.toString())
queue.add(request)
}
}
I am not able to find what is wrong with the code why it is not inflating the recycler view.
After the for loop in getJson method call
recycleradpter.notifyDataSetChanged() to refresh the RecyclerView.

Save/Restore Fragment state with RecyclerView in Android

I have created an Android app, which connects to GitHub API and shows a list of repositories of certain user. List is shown by recyclerView. I'm trying to save and restore a list, when I'm switching fragments. I have tried to save a list to a variable and add this to recyclerView. But it doesn't work.
My Fragment:
class MainFragment : Fragment() {
lateinit var recyclerView: RecyclerView
var responseSave:List<GitHubPOJO> = ArrayList()
var posts: MutableList<GitHubPOJO> = ArrayList()
lateinit var btn:Button
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_main, container, false)
btn = view.findViewById(R.id.button)
btn.setOnClickListener {
posts = ArrayList()
val name:String = view!!.findViewById<EditText>(R.id.editText).text.toString()
recyclerView = view!!.findViewById(R.id.posts_recycle_view)
val layoutManager = LinearLayoutManager(this.activity!!)
recyclerView.layoutManager = layoutManager
val adapter = PostsAdapter(posts)
recyclerView.adapter = adapter
//HIDE KEYBOARD
val inputMethodManager = this.activity!!.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
if(activity!!.currentFocus !=null) {
inputMethodManager.hideSoftInputFromWindow(this.activity!!.currentFocus!!.windowToken, 0)
}
val service = Retrofit.Builder()
.baseUrl("https://api.github.com/") // CHANGE API
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(GitHubService::class.java)
service.retrieveRepositories(name)
.enqueue(object : Callback<List<GitHubPOJO>> {
override fun onResponse(call: Call<List<GitHubPOJO>>, response: Response<List<GitHubPOJO>>) {
responseSave = response.body()!!
posts.addAll(responseSave)
response.body()?.forEach { println ("TAG_: $it")}
recyclerView.adapter?.notifyDataSetChanged()
}
override fun onFailure(call: Call<List<GitHubPOJO>>, t: Throwable) {
//Toast.makeText(this#MainFragment, "Error occurred while networking", Toast.LENGTH_SHORT).show()
}
})
recyclerView.addOnItemTouchListener(
ClickListener(this.activity!!, recyclerView, object : ClickListener.OnItemClickListener {
override fun onLongItemClick(view: View?, position: Int) {
}
override fun onItemClick(view: View, position: Int) {
val url = posts!![position].htmlUrl
println("URL = $url")
view.findNavController().navigate(MainFragmentDirections.actionMainFragmentToWebFragment(url))
}
})
)
}
return view
}
And there is my code onResume:
override fun onResume() {
super.onResume()
val adapter = PostsAdapter()
adapter.updateAdapterList(responseSave.toMutableList())
println("RESUME")
println(responseSave)
}
When I print responseSave I see that my list is there. But it doesn't appears in RecyclerView.
Fragments are swithced by standart navigation library.
Activity code:
class MainActivity : AppCompatActivity(){
private lateinit var navController: NavController
private lateinit var mNavView:NavigationView
private lateinit var mDrawerLayout:DrawerLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mDrawerLayout = findViewById(R.id.drawer_layout)
mNavView = findViewById(R.id.nav_view)
navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController,mDrawerLayout)
NavigationUI.setupWithNavController(mNavView, navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return NavigationUI.navigateUp(mDrawerLayout,navController)
}}
My RecyclerView Adapter code:
class PostsAdapter(private var posts: MutableList<GitHubPOJO>? = ArrayList()) : RecyclerView.Adapter<PostsAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.post_item, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = posts!![position]
holder.post.text = post.name
holder.site.text = post.fullName // Change what you wanna see
}
fun updateAdapterList(newList: MutableList<GitHubPOJO>) {
posts!!.clear()
posts!!.addAll(newList)
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return posts?.size ?: 0
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var post: TextView = itemView.findViewById<View>(R.id.postitem_post) as TextView
var site: TextView = itemView.findViewById<View>(R.id.postitem_site) as TextView
}}
Add a method in your adapter thats something like;
fun updateAdapterList(newList: MutableList<GitHubPOJO>) {
oldList.clear()
oldList.addAll(newList)
notifyDataSetChanged()
}
Then call this in your onResume() and pass in your updated list
The problem is on your onResume method. You are not adding an Adapter and a LayoutManager to the RecylerView so it can't display the data. Try moving the RecylerView initalization code from the onClickListener to your onResume method:
recyclerView = view!!.findViewById(R.id.posts_recycle_view)
val layoutManager = LinearLayoutManager(this.activity!!)
recyclerView.layoutManager = layoutManager
val adapter = PostsAdapter(posts)
recyclerView.adapter = adapter
Although having this initialization code in your onCreateView method would be better since it's the first lifecycle method called when a fragment returns to the front view from the backstack.

Categories

Resources