Arraylist loop unexpectedly on kotlin recycleview - android

Here is when the list increase
I call data from API and place it in recycleview horizontal and vertical, but when I try to move on Profile Menu with bottom navigation and back again on Home Menu where my horizontal and vertical recycleview is, the data on recycleview start to increase, it only happen for vertical recycleview
Here is my code:
SectionPagerAdapter for vertical recycleview
class SectionsPagerAdapter(fm: FragmentManager) :
FragmentPagerAdapter(
fm
) {
var new_craft: ArrayList<Data>? = ArrayList()
var popular: ArrayList<Data>? = ArrayList()
var recommended: ArrayList<Data>? = ArrayList()
override fun getItem(position: Int): Fragment {
var fragment : Fragment
return when (position) {
0 -> {
fragment = HomeNewCraftFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", new_craft)
fragment.setArguments(bundle)
return fragment
}
1 -> {
fragment = HomePopularFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", popular)
fragment.setArguments(bundle)
return fragment
}
2 -> {
fragment = HomeRecommendedFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", recommended)
fragment.setArguments(bundle)
return fragment
}
else -> {
fragment = HomeNewCraftFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", new_craft)
fragment.setArguments(bundle)
return fragment
}
}
}
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> "New Craft"
1 -> "Popular"
2 -> "Recommended"
else -> null
}
}
override fun getCount(): Int {
return 3
}
fun setData(newStateListParms: ArrayList<Data>?, popularListParms: ArrayList<Data>?, recomendedListParms: ArrayList<Data>?) {
new_craft = newStateListParms
popular = popularListParms
recommended = recomendedListParms
}
}
Home Fragment:
class HomeFragment : Fragment(), HomeAdapter.ItemAdapterCallback, HomeContract.View {
private var adapter: HomeAdapter? = null
var progressDialog: Dialog? = null
private var newStateList: ArrayList<Data>? = ArrayList()
private var popularList: ArrayList<Data>? = ArrayList()
private var recomendedList: ArrayList<Data>? = ArrayList()
private lateinit var presenter: HomePresenter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initView()
presenter = HomePresenter(this)
presenter.getHome()
}
private fun initView() {
progressDialog = Dialog(requireContext())
val dialogLayout = layoutInflater.inflate(R.layout.dialog_loader, null)
progressDialog?.let {
it.setContentView(dialogLayout)
it.setCancelable(false)
it.window?.setBackgroundDrawableResource(android.R.color.transparent)
}
var user = IcaCraft.getApp().getUser()
var userResponse = Gson().fromJson(user, User::class.java)
if (!userResponse.profile_photo_url.isNullOrEmpty()) {
Glide.with(requireActivity())
.load(userResponse.profile_photo_url)
.into(ivProfile)
}
}
override fun onClick(v: View?, data: Data) {
var bundle = Bundle()
bundle.putParcelable("data", data)
val detail = Intent(activity, DetailActivity::class.java).putExtras(bundle)
startActivity(detail)
}
override fun onHomeSuccess(homeResponse: HomeResponse) {
for (a in homeResponse.data.indices) {
var items: List<String> = homeResponse.data[a].types?.split(",") ?: ArrayList()
for (x in items.indices)
{
if (items[x].equals("new_craft", true)) {
newStateList?.add(homeResponse.data[a])
} else if (items[x].equals("recommended", true)) {
recomendedList?.add(homeResponse.data[a])
} else if (items[x].equals("popular", true)) {
popularList?.add(homeResponse.data[a])
}
}
}
adapter = HomeAdapter(homeResponse.data, this)
val layoutManager: RecyclerView.LayoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
rvList.layoutManager = layoutManager
rvList.adapter = adapter
val sectionsPagerAdapter = SectionsPagerAdapter(
childFragmentManager
)
sectionsPagerAdapter.setData(newStateList, popularList, recomendedList)
viewPager.adapter = sectionsPagerAdapter
tabLayout.setupWithViewPager(viewPager)
}
override fun onHomeFailed(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
override fun showLoading() {
progressDialog?.show()
}
override fun dismissLoading() {
progressDialog?.dismiss()
}
}
HomeCraftAdapter: adapter for vertical recycleview
class HomeNewCraftAdapter (
private val listData: ArrayList<Data>,
private val itemAdapterCallback: ItemAdapterCallback
) : RecyclerView.Adapter<HomeNewCraftAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.item_home_vertical, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(listData[position], itemAdapterCallback)
}
override fun getItemCount(): Int {
return listData.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(data : Data, itemAdapterCallback : ItemAdapterCallback) {
itemView.apply {
tvTitle.text = data.name
tvPrice.formatPrice(data?.price.toString())
rbCraft.rating = data.rate?.toFloat() ?: 0f
Glide.with(context)
.load(data.picturePath)
.into(ivPoster)
itemView.setOnClickListener { view -> itemAdapterCallback.onClick(view, data) }
}
}
}
interface ItemAdapterCallback {
fun onClick(v: View?, data:Data)
}
}
And HomeNewCraftFragment : for vertical recycleview fragment
class HomeNewCraftFragment : Fragment() , HomeNewCraftAdapter.ItemAdapterCallback {
private var adapter: HomeNewCraftAdapter? = null
private var newcraftlist: ArrayList<Data>? = ArrayList()
private var test: String? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home_new_craft, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
newcraftlist = arguments?.getParcelableArrayList("data")
adapter = HomeNewCraftAdapter(newcraftlist!!, this)
val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(activity)
rcListNewCraft.layoutManager = layoutManager
rcListNewCraft.adapter = adapter
}
override fun onClick(v: View?, data: Data) {
val detail = Intent(activity, DetailActivity::class.java).putExtra("data", data)
startActivity(detail)
}
}

When you leave from your HomeFragment and coming back again, your HomeFragment keeps the old newStateList and add same values in it again. So, you need to empty the values before adding new values.
override fun onHomeSuccess(homeResponse: HomeResponse) {
newStateList = ArrayList() // empty it before load it again.
for (a in homeResponse.data.indices) {
You might need to empty the recomendedList and popularList as well.

Related

java.lang.IllegalStateException: Fragment already added: InprogressFragment

When I click on order menu the app suddenly stopped working, and on the log show this error
image, it says java.lang.IllegalStateException: Fragment already added: InprogressFragment
I have 2 section pager on order menu, it's for inprogress & pastOrders.
here is the code
inProgressFragment:
class InprogressFragment : Fragment(), InprogressAdapter.ItemAdapterCallback {
private var adapter: InprogressAdapter? = null
private var inprogressList: ArrayList<Data>? = ArrayList()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_inprogress, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
inprogressList = arguments?.getParcelableArrayList("data")
if (!inprogressList.isNullOrEmpty()) {
adapter = InprogressAdapter(inprogressList!!, this)
val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(activity)
rvList.layoutManager = layoutManager
rvList.adapter = adapter
}
}
override fun onClick(v: View?, data: Data) {
val detail = Intent(activity, OrdersDetailActivity::class.java).putExtra("data", data)
startActivity(detail)
}
}
OrderFragment:
class OrderFragment : Fragment(), OrderContract.View{
lateinit var presenter: OrderPresenter
var progressDialog: Dialog? = null
private var inprogressList: ArrayList<Data>? = ArrayList()
private var pastordersList: ArrayList<Data>? = ArrayList()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_order, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initView()
presenter = OrderPresenter(this)
presenter.getTransaction()
}
override fun onTransactionSuccess(transactionResponse: TransactionResponse) {
Log.v("tamvan", "ini token "+IcaCraft.getApp().getToken())
if (transactionResponse.data.isNullOrEmpty()) {
include_toolbar.visibility = View.GONE
ll_tab.visibility = View.GONE
ll_empty.visibility = View.VISIBLE
} else {
for (a in transactionResponse.data.indices) {
// past order
if (transactionResponse.data[a].status.equals("ON_DELIVERY", true)
|| transactionResponse.data[a].status.equals("PENDING", true)) {
inprogressList?.add(transactionResponse.data[a])
// progress
} else if (transactionResponse.data[a].status.equals("DELIVERY", true)
|| transactionResponse.data[a].status.equals("DELIVERED", true)
|| transactionResponse.data[a].status.equals("CANCELLED", true)
|| transactionResponse.data[a].status.equals("SUCCESS", true)) {
pastordersList?.add(transactionResponse.data[a])
}
}
if (inprogressList.isNullOrEmpty() && pastordersList.isNullOrEmpty()) {
include_toolbar.visibility = View.GONE
ll_tab.visibility = View.GONE
ll_empty.visibility = View.VISIBLE
} else {
val sectionsPagerAdapter = SectionsPagerAdapter(
childFragmentManager
)
sectionsPagerAdapter.setData(inprogressList, pastordersList)
//viewPager!!.offscreenPageLimit = 3
viewPager.adapter = sectionsPagerAdapter
tabLayout.setupWithViewPager(viewPager)
}
}
}
override fun onTransactionFailed(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
private fun initView() {
progressDialog = Dialog(requireContext())
val dialogLayout = layoutInflater.inflate(R.layout.dialog_loader, null)
progressDialog?.let {
it.setContentView(dialogLayout)
it.setCancelable(false)
it.window?.setBackgroundDrawableResource(android.R.color.transparent)
}
include_toolbar.toolbar.title = "Your Orders"
include_toolbar.toolbar.subtitle = "Wait for the best meal"
}
override fun showLoading() {
progressDialog?.show()
}
override fun dismissLoading() {
progressDialog?.dismiss()
}
}
SectionPagerAdapter:
class SectionsPagerAdapter(fm: FragmentManager) :
FragmentPagerAdapter(
fm
) {
var inprogressList: ArrayList<Data>? = ArrayList()
var pastOrdersList: ArrayList<Data>? = ArrayList()
override fun getItem(position: Int): Fragment {
var fragment : Fragment
return when (position) {
0 -> {
fragment = InprogressFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", inprogressList)
fragment.setArguments(bundle)
return fragment
}
1 -> {
fragment = PastordersFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", pastOrdersList)
fragment.setArguments(bundle)
return fragment
}
else -> {
fragment = InprogressFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", inprogressList)
fragment.setArguments(bundle)
return fragment
}
}
}
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> "In Progress"
1 -> "Past Orders"
else -> null
}
}
override fun getCount(): Int {
return 2
}
fun setData(inprogressListParms: ArrayList<Data>?, pastOrdersListParms: ArrayList<Data>?) {
inprogressList = inprogressListParms
pastOrdersList = pastOrdersListParms
}
}
Try something like this(I didn't test this code, it's just a concept for your app):
enum class FragmentType {
IN_PROGRESS,
POSTORDERS
}
data class Data(
val fragmentType: FragmentType,
val fragmentName: String,
val orderList: ArrayList<Data>?
)
class SectionsPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
var inprogressList: ArrayList<Data> = ArrayList()
override fun getItem(position: Int): Fragment {
val item = inprogressList[position]
return when (item.fragmentType) {
FragmentType.IN_PROGRESS -> {
InprogressFragment().apply {
val bundle = Bundle()
bundle.putParcelableArrayList("data", item.orderList)
setArguments(bundle)
}
}
FragmentType.POSTORDERS -> {
PastordersFragment().apply {
val bundle = Bundle()
bundle.putParcelableArrayList("data", item.orderList)
setArguments(bundle)
}
}
}
}
override fun getPageTitle(position: Int): CharSequence {
return inprogressList[position].fragmentName
}
override fun getCount(): Int {
return inprogressList.size
}
fun setData(items: ArrayList<Data>) {
inprogressList = items
notifyDataSetChanged()
}
}

