Fragment screen switching when clicking recycler view item - android

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

Related

Arraylist loop unexpectedly on kotlin recycleview

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.

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
}
}

NavArgs returning a RuntimeException

NavArgs not returning a value, I just get this when I try to update through the setOnClickListener in update fragment:
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
Caused by: java.lang.IllegalArgumentException: Required argument "currentAlarm" is missing and does not have an android:defaultValue
I need to return the ID from the alarm selected in the RecyclerView, so a default value wouldn't make sense here.
The NavArgs are set in the nav-view correctly, with no default. Could the model and ID be set up wrong with autoGenerate? or is going via the adapter the problem here?
update fragment:
class UpdateFragment : Fragment() {
private val timePickerUtil = TimePickerUtil()
lateinit var binding: FragmentUpdateBinding
private lateinit var alarmViewModel: AlarmViewModel
private val args by navArgs<UpdateFragmentArgs>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentUpdateBinding.inflate(inflater, container, false)
alarmViewModel = ViewModelProvider(this)[AlarmViewModel::class.java]
binding.fragmentBtnUpdateAlarm.setOnClickListener {
updateAlarm()
Navigation.findNavController(requireView())
.navigate(R.id.action_updateFragment_to_homeFragment)
}
return binding.root
}
private fun updateDatabase(id: Int, hour: Int, minute: Int, repeat: Boolean) {
val alarm = Alarm(id, hour, minute, repeat)
alarmViewModel.update(alarm)
}
private fun updateAlarm() {
val timePicker = binding.fragmentUpdateAlarmTimePicker
val id = args.currentAlarm.id
val hour = timePickerUtil.getTimePickerHour(timePicker)
val minute = timePickerUtil.getTimePickerMinute(timePicker)
val repeat = binding.fragmentUpdateAlarmRecurring.isChecked
val alarmManager = AlarmManager(
id,
hour,
minute,
true,
binding.fragmentUpdateAlarmRecurring.isChecked
)
updateDatabase(id, hour, minute, repeat)
alarmManager.cancel(requireContext())
alarmManager.schedule(requireContext())
}
}
adapter:
class AlarmListAdapter() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class MyViewHolder(binding: LayoutAlarmBinding) : RecyclerView.ViewHolder(binding.root)
private var alarmList = ArrayList<Alarm>()
private var onItemClickListener: OnItemClickListener? = null
interface OnItemClickListener{
fun onClick(alarm: Alarm)
fun onLongClick(alarm: Alarm)
}
fun setOnItemClickListener(listener: OnItemClickListener){
onItemClickListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding = LayoutAlarmBinding.inflate(LayoutInflater.from(parent.context))
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val currentItem = alarmList[position]
val minute = currentItem.minute
holder.itemView.findViewById<TextView>(R.id.tv_alarm_time).text =
if (minute >= 10) {
"${currentItem.hour}:${currentItem.minute}"
} else {
"${currentItem.hour}:0${currentItem.minute}"
}
holder.itemView.setOnClickListener{
if(onItemClickListener != null){
onItemClickListener?.onClick(currentItem)
}
}
holder.itemView.setOnLongClickListener {
if(onItemClickListener != null){
onItemClickListener?.onLongClick(currentItem)
}
true
}
}
override fun getItemCount(): Int {
return alarmList.size
}
fun setData(alarm: List<Alarm>) {
alarmList.clear()
alarmList.addAll(alarm)
notifyDataSetChanged()
}
}
HomeFragment:
class HomeFragment : Fragment() {
lateinit var binding: FragmentHomeBinding
private lateinit var alarmViewModel: AlarmViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentHomeBinding.inflate(inflater, container, false)
// RecyclerView
val adapter = AlarmListAdapter()
val recyclerView = binding.recyclerView
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
//ViewModel
alarmViewModel = ViewModelProvider(this).get(AlarmViewModel::class.java)
alarmViewModel.readAlarmData.observe(viewLifecycleOwner, Observer { alarm ->
adapter.setData(alarm)
})
binding.btnAddAlarm.setOnClickListener {
Navigation.findNavController(requireView())
.navigate(R.id.action_homeFragment_to_newAlarmFragment)
}
adapter.setOnItemClickListener(object : AlarmListAdapter.OnItemClickListener {
override fun onClick(alarm: Alarm) {
Navigation.findNavController(requireView())
.navigate(R.id.action_homeFragment_to_updateFragment)
}
override fun onLongClick(alarm: Alarm) {
val deleteBuilder = AlertDialog.Builder(requireContext())
deleteBuilder.setPositiveButton("Delete") { _, _ ->
alarmViewModel.delete(alarm)
Toast.makeText(context, "Alarm Deleted", Toast.LENGTH_SHORT)
.show()
}
deleteBuilder.setNegativeButton("Cancel") { _, _ ->
}
deleteBuilder.setTitle("Delete Alarm?")
deleteBuilder.create().show()
}
})
return binding.root
}
}
and my alarm model:
#Parcelize
#Entity(tableName = "alarm_table")
data class Alarm(
#PrimaryKey(autoGenerate = true)
val id: Int,
val hour: Int,
val minute: Int,
val repeat: Boolean
): Parcelable
Safe args creates its own classes (like here HomeFragmentDirections). So in homeFragment, where you are setting up onclick listeners for the adapter, you should use->
adapter.setOnItemClickListener(object : AlarmListAdapter.OnItemClickListener {
override fun onClick(alarm: Alarm) {
Navigation.findNavController(requireView())
.navigate(HomeFragmentDirections.actionHomeFragmentToUpdateFragment(params))
}
override fun onLongClick(alarm: Alarm) {
// preform longclick action here
}
})
And add a param (in your case, id may be of type int or String) of the required type in UpdateFragment in the corresponding navgraph.
I hope you got my point and if not don't hesitate to comment.
You can read about this on the android developer website here.

I put the search function in the recyclerView. Items are not only seen as new data, but new data seems to be filled up a little on old data. in kotlin

Pressing the search button in Main Activity changes the values of the items in RecyclerView.
The problem is that the existing data remains except for the newly retrieved data.
How can I show you only the new values on the list?
--- MainActivity.kt ----
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var toolbar: Toolbar
private var logTag : String? = "로그 MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
Log.d(logTag,"onCreate is called")
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
toolbar = binding.searchToolbar
setContentView(view)
setSupportActionBar(toolbar)
val navView: BottomNavigationView = binding.navView
val navController : NavController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.A, R.id.B, R.id.C
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
binding.btnSearch.setOnClickListener {
val recordFragment = RecordFragment()
val bundle = Bundle()
if ( binding.etSearch.text.toString() ==""|| binding.etSearch.text.isEmpty()) {
bundle.putString("parameter", "A")
}else {
bundle.putString("parameter", binding.etSearch.text.toString())
}
recordFragment.arguments = bundle
this.supportFragmentManager.beginTransaction().replace(R.id.nav_host_fragment, recordFragment).commit()
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.record_app_bar, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.item_search -> {
when (binding.loSearch.visibility) {
GONE -> binding.loSearch.visibility = VISIBLE
VISIBLE -> binding.loSearch.visibility = GONE
else -> {
Log.d(logTag, "onOptionsItemSelected is called // error")
}
}
}
}
return super.onOptionsItemSelected(item)
}
}
class RecordFragment : Fragment() {
private var rRecyclerView: RecyclerView? = null
private val rApi: ApiInterface? = HttpClient.getRetrofit()?.create(ApiInterface::class.java)
private var EventName : String? = null
var logTag : String? = "로그 RecordFragment"
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_record, container, false)
EventName = arguments?.getString("parameter")
if (EventName == null||EventName =="") {
EventName = ""
}
rRecyclerView = root.findViewById(R.id.recordRecyclerView)
rRecyclerView?.layoutManager = LinearLayoutManager(context)
val rAdapter = Radapter()
rRecyclerView?.adapter = rAdapter
//쿼리 값을 Map 으로 생성하여 api 호출
val queries = mapOf("parameter" to EventName)
val call: Call<Record?>? = rApi!!.getRecordData(queries)
call?.enqueue(object : Callback<Record?> {
#SuppressLint("NotifyDataSetChanged")
override fun onResponse(
call: Call<Record?>,
response: retrofit2.Response<Record?>
) {
//응답 성공시 어댑터에 결과 전달
val result: Record = response.body() as Record
Log.d(logTag, "onResponse: refresh 직전")
rAdapter.setList(result.data)
rAdapter.notifyDataSetChanged()
//rAdapter.notifyItemRangeChanged(0, result.data?.size!!);
}
override fun onFailure(call: Call<Record?>, t: Throwable) {
Log.e("D/OkHttp", "onFailure - message=" + t.message)
}
})
return root
}
override fun onDestroyView() {
super.onDestroyView()
rRecyclerView?.adapter = null
rRecyclerView = null
}
inner class Radapter : RecyclerView.Adapter<RecordFragment.Radapter.RvhItem>() {
private var showMatchCount: Int? = 0
private var matchCount: Int? = 0
private var mRecord: ArrayList<Event>? = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RvhItem {
val vhView = LayoutInflater.from(parent.context).inflate(R.layout.vh_record, parent, false)
return RvhItem(vhView)
}
#SuppressLint("NotifyDataSetChanged")
fun setList(Data: List<Event>?) {
mRecord!!.clear()
mRecord!!.addAll(Data!!)
notifyDataSetChanged()
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: RvhItem, position: Int) {
val record: Event = mRecord!![position]
holder.EventName.text = record._event_name
holder.EventDate.text = record._event_date
showMatchCount = record._event_count?.toInt()
matchCount = record._event_result!!.size
}
override fun getItemCount(): Int {
return mRecord!!.size
}
inner class RvhItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
var EventName: TextView = itemView.findViewById(R.id.tv_event_name)
var EventDate: TextView = itemView.findViewById(R.id.tv_fighter_ranking)
}
}
}

Categories

Resources