How to update fragmentStateAdapter after a bottomSheet dismiss - android

I have this fragmentStateAdapter which holds some of my Cards in order to swipe among them:
public class ScreenSlidePagerAdapter(activity: AppCompatActivity, var items: ArrayList<Helpers.Card>, private val saveToDb: Boolean) : FragmentStateAdapter(activity) {
override fun getItemCount(): Int = items.count()
var fragment:CardItemFragment?=null
override fun createFragment(position: Int): Fragment{
fragment =CardItemFragment(position, saveToDb ,this) //.newInstance( items[position ].getSaveString())
return fragment as CardItemFragment
}}
and the CardItemFragement is:
class CardItemFragment(val position:Int, val saveToDb: Boolean, val isCardMine: Boolean,val adapter: ScreenSlidePagerAdapter) : Fragment() {
lateinit var cardView1: ConstraintLayout
lateinit var crdNameTB1: TextView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.card_item, container, false)
cardView1 = v.findViewById(R.id.cardView1)
crdNameTB1= v.findViewById(R.id.crdNameTB1)
return v
}
Then I can update the Card inside the fragementStateAdapter like this:
fun updateCard(): Bitmap? {
crdNameTB1.text = .....
}
That's works fine and I can swipe among my Cards, but when I change the value of "name" attribute of my Card via a bottom sheet and when I dismiss the BottomSheet I use this code to refresh the fragmentSatateAdapter:
MainActivity.galleryPagerAdapter?.fragment ?.updateCard()
I set MyActivity as companion object to reach it anywher.
When this code executed, I got the fragment not null object but the updateCard gives me null objects
1what is the problem and how can I overcome this and update the shown Card?

I have found the answer by overriding the following methods in order to make the Adapter run
override fun getItemId(position: Int): Long {return items[position].id}
override fun containsItem(itemId: Long): Boolean = items.any { it.id == itemId }
the answer post is here

Related

Is it okay to delete a CompanionObject on a fragment?

So I'm working with Android Studio, and I found a video on youtube on how to set tab layout. I'm trying to create a fragment with new>Fragment>Fragment(Blank) and I'm following youtube instructions to remove everything else except onCreate and onCreateView in Fragment class. But when i try to put everything together, i get this error:
Classifier 'PemasukanFragment' does not have a companion object, and thus must be initialized here
I use AddActivity class to hold my fragment, the code look like this:
class AddActicity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_acticity)
setUpTabs()
}
private fun setUpTabs(){
val adapter= Adapter_AddActivity(supportFragmentManager)
adapter.addFragment(PemasukanFragment,"Pemasukan")
}
}
I have 2 fragment pages. The code look like this:
class PemasukanFragment : Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
arguments?.let {
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_pemasukan, container, false)
}
}
The other fragment page have a similar code to this. I also have adapter that look like this:
class Adapter_AddActivity(supportFragmentManager: FragmentManager) :
FragmentPagerAdapter(supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val mFragmentList = ArrayList<Fragment>()
private val mFragmentListTitle = ArrayList<String>()
override fun getCount(): Int {
return mFragmentList.size
}
override fun getItem(position: Int): Fragment {
return mFragmentList[position]
}
override fun getPageTitle(position: Int): CharSequence? {
return mFragmentListTitle[position]
}
fun addFragment(fragment: Fragment, title: String){
mFragmentList.add(fragment)
mFragmentListTitle.add(title)
}
}
I don't know if companion objects can be removed. But if it can, how to solve this error? Any help is appreciated

I could not connect my recyclerview in fragment with adapter

my fragment class
class RecyclerFragment : Fragment() {
var recycler:RecyclerView? =null
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
recycler = view?.findViewById(R.id.recycler)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_recycler, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
recycler?.layoutManager = LinearLayoutManager(this.context,RecyclerView.VERTICAL,false)
recycler?.adapter = Custom_Adapter() // error-Unresolved reference: Custom_Adapter
}
}
//here is my adater
class Custom_Adapter() : RecyclerView.Adapter<Custom_Adapter.ViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): Custom_Adapter.ViewHolder {
var myViewInflater = LayoutInflater.from(viewGroup.context).inflate(R.layout.custom_layout,viewGroup,false)
return ViewHolder(myViewInflater)
}
override fun onBindViewHolder(holder: Custom_Adapter.ViewHolder, position: Int) {
}
override fun getItemCount(): Int {
return 9
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
}
Update your recycler fragment like this.
class RecyclerFragment : Fragment(R.layout.fragment_recycler) {
var recycler:RecyclerView? =null
private var param1: String? = null
private var param2: String? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recycler = view.findViewById(R.id.recycler)
recycler?.layoutManager = LinearLayoutManager(this.context,RecyclerView.VERTICAL,false)
recycler?.adapter = Custom_Adapter()
}
}
And you have to move classes Recucler_Item and Custom_Adapter into correct java package.
From your repo link in one of the comments, Custom_Adapter is under your src/test directory. It needs to be under src/main where RecyclerFragment can see it - that's why you're getting unresolved reference errors. Stick it in there with everything else
main and test are different source sets, it's basically a way of keeping stuff separate. All your production code goes in main, and it can't see anything in test by default. When you run tests, those use everything in test (and maybe androidTest) and get all the stuff in main included, since the tests need access to the production code to, y'know, test it!

recyclerview not visible at run time

I implemented a simple recyclerview using databinding using kotlin language.
In xml, recyclerview seems to be the default,
but when I run it, it doesn't show up as default.
Other buttons and views included in the fragment appear normally when executed, but only the recyclerview is not visible. Which part is wrong?
class WorkFragment : Fragment() {
private var _binding: FragmentWorkBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWorkBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val data = mutableListOf<WorkList>()
val adapter = WorkAdapter()
adapter.data = data
binding.recycler1.adapter = adapter
binding.recycler1.layoutManager = LinearLayoutManager(requireContext())
}
}
class WorkAdapter : RecyclerView.Adapter<WorkAdapter.ViewHolder>() {
var data = mutableListOf<WorkList>()
class ViewHolder(val binding: RecyclerviewWorkItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(workList: WorkList) {
binding.tvStarttime.text = workList.Starttime
binding.tvAdmin.text = workList.Admin
binding.tvPart.text = workList.Part
binding.tvStoptime.text = workList.Stoptime
binding.tvWorkers.text = workList.Workers.toString()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkAdapter.ViewHolder {
val binding = RecyclerviewWorkItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: WorkAdapter.ViewHolder, position: Int) {
holder.bind(data[position])
}
override fun getItemCount(): Int {
return data.size
}
// fun replaceList(newList: MutableList<WorkList>) {
// data = newList.toMutableList()
// notifyDataSetChanged()
// }
}
Is there something wrong with my code?
For reference, I just used a fragment, not an activity.
its typically for the RecyclerView to behave like its hidden but actually its not. the reason is you are passing an empty list so the recycler will not be extended or show because there is no items in the list.

Shared element does not return to RecyclerView item

I have a fragment called MainFragment that contains a ViewPager that contains another fragment called LibraryFragment.
LibraryFragment contains a RecyclerView with a list of items that contain an ImageView. The ImageView's contents are loaded with Coil.
When an item is clicked, LibraryFragment navigates to another fragment called ArtistDetailFragment and uses the ImageView from the item.
The problem is that while the enter transition works fine, the ImageView does not return to the list item when navigating back and only fades away. Ive attached an example below:
Ive tried using postponeEnterTransition() and startPostponedEnterTransition() along with adding a SharedElementCallback but neither have worked that well. I've also ruled out Coil being the issue.
Heres LibraryFragment:
class LibraryFragment : Fragment() {
private val musicModel: MusicViewModel by activityViewModels()
private val libraryModel: LibraryViewModel by activityViewModels()
private lateinit var binding: FragmentLibraryBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentLibraryBinding.inflate(inflater)
binding.libraryRecycler.adapter = ArtistAdapter(
musicModel.artists.value!!,
BindingClickListener { artist, itemBinding ->
navToArtist(artist, itemBinding)
}
)
return binding.root
}
private fun navToArtist(artist: Artist, itemBinding: ItemArtistBinding) {
// When navigation, pass the artistImage of the item as a shared element to create
// the image popup.
findNavController().navigate(
MainFragmentDirections.actionShowArtist(artist.id),
FragmentNavigatorExtras(
itemBinding.artistImage to itemBinding.artistImage.transitionName
)
)
}
}
Heres ArtistDetailFragment:
class ArtistDetailFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentArtistDetailBinding.inflate(inflater)
sharedElementEnterTransition = TransitionInflater.from(requireContext())
.inflateTransition(android.R.transition.move)
val musicModel: MusicViewModel by activityViewModels()
val artistId = ArtistDetailFragmentArgs.fromBundle(requireArguments()).artistId
// Get the transition name used by the recyclerview ite
binding.artistImage.transitionName = artistId.toString()
binding.artist = musicModel.artists.value?.find { it.id == artistId }
return binding.root
}
}
And heres the RecyclerView Adapter/ViewHolder:
class ArtistAdapter(
private val data: List<Artist>,
private val listener: BindingClickListener<Artist, ItemArtistBinding>
) : RecyclerView.Adapter<ArtistAdapter.ViewHolder>() {
override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemArtistBinding.inflate(LayoutInflater.from(parent.context))
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(data[position])
}
// Generic ViewHolder for an artist
inner class ViewHolder(
private val binding: ItemArtistBinding
) : RecyclerView.ViewHolder(binding.root) {
// Bind the view w/new data
fun bind(artist: Artist) {
binding.artist = artist
binding.root.setOnClickListener { listener.onClick(artist, binding) }
// Update the transition name with the new artist's ID.
binding.artistImage.transitionName = artist.id.toString()
binding.executePendingBindings()
}
}
}
EDIT: I used postponeEnterTransition and startPostponedEnterTransition like this:
// LibraryFragment
override fun onResume() {
super.onResume()
postponeEnterTransition()
// Refresh the parent adapter to make the image reappear
binding.libraryRecycler.adapter = artistAdapter
// Do the Pre-Draw listener
binding.libraryRecycler.viewTreeObserver.addOnPreDrawListener {
startPostponedEnterTransition()
true
}
}
This only makes the RecyclerView itself refresh however, the shared element still fades away instead of returning to the RecyclerView item.
Chris Banes says;
You may wonder why we set the OnPreDrawListener on the parent rather than the view itself. Well that is because your view may not actually be drawn, therefore the listener would never fire and the transaction would sit there postponed forever. To work around that we set the listener on the parent instead, which will (probably) be drawn.
https://chris.banes.dev/fragmented-transitions/
try this;
change
// LibraryFragment
override fun onResume() {
super.onResume()
postponeEnterTransition()
// Refresh the parent adapter to make the image reappear
binding.libraryRecycler.adapter = artistAdapter
// Do the Pre-Draw listener
binding.libraryRecycler.viewTreeObserver.addOnPreDrawListener {
startPostponedEnterTransition()
true
}
}
enter code here
to
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
postponeEnterTransition()
binding = FragmentLibraryBinding.inflate(inflater)
binding.libraryRecycler.adapter = ArtistAdapter(
musicModel.artists.value!!,
BindingClickListener { artist, itemBinding ->
navToArtist(artist, itemBinding)
}
)
(view?.parent as? ViewGroup)?.doOnPreDraw {
// Parent has been drawn. Start transitioning!
startPostponedEnterTransition()
}
return binding.root
}

How to Read Data from RecyclerView and send in BottomSheet

I am using recyclerView to show list of apps installed in device
image - Link
and for more details I use bottomSheet on LongPress i.e. in ViewHolder Class, but How to send data of selected tab to bottomSheet with more details(such as package name, API level etc.)...for reference see images
I want - Link
I get from below coding - Link
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.adapter = Adapter // I set adapter here with function getApps()
recyclerView.layoutManager = LinearLayoutManager(this)
private fun getApps(): List<DataClass> {
// here I get apps icon,name,size and return list<DataClass>
return list
}
Adapter.kt
class Adapter(private val listOfApps: List<AppData>) :
RecyclerView.Adapter<Adapter.ViewHolder>() {
class ViewHolder(appView: View) : RecyclerView.ViewHolder(appView), View.OnClickListener,
View.OnLongClickListener {
init {
appView.setOnClickListener(this)
appView.setOnLongClickListener(this)
}
val icon: ImageView = appView.App_icon
val name: TextView = appView.App_name
val size: TextView = appView.App_size
override fun onClick(v: View?) {
Toast.makeText(v?.context, "OnClick", Toast.LENGTH_SHORT).show()
}
override fun onLongClick(v: View?): Boolean {
// I want here on Long press BottomSheet appears with details
val bottomSheetDialog = BottomSheetDialog()
// Show bottomSheet on LongPress
bottomSheetDialog.show(
(v?.context as FragmentActivity).supportFragmentManager, bottomSheetDialog.tag
)
return true
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.list_apps, parent, false
)
return ViewHolder(view)
}
override fun getItemCount() = listOfApps.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = listOfApps[position]
holder.icon.setImageDrawable(currentItem.icon)
holder.name.text = currentItem.name
holder.size.text = currentItem.size
}
}
BottomSheetDialog.kt
class BottomSheetDialog: BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
DataClass
data class AppData(
val icon: Drawable,
val name: String,
val size: String,
)
With your current code the easiest solution would be:
Modify your BottomSheetDialog to include AppData in the constructor
class BottomSheetDialog(val appData: AppData): BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
Add onBind method inside your ViewHolder class:
fun onBind(appData: AppData) {
icon.setImageDrawable(currentItem.icon)
name.text = currentItem.name
size.text = currentItem.size
}
Modify your onBindViewHolder method inside the Adapter to call that onBind method:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.onBind(listOfApps[position])
}
Inside your ViewHolder add lateinit var currentItem: AppData that will be set inside onBind and we can use it in onLongClick:
class ViewHolder(appView: View) : RecyclerView.ViewHolder(appView), View.OnClickListener,
View.OnLongClickListener {
.
.
.
override fun onLongClick(v: View?): Boolean {
// I want here on Long press BottomSheet appears with details
**val bottomSheetDialog = BottomSheetDialog(currentItem)**
// Show bottomSheet on LongPress
bottomSheetDialog.show(
(v?.context as FragmentActivity).supportFragmentManager, bottomSheetDialog.tag
)
return true
}
**private lateinit var currentItem: AppData**
fun onBind(appData: AppData) {
**currentItem = appData**
icon.setImageDrawable(currentItem.icon)
name.text = currentItem.name
size.text = currentItem.size
}
attach your currentItem list item to ViewHolder inside onBindViewHolder and inside onLongClick pass this data to freshly created BottomSheetDialog, which is extending Fragment (so you may use Bundle - setArgument method). then inside onCreateView set your data to Views
Create an interface
interface OnListItemClicked {
fun onClicked(data : AppData)
}
Implement that interface in BottomSheetDialog
class BottomSheetDialog: BottomSheetDialogFragment(), OnListItemClicked {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun onClicked(data: AppData) {
//show Details here
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
Pass extra callback to the adapter from main activity.
Adapter(private val listOfApps: List<AppData>, val onClick: (AppData) -> Unit)
Invoke the click listener from adapter with listOfAppData[position]
On MainActivity pass a function into the Adapter initialisation.
fun passDataToBottomSheet(data: AppData) {
val listener = OnListItemClicked()
listener.onClicked(data)
}
Adapter(list, ::passDataToBottomSheet)
That's it. It should work now.

Categories

Resources