Fragment screen switching when clicking recycler view item

Fragment1 exists in MainActivity and Fragment2 exists in Fragment1.
Fragment2 contains a recycler view, and inside the recycler view there are items with a move button. When I click the go button, I want to switch to a screen other than the RcyclerView with the items currently displayed in Fragment2.
adpater
class Quality_Material_Certification(
val material_name : String,
val use_type : String,
val standard : String,
val material_num : String
)
class QualityMaterialCertificationAdapter(private val dataset:
List<Quality_Material_Certification>) :RecyclerView.Adapter<QualityMaterialCertificationAdapter.QualityMaterialCertificationViewHolder>() {
class QualityMaterialCertificationViewHolder(val binding:
ItemQualityMaterialCertificationBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int):
QualityMaterialCertificationViewHolder {
val view =LayoutInflater.from(viewGroup.context).inflate(R.layout.item_quality_material_certification, viewGroup, false)
return QualityMaterialCertificationViewHolder(ItemQualityMaterialCertificationBinding.bind(view))
}
override fun onBindViewHolder(viewHolder: QualityMaterialCertificationViewHolder, position: Int) {
val listposition = dataset[position]
viewHolder.binding.materialName.text= listposition.material_name
viewHolder.binding.useType.text = listposition.use_type
viewHolder.binding.standard.text = listposition.standard
viewHolder.binding.goToMoreBtn.setOnClickListener {
setDataAtFragment(Fragment2(), listposition.material_num)
}
}
override fun getItemCount() = dataset.size
private fun setDataAtFragment(fragment: Fragment, material : String)
{
val bundle = Bundle()
val transaction = binding.root.supportFragmentManager?.beginTransaction()
bundle.putString("material", material)
fragment.arguments = bundle
transaction?.replace(R.id.appro_fl, fragment)?.commit()
}
}
fragment1
private var token : String? = null
private var const_code : String? = null
private var list = arrayListOf<Quality_Material_Certification>()
class Fragment1 : Fragment() {
private lateinit var binding:
FragmentQualityManagementFirefightingMaterialsCertificationBinding
var material: GetApproMaterListDTO? = null
lateinit var data: Quality_Material_Certification
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = FragmentQualityManagementFirefightingMaterialsCertificationBinding.inflate(layoutInflater)
binding.materialRecycler.layoutManager = LinearLayoutManager(context)
binding.materialRecycler.adapter = QualityMaterialCertificationAdapter(list)
arguments?.let {
token = it.getString("token").toString()
const_code = it.getString("cons_code").toString()
}
val dto = RequestDTO( "0", "9")
retrofit.requestservice(sysCd, token.toString(), dto).enqueue(object :
Callback<GetApproMaterListDTO> {
override fun onFailure(call: Call<GetApproMaterListDTO>, t: Throwable) { Log.d("retrofit", t.toString()) }
#SuppressLint("NotifyDataSetChanged")
override fun onResponse(call: Call<GetApproMaterListDTO>, response: Response<GetApproMaterListDTO>) {
material = response.body()
list.clear()
for (i in 0 until material?.value?.list!!.size) {
val material_name = material?.value?.list?.get(i)?.material_name.toString()
val use_type = material?.value?.list?.get(i)?.use_type.toString()
val standard = material?.value?.list?.get(i)?.standard.toString()
val material_num = material?.value?.list?.get(i)?.material_num.toString()
data = Quality_Material_Certification(material_name, use_type, standard ,material_num )
list.add(data)
}
binding.materialRecycler.adapter?.notifyDataSetChanged()
}
})
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return binding.root
}
companion object {
#JvmStatic
fun newInstance(param1: String, param2: String) =
Fragment1().apply {
arguments = Bundle().apply {
}
}
}
}
modified comment opinion
apdapter
interface clicktoDetail{
fun onClickDetail(model : CertificationDTO)
}
class CertificationAdapter(
private val context: Context,
private val dataset: List<CertificationDTO>,
private val clickListener: clicktoDetail)
: RecyclerView.Adapter<QualityMaterialCertificationAdapter.ChangeHolder>() {
inner class ChangeHolder(val binding: ItemCertificationBinding) : RecyclerView.ViewHolder(binding.root){
//val btn :Button = button.findViewById(R.id.go_to_more_btn)
init{
binding.goToMoreBtn.setOnClickListener { clickListener.onClickDetail(dataset[position]) }
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ChangeHolder {
val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.item_certification, viewGroup, false)
return ChangeHolder(ItemCertificationBinding.bind(view))
}
override fun onBindViewHolder(viewHolder: ChangeHolder, position: Int) {
val listposition = dataset[position]
viewHolder.binding.materialName.text= listposition.material_name
viewHolder.binding.useType.text = listposition.use_type
viewHolder.binding.standard.text = listposition.standard
viewHolder.binding.madeCompanyName.text = listposition.produce_co
/* viewHolder.binding.goToMoreBtn.setOnClickListener {
val intent = Intent(context, Fragment1::class.java)
intent.putExtra("cons_code", listposition.project_const)
intent.run{
context.startActivity(this)
(context as Activity).finish()
}
}*/
}
override fun getItemCount() = dataset.size
}
Fragment
private var token : String? = null
private var const_code : String? = null
private var material_list = arrayListOf<Quality_Material_CertificationDTO>()
class Certification_Fragment : Fragment() {
private lateinit var binding: FragmentQualityManagementFirefightingMaterialsCertificationBinding
var material: GetListDTO? = null
lateinit var material_data : Quality_Material_CertificationDTO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = FragmentCertificationBinding.inflate(layoutInflater)
val retrofitApproMaterList = RetrofitC("http://")
val GetApproMaterListService: GetApproMaterListService = retrofitApproMaterList.create(GetListService::class.java)
binding.materialRecycler.layoutManager = LinearLayoutManager(context)
binding.materialRecycler.adapter = CertificationAdapter(requireContext(), material_list, GotoDetail())
arguments?.let {
token = it.getString("token").toString()
const_code = it.getString("cons_code").toString()
}
val mdto = RequestDTO("", "")
GetApproMaterListService.requestappromaterial(sysCd, token, mdto).enqueue(object :
Callback<GetListDTO> {
override fun onFailure(call: Call<GetListDTO>, t: Throwable) { Log.d("retrofit", t.toString()) }
#SuppressLint("NotifyDataSetChanged")
override fun onResponse(call: Call<GetAListDTO>, response: Response<GetListDTO>) {
material = response.body()
material_list.clear()
for (i in 0 until material?.value?.list!!.size) {
val material_name = material?.value?.list?.get(i)?.material_name.toString()
val use_type = material?.value?.list?.get(i)?.use_type.toString()
val standard = material?.value?.list?.get(i)?.standard.toString()
val produce_co = material?.value?.list?.get(i)?.produce_co.toString()
val material_num = material?.value?.list?.get(i)?.material_num.toString()
material_data = Quality_Material_CertificationDTO(material_name, use_type, standard, produce_co ,material_num)
material_list.add(material_data)
CertificationAdapter(requireContext(), material_list, GotoDetail())
}
binding.materialRecycler.adapter?.notifyDataSetChanged()
}
})
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return binding.root
}
companion object {
#JvmStatic
fun newInstance(param1: String, param2: String) =
Quality_Management_Firefighting_Materials_Certification_Fragment().apply {
arguments = Bundle().apply {
}
}
}
class GotoDetail : Fragment(), clicktoDetail{
override fun onClickDetail(model: Quality_Material_CertificationDTO) {
val bundle = Bundle()
val transaction = activity?.supportFragmentManager?.beginTransaction()
bundle.putString("cons_code", const_code)
bundle.putString("token", token)
Fragment().arguments = bundle
transaction?.replace(R.id.appro_fl, Certification_More_Fragment())!!.commit()
}
}
}
You are trying to access activities and fragments inside your adapter class which is incorrect. Never do that! Instead, create an item click listener and create a reference to it in your activity. This way you can handle item click in your activity and perform fragment transactions.
Here, You can take help on how to do so by making an onClickInterface

java.lang.IllegalArgumentException: Required argument "data" is missing and does not have an android:defaultValue

I try to pass data from API on recycleView on Home Activity to new activity and fragment, but I have a problem like this
java.lang.IllegalArgumentException: Required argument "data" is missing and does not have an android:defaultValue
at com.project.icacraft.ui.detail.DetailFragmentArgs$Companion.fromBundle(DetailFragmentArgs.kt:60)
at com.project.icacraft.ui.detail.DetailFragment.onActivityCreated(DetailFragment.kt:35)
I have 2 recycleView, horizontal and vertical, here is my code for HomeAdapter:
class HomeAdapter (
private val listData: List<Data>,
private val itemAdapterCallback: ItemAdapterCallback
) : RecyclerView.Adapter<HomeAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.item_home_horizontal, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(listData[position], itemAdapterCallback)
}
override fun getItemCount(): Int {
return listData.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(data: Data, itemAdapterCallback: ItemAdapterCallback) {
itemView.apply {
tvTitle.text = data.name
rbcraft.rating = data.rate?.toFloat()?:0f
Glide.with(context)
.load(data.picturePath)
.into(ivPoster)
itemView.setOnClickListener { view -> itemAdapterCallback.onClick(view, data) }
}
}
}
interface ItemAdapterCallback {
fun onClick(v: View?, data: Data)
}
}
HomePresenter:
class HomePresenter (private val view:HomeContract.View) : HomeContract.Presenter{
private val mCompositeDisposable : CompositeDisposable?
init {
this.mCompositeDisposable = CompositeDisposable()
}
override fun getHome() {
view.showLoading()
val disposable = HttpClient.getInstance().getAPI()!!.home()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
view.dismissLoading()
if (it.meta?.status.equals("success")){
it.data?.let { data -> view.onHomeSuccess(data) }
} else {
view.onHomeFailed(it.meta?.message.toString())
}
},
{
view.dismissLoading()
view.onHomeFailed(it.getErrorBodyMessage())
}
)
mCompositeDisposable!!.add(disposable)
}
override fun subscribe() {}
override fun unSubscribe() {
mCompositeDisposable!!.clear()
}
}
Here is the code for Horizontal Recycleview on home:
class HomeFragment : Fragment(), HomeAdapter.ItemAdapterCallback, HomeContract.View {
private var adapter: HomeAdapter? = null
var progressDialog: Dialog? = null
private var newStateList: ArrayList<Data>? = ArrayList()
private var popularList: ArrayList<Data>? = ArrayList()
private var recomendedList: ArrayList<Data>? = ArrayList()
private lateinit var presenter: HomePresenter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initView()
presenter = HomePresenter(this)
presenter.getHome()
}
private fun initView() {
progressDialog = Dialog(requireContext())
val dialogLayout = layoutInflater.inflate(R.layout.dialog_loader, null)
progressDialog?.let {
it.setContentView(dialogLayout)
it.setCancelable(false)
it.window?.setBackgroundDrawableResource(android.R.color.transparent)
}
//menampilakn foto user
var user = IcaCraft.getApp().getUser()
var userResponse = Gson().fromJson(user, User::class.java)
if (!userResponse.profile_photo_url.isNullOrEmpty()) {
Glide.with(requireActivity())
.load(userResponse.profile_photo_url)
.into(ivProfile)
}
}
override fun onClick(v: View?, data: Data) {
var bundle = Bundle()
bundle.putParcelable("data", data)
val detail = Intent(activity, DetailActivity::class.java).putExtras(bundle)
startActivity(detail)
}
override fun onHomeSuccess(homeResponse: HomeResponse) {
for (a in homeResponse.data.indices) {
var items: List<String> = homeResponse.data[a].types?.split(",") ?: ArrayList()
for (x in items.indices)
{
if (items[x].equals("new_craft", true)) {
newStateList?.add(homeResponse.data[a])
} else if (items[x].equals("recommended", true)) {
recomendedList?.add(homeResponse.data[a])
} else if (items[x].equals("popular", true)) {
popularList?.add(homeResponse.data[a])
}
}
}
adapter = HomeAdapter(homeResponse.data, this)
val layoutManager: RecyclerView.LayoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
rvList.layoutManager = layoutManager
rvList.adapter = adapter
val sectionsPagerAdapter = SectionsPagerAdapter(
childFragmentManager
)
sectionsPagerAdapter.setData(newStateList, popularList, recomendedList)
// viewPager!!.offscreenPageLimit = 3
viewPager.adapter = sectionsPagerAdapter
tabLayout.setupWithViewPager(viewPager)
}
override fun onHomeFailed(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
override fun showLoading() {
progressDialog?.show()
}
override fun dismissLoading() {
progressDialog?.dismiss()
}
}
Code for SectionPagerAdapter
class SectionsPagerAdapter(fm: FragmentManager) :
FragmentPagerAdapter(
fm
) {
var new_craft: ArrayList<Data>? = ArrayList()
var popular: ArrayList<Data>? = ArrayList()
var recommended: ArrayList<Data>? = ArrayList()
override fun getItem(position: Int): Fragment {
var fragment : Fragment
return when (position) {
0 -> {
fragment = HomeNewCraftFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", new_craft)
fragment.setArguments(bundle)
return fragment
}
1 -> {
fragment = HomePopularFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", popular)
fragment.setArguments(bundle)
return fragment
}
2 -> {
fragment = HomeRecommendedFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", recommended)
fragment.setArguments(bundle)
return fragment
}
else -> {
fragment = HomeNewCraftFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", new_craft)
fragment.setArguments(bundle)
return fragment
}
}
}
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> "New Craft"
1 -> "Popular"
2 -> "Recommended"
else -> null
}
}
override fun getCount(): Int {
return 3
}
fun setData(newStateListParms: ArrayList<Data>?, popularListParms: ArrayList<Data>?, recomendedListParms: ArrayList<Data>?) {
new_craft = newStateListParms
popular = popularListParms
recommended = recomendedListParms
}
}
Here is code for vertical recycleview adapter:
class HomeNewCraftAdapter (
private val listData: ArrayList<Data>,
private val itemAdapterCallback : ItemAdapterCallback
) : RecyclerView.Adapter<HomeNewCraftAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.item_home_vertical, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(listData[position], itemAdapterCallback)
}
override fun getItemCount(): Int {
return listData.size
}
class ViewHolder (itemView:View) : RecyclerView.ViewHolder(itemView){
fun bind(data : Data, itemAdapterCallback: ItemAdapterCallback){
itemView.apply {
tvTitle.text = data.name
tvPrice.formatPrice(data?.price.toString())
rbCraft.rating = data.rate?.toFloat() ?: 0f
Glide.with(context)
.load(data.picturePath)
.into(ivPoster)
itemView.setOnClickListener { view -> itemAdapterCallback.onClick(view, data) }
}
}
}
interface ItemAdapterCallback {
fun onClick(v: View?, data:Data)
}
}
Code for vertical recycleview fragment:
class HomeNewCraftFragment : Fragment(), HomeNewCraftAdapter.ItemAdapterCallback {
private var adapter: HomeNewCraftAdapter? = null
private var newcraftlist: java.util.ArrayList<Data>? = java.util.ArrayList()
private var test: String? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home_new_craft, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
newcraftlist = arguments?.getParcelableArrayList("data")
adapter = HomeNewCraftAdapter(newcraftlist!!, this)
val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(activity)
rcListNewCraft.layoutManager = layoutManager
rcListNewCraft.adapter = adapter
}
override fun onClick(v: View?, data: Data) {
val detail = Intent(activity, DetailActivity::class.java).putExtra("data", data)
startActivity(detail)
}
}
And I want to pass data here, on Detail Activity:
class DetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
intent.extras?.let {
val navController = Navigation.findNavController(findViewById(R.id.detailHostFragment))
val bundle = Bundle()
bundle.putParcelable("data", it.get("data") as Parcelable?)
navController.setGraph(navController.graph, bundle)
}
}
fun toolbarPayment() {
toolbar.visibility = View.VISIBLE
toolbar.title = "Payment"
toolbar.subtitle = "You deserve better meal"
toolbar.navigationIcon = resources.getDrawable(R.drawable.arrow_back)
toolbar.setNavigationOnClickListener { onBackPressed() }
}
fun toolbarDetail() {
toolbar.visibility = View.GONE
}
}
And fragment:
class DetailFragment : Fragment() {
var data:Data?= null
var bundle:Bundle?= null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_detail, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as DetailActivity?)!!.toolbarDetail()
arguments?.let {
DetailFragmentArgs.fromBundle(it).data.let {
initView(it)
}
}
btnOrderNow.setOnClickListener { view ->
Navigation.findNavController(view).navigate(R.id.action_fragmentDetail_to_fragmentPayment, bundle)
}
}
private fun initView(data: Data?) {
bundle = bundleOf("data" to data)
Glide.with(requireContext())
.load(data?.picturePath)
.into(ivPoster)
tvTitle.text = data?.name
tvDescription.text = data?.description
tvMaterials.text = data?.materials
// tvTotal.formatPrice(data?.price.toString())
}
}
and here is the navigation:
<navigation 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"
app:startDestination="#id/fragmentDetail"
android:id="#+id/nav_detail">
<fragment
android:id="#+id/fragmentDetail"
android:name="com.project.icacraft.ui.detail.DetailFragment"
tools:layout="#layout/fragment_detail">
<action
android:id="#+id/action_fragmentDetail_to_fragmentPayment"
app:destination="#id/fragmentPayment" />
<argument
android:name="data"
app:argType="com.project.icacraft.model.response.home.Data"
app:nullable="true" />
</fragment>
Here is xml for activiy_detail:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.detail.DetailActivity">
//Menambahkan toolbar dan detail fragment
<include
android:id="#+id/include_toolbar"
layout="#layout/layout_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<fragment
android:id="#+id/detailHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_detail"
/>
</LinearLayout>
As per the Pass data to the start destination documentation:
Note: when manually calling setGraph() with arguments, you must not use the app:navGraph attribute when creating the NavHostFragment in XML as that will internally call setGraph() without any arguments, resulting in your graph and start destination being created twice.
So you can fix your error by removing the app:navGraph="#navigation/nav_detail" line from your activity_detail.xml file.
You'll also need to change your setGraph call to use the same R.navigation.nav_detail:
navController.setGraph(R.navigation.nav_detail, bundle)

How to open a "Details" Fragment onclick of Recyclerview in Kotlin

I have home fragment and I want it to go to another fragment which will show its "details", which passes the data of the clicked recyclerview item to that "details" fragment.
When I click the article it will move to detail article which passes the data.
As for the code, here's the adapter:
class ArticleAdapter(private val articleList: ArrayList<Article>) :
RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = ItemArticleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val data = articleList[position]
holder.bind(data)
}
class MyViewHolder(private val binding: ItemArticleBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(data: Article) {
Glide.with(binding.root.context)
.load(data.img)
.into(binding.articleImage)
binding.articleTitle.text = data.title
binding.root.setOnClickListener {
val article = Article(
data.title,
data.img,
data.content
)
}
}
}
override fun getItemCount(): Int {
return articleList.size
}
}
Here's my detailFragment
class DetailArticleFragment : Fragment() {
private var _binding: FragmentDetailArticleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDetailArticleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val intent = Intent(binding.root.context, DetailArticleFragment::class.java)
val article = intent.getParcelableExtra<Article>(DETAIL_ARTICLE) as Article
Glide.with(this)
.load(article.img)
.into(_binding!!.articleImage)
_binding!!.articleTitle.text = article.title
_binding!!.articleDescription.text = article.content
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
const val DETAIL_ARTICLE = "detail_article"
}
}
You need to pass click listener interface to adapter, for example:
typealias OnArticleClick = (article: Article) -> Unit
class ArticleAdapter(
private val articleList: ArrayList<Article>
) :
RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() {
var onArticleClick: OnArticleClick? = null
...
binding.root.setOnClickListener {
val article = Article(
data.title,
data.img,
data.content
)
onArticleClick?.invoke(article)
}
...
}
And init onArticleClick in your home fragment with RecyclerView:
adapter.apply {
onArticleClick = {
// show details fragment
}
}

TabLayout jumps to start of its witdth after notifyItemChanged()

I'm building quiz app and all the time I have trouble with notifying RecyclerView about updated item.
DiffUtil didn't work for me, anyway - I can use adapter.notifyItemChanged() but after this call my TabLayout jumps to start of it's width.
I have set onClickListener to my floatingButton to be sure that it's notifyItemChanged() issue.
Am I doing something wrong?
Fragment
class TestFragment : Fragment() {
private lateinit var viewModel: TestViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentTestBinding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_test,
container,
false
)
val args = TestFragmentArgs.fromBundle(requireArguments())
val viewModelFactory = TestViewModelFactory(args.course)
viewModel = ViewModelProvider(this, viewModelFactory).get(TestViewModel::class.java)
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
val adapter = PagerAdapter(ClickListener { questionDatabase: QuestionDatabase, string: String ->
val questionUpdated = questionDatabase.apply { answer = string }
viewModel.updateAnswer(questionDatabase, questionUpdated)
})
binding.viewPager.adapter = adapter
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab: TabLayout.Tab, i: Int ->
tab.text = (i + 1).toString()
}.attach()
viewModel.pytania.observe(viewLifecycleOwner, Observer {
adapter.differ.submitList(it)
})
viewModel.score.observe(viewLifecycleOwner, Observer {
if (it != null) {
findNavController().navigate(
TestFragmentDirections.actionTestFragmentToScoreFragment(it)
)
viewModel.score.value = null
}
})
binding.bttnFinish.setOnClickListener {
adapter.notifyItemChanged(binding.viewPager.currentItem)
}
return binding.root
}
}
ViewModel
class TestViewModel(
test: String
): ViewModel() {
private val _pytania = MutableLiveData<List<QuestionDatabase>>()
val pytania: LiveData<List<QuestionDatabase>>
get() = _pytania
val score = MutableLiveData<Int?>()
init {
val listOfQuestions = mutableListOf<QuestionDatabase>()
for (x in 1..15) {
listOfQuestions.add(
QuestionDatabase(
question = "Question $x",
answerA = "AnswerA",
answerB = "AnswerB",
answerC = "AnswerC",
answerD = "AnswerD",
correctAnswer = "AnswerD",
image = 0,
questionNumber = 0
)
)
}
_pytania.value = listOfQuestions.toList()
}
fun updateAnswer(questionDatabase: QuestionDatabase, questionUpdated: QuestionDatabase) {
_pytania.value = _pytania.value?.replace(questionDatabase, questionUpdated)
}
}
ViewPagerAdapter
class PagerAdapter(
private val clickListener: ClickListener
) : RecyclerView.Adapter<ViewHolder>() {
private val differCallBack = object : DiffUtil.ItemCallback<QuestionDatabase>() {
override fun areItemsTheSame(oldItem: QuestionDatabase, newItem: QuestionDatabase): Boolean {
return oldItem.question == newItem.question
}
override fun areContentsTheSame(oldItem: QuestionDatabase, newItem: QuestionDatabase): Boolean {
return oldItem == newItem
}
}
val differ = AsyncListDiffer(this, differCallBack)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(differ.currentList[position], clickListener)
}
override fun getItemCount(): Int {
return differ.currentList.size
}
}
class ViewHolder private constructor(val binding: VpItemQuestionBinding): RecyclerView.ViewHolder(
binding.root
) {
fun bind(
question: QuestionDatabase,
clickListener: ClickListener
) {
binding.question = question
binding.onClickListener = clickListener
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = VpItemQuestionBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}
class ClickListener(val clickListener: (QuestionDatabase, string: String) -> Unit) {
fun onClick(question: QuestionDatabase, string: String) = clickListener(question, string)
}
Before notifyItemChanged()
After notifyItemChanged()

Categories

Resources