RecyclerView Shared Element Transition Not Working - android

My aim : GIF
I followed this guide to implement shared element transitions in my app
My Result : Screen Capture
Problem : The shared animation is happening. But the image in the ItemMovieFragment is coming from top left corner of container.
Cannot figure out the solution.
Please suggest some solution
Here is the code :
ItemMovieFragment : complete code
class ItemMovieFragment : Fragment() {
private lateinit var itemMovieViewModel: ItemMovieViewModel
private lateinit var args : ItemMovieFragmentArgs
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentItemMovieBinding.inflate(inflater)
// (activity as AppCompatActivity).supportActionBar?.hide()
sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
// rest of the code
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ViewCompat.setTransitionName(image_poster,args.itemiId.toString())
Toast.makeText(context,"id: ${args.itemiId}", Toast.LENGTH_SHORT).show()
}
}
ItemMovieFragment - Layout : complete code
<androidx.cardview.widget.CardView
android:id="#+id/poster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
app:cardCornerRadius="4dp"
app:cardElevation="10dp"
app:cardMaxElevation="10dp"
app:layout_constraintBottom_toTopOf="#+id/scrollView2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/scrollView2">
<ImageView
android:id="#+id/image_poster"
android:layout_width="120dp"
android:layout_height="170dp"
android:layout_margin="0dp"
android:padding="0dp"
android:scaleType="centerCrop"
app:imageUrl="#{itemMovieViewModel.movieInf.posterPath}"
app:srcCompat="#drawable/stp" />
</androidx.cardview.widget.CardView>
CompletedFragment : complete code
class CompletedFragment(private var type : String) : Fragment() {
//firebase
private lateinit var firebaseDatabase: FirebaseDatabase
private lateinit var dbReference: DatabaseReference
private lateinit var firebaseAdapter: FirebaseRVAdapter
private lateinit var auth: FirebaseAuth
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentCompletedBinding.inflate(inflater)
// rest of the code...
firebaseAdapter = FirebaseRVAdapter(FirebaseRVAdapter.ItemClickListener { item ->
val id : Int = item.id!!.toInt()
val extras = FragmentNavigatorExtras(
poster to item.id!!
)
if(type == TV)
findNavController().navigate(MyWatchlistFragmentDirections.actionGlobalItemTvFragment(id))
else
findNavController().navigate(MyWatchlistFragmentDirections.actionMyWatchlistFragmentToItemMovieFragment(id),extras) // this is the navigation line for shared animation
}, optionss)
binding.myfirebaseList.adapter = firebaseAdapter
return binding.root
}
//rest of the code
}
FirebaseRVAdapter :
package com.utkarsh.fxn.ui.mywatchlist
class FirebaseRVAdapter(private val clickListener: ItemClickListener , options: FirebaseRecyclerOptions<TvFirebase>)
: FirebaseRecyclerAdapter<TvFirebase, FirebaseRVAdapter.tvFirebaseViewHolder>(options) {
class tvFirebaseViewHolder ( private var binding: MywatchlistListItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(
tvfirebase: TvFirebase,
clickListener: ItemClickListener
){
ViewCompat.setTransitionName(binding.poster,tvfirebase.id) //setting transition name
binding.tvFirebase=tvfirebase
binding.clickListener=clickListener
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): tvFirebaseViewHolder {
return tvFirebaseViewHolder(MywatchlistListItemBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: tvFirebaseViewHolder, position: Int, model: TvFirebase) {
return holder.bind(model!!,clickListener)
}
class ItemClickListener(val listener : (tvfirebase: TvFirebase) -> Unit){
fun OnClick(tvfirebase: TvFirebase) = listener(tvfirebase)
}
}
XML of image of item of Completed Fragment : complete file
<ImageView
android:id="#+id/poster"
android:layout_width="114dp"
android:layout_height="170dp"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
app:imageUrl="#{tvFirebase.poster_path}"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.013"
tools:srcCompat="#drawable/smol" />

You must have same transitionName for item and details screen.
<ImageView
android:id="#+id/poster"
android:layout_width="114dp"
android:layout_height="170dp"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:transitionName="image_poster"
app:imageUrl="#{tvFirebase.poster_path}"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.013"
tools:srcCompat="#drawable/smol" />

Related

Connect tabItem to viewpager2 in Kotlin

I have one AlertSettingfragment which has two tabItem ( SMS text and Email). I have created two fragments for viewpager(SMStext Fragment and Email Fragment) according to the tabItem. But i am not able to connect to the viewpager. Please Help
fragment_alertSetting.xml
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="42dp"
android:layout_margin="2dp"
android:backgroundTint="#color/gray"
app:tabBackground="#drawable/track_selector"
app:tabGravity="fill"
app:tabIndicatorHeight="0dp"
app:tabMode="fixed"
app:tabRippleColor="#color/transparent"
app:tabSelectedTextColor="#color/dark_blue"
app:tabTextAppearance="#style/CustomTextAppearanceTab"
app:tabTextColor="#color/gray_blue">
<com.google.android.material.tabs.TabItem
android:id="#+id/smsTab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/sms_text" />
<com.google.android.material.tabs.TabItem
android:id="#+id/emailTab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/email" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/alertSettingViewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="12dp"
android:layout_marginVertical="16dp"
android:background="#color/white"
app:layout_constraintTop_toBottomOf="#id/tabLayoutView" />
AlertSettingFragment.kt
#AndroidEntryPoint
class CascadeSettingsAlertFragment : Fragment() {
private lateinit var binding: FragmentCascadeSettingsAlertBinding
private var listener: OnFragmentInteractionListener? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentCascadeSettingsAlertBinding.inflate(inflater,container,false).apply {
alertSettingViewPager.adapter = CascadeAlertSettingAdapter(this#CascadeSettingsAlertFragment)
TabLayoutMediator(
tabLayout,
alertSettingViewPager
) { tabs, position ->
val tabBinding = when (position) {
SMS_PAGE_INDEX -> binding.smsTab
EMAIL_PAGE_INDEX -> binding.emailTab
else -> binding.smsTab
}
tabs.view = tabBinding.
}.attach()
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
AlertSettingAdapter.kt
const val SMS_PAGE_INDEX = 0
const val EMAIL_PAGE_INDEX = 1
class CascadeAlertSettingAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
private val tabFragmentsCreators: Map<Int, () -> Fragment> = mapOf(
SMS_PAGE_INDEX to { AlertSettingsSmsFragment() },
EMAIL_PAGE_INDEX to { AlertSettingsEmailFragment() },
)
override fun getItemCount() = tabFragmentsCreators.size
override fun createFragment(position: Int): Fragment {
return tabFragmentsCreators[position]?.invoke() ?: throw IndexOutOfBoundsException()
}
}
You have to add the TabItems programmatically to the TabLayout, TabItems added via XML are not actually added to the TabLayout, as mentioned in the documentation of TabLayout.
More info:
https://developer.android.com/reference/com/google/android/material/tabs/TabItem

Bottom Sheet Fragment only show the first item in the list

I like to show a language list when I press the button. I use dependency injection on the bottom sheet fragment. But it only shows the first item on the list, that is "English". Is there something I miss? Thanks.
I try to use the following sample to learn the concept of hilt and digger.
https://www.geeksforgeeks.org/dagger-hilt-in-android-with-example/
KC
** AppModule **
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Provides
fun provideTestString() = "This is a string we will inject"
#Provides
#Singleton
fun provideLanguageRepository(): LanguageRepository = LanguageRepositoryImpl()
}
** BottomSheetFragment **
#AndroidEntryPoint
class BottomSheetFragment : BottomSheetDialogFragment(){
private lateinit var binding: BottomSheetBinding
private lateinit var languageList: RecyclerView
#Inject
lateinit var testString: String
private val viewModel: LanguageViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = BottomSheetBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
languageList = binding.languageList
languageList.layoutManager = LinearLayoutManager(context)
observeLanguage()
}
private fun observeLanguage() {
viewModel.language.observe(this) {
languageList.adapter = LanguageAdapter(it)
}
}
}
** Language **
data class Language(
val isChecked: Boolean,
val language: String
)
** Language Adapter **
class LanguageAdapter(private val language: List<Language>) :
RecyclerView.Adapter<LanguageAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LanguageAdapter.ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_language, parent, false)
return ViewHolder(view)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(index:Language){
itemView.findViewById<CheckBox>(R.id.isChecked).isChecked = index.isChecked
itemView.findViewById<TextView>(R.id.language).text = index.language
}
}
override fun getItemCount(): Int = language.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(language[position])
}
}
** Language Repository **
interface LanguageRepository {
fun getLanguage(): List<Language>
}
** Language Repository Implementation **
class LanguageRepositoryImpl : LanguageRepository {
override fun getLanguage() = listOf(
Language(true, "English"),
Language(true, "Spanish"),
Language(false, "Korean"),
Language(false, "Japanese"),
Language(false, "Hindi"),
)
}
** Language View Model **
#HiltViewModel
class LanguageViewModel #Inject constructor(
private val languageRepository: LanguageRepository
) : ViewModel() {
private val languageEmitter = MutableLiveData<List<Language>>()
val language: LiveData<List<Language>> = languageEmitter
init {
loadLanguage()
}
private fun loadLanguage(){
languageEmitter.value = languageRepository.getLanguage()
}
}
** Main Activity **
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val bottomSheetFragment = BottomSheetFragment()
binding.btnShow.setOnClickListener{
bottomSheetFragment.show(supportFragmentManager, "bottom_sheet")
}
}
}
** MyApplication **
#HiltAndroidApp
class MyApplication : Application(){
}
** activity main **
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="#+id/btn_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
** Bottom Sheet **
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/language_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
** item language **
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<CheckBox
android:id="#+id/isChecked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:minWidth="48dp"
android:minHeight="48dp" />
<TextView
android:id="#+id/language"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/isChecked"
android:layout_alignBottom="#+id/isChecked"
android:layout_toEndOf="#+id/isChecked"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
tools:text="English" />
</RelativeLayout>
** I have the following error **
2022-07-21 12:29:13.664 22674-22674/? E/Zygote: isWhitelistProcess - Process is Whitelisted
2022-07-21 12:29:13.665 22674-22674/? E/Zygote: accessInfo : 1
2022-07-21 12:29:13.695 22674-22674/? E/omsheetwithhil: Unknown bits set in runtime_flags: 0x8000
2022-07-21 12:30:04.312 22674-22674/com.example.testbottomsheetwithhilt E/DecorView: mWindow.mActivityCurrentConfig is null
I am stupid...The language list is not showing because I set the layout height as "match_parent" in the "Bottom Sheet"
I should set have set it as "wrap_content".
KC

Data does not appear in the recylcerview

I have 2 fragments which each have a Recyclerview and use the same Adapter. But why does the data only appear in the Recyclerview on the DashboardFragment while the Recyclerview in TiketFragment doesn't?
Screenshot DashboardFragment
Screenshot TiketFragment
TiketFragment.kt
class TiketFragment : Fragment() {
private lateinit var preferences: Preferences
private lateinit var mDatabase:DatabaseReference
private var dataList=ArrayList<Film>()
override fun onCreateView(
inflater: LayoutInflater,container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_tiket, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
preferences= Preferences(activity!!.applicationContext)
mDatabase=FirebaseDatabase.getInstance().getReference("Film")
rc_ticket.layoutManager=LinearLayoutManager(context)
getData()
}
private fun getData() {
mDatabase.addValueEventListener(object :ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
dataList.clear()
for(getdataSnapshot in snapshot.children){
val film=getdataSnapshot.getValue(Film::class.java)
dataList.add(film!!)
}
rc_ticket.adapter=ComingSoonAdapter(dataList){
}
tv_total.setText("${dataList.size} Movies")
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(context, error.message, Toast.LENGTH_LONG).show()
}
})
}
}
DashboardFragment.kt
class DashboardFragment : Fragment() {
private lateinit var preferences:Preferences
private lateinit var mDatabase:DatabaseReference
private var dataList=ArrayList<Film>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_dashboard , container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
preferences= Preferences(activity!!.applicationContext)
mDatabase=FirebaseDatabase.getInstance().getReference("Film")
tv_nama.setText(preferences.getValues("nama"))
if(preferences.getValues("saldo").equals("")){
currency(preferences.getValues("saldo")!!.toDouble(),tv_saldo)
}
Glide.with(this)
.load(preferences.getValues("url"))
.apply(RequestOptions.circleCropTransform())
.into(iv_profile)
rv_now_playing.layoutManager=LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false)
rv_coming_soon.layoutManager=LinearLayoutManager(context)
getData()
}
private fun getData() {
mDatabase.addValueEventListener(object :ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
dataList.clear()
for(getSnapshot in snapshot.children){
var film=getSnapshot.getValue(Film::class.java)
dataList.add(film!!)
}
rv_now_playing.adapter=NowPlayingAdapter(dataList){
var intent=Intent(context,DetailActivity::class.java).putExtra("data",it)
startActivity(intent)
}
rv_coming_soon.adapter=ComingSoonAdapter(dataList){
var intent=Intent(context,DetailActivity::class.java).putExtra("data",it)
startActivity(intent)
}
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(context,""+error.message,Toast.LENGTH_LONG).show()
}
})
}
private fun currency(harga:Double, textView: TextView){
val localID=Locale("in","ID")
val format= NumberFormat.getCurrencyInstance(localID)
textView.setText(format.format(harga))
}
}
ComingSoonAdapter.kt
class ComingSoonAdapter(private var data:List<Film>,
private val listener:(Film)-> Unit) : RecyclerView.Adapter<ComingSoonAdapter.ViewHolder>() {
lateinit var contextAdapter:Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ComingSoonAdapter.ViewHolder {
val layoutInflater=LayoutInflater.from(parent.context)
contextAdapter=parent.context
val inflatedView=layoutInflater.inflate(R.layout.row_item_coming_soon,parent,false)
return ViewHolder(inflatedView)
}
override fun onBindViewHolder(holder: ComingSoonAdapter.ViewHolder, position: Int) {
holder.bindItem(data[position],listener,contextAdapter)
}
override fun getItemCount(): Int =data.size
class ViewHolder(view:View):RecyclerView.ViewHolder(view){
private val tvJudul:TextView=view.findViewById(R.id.tv_title)
private val tvRating:TextView=view.findViewById(R.id.tv_rate)
private val tvGenre:TextView=view.findViewById(R.id.tv_genre)
private val ivPoster:ImageView=view.findViewById(R.id.iv_poster_image)
fun bindItem(data: Film, listener: (Film) -> Unit, context: Context){
tvJudul.setText(data.judul)
tvGenre.setText(data.genre)
tvRating.setText(data.rating)
Glide.with(context)
.load(data.poster)
.into(ivPoster)
itemView.setOnClickListener{
listener(data)
}
}
}
}
fragment_tiket.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/shape_background_blue"
tools:context=".home.tiket.TiketFragment">
<TextView
android:id="#+id/textView15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="24dp"
android:fontFamily="#font/montserrat"
android:text="Today's"
android:textColor="#color/white"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tv_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:fontFamily="#font/montserrat"
android:text="4 Movies"
android:textColor="#color/white_grey"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView15" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rc_ticket"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tv_total"
tools:itemCount="4"
tools:listitem="#layout/row_item_coming_soon" />
</androidx.constraintlayout.widget.ConstraintLayout>

Fragments inside other fragment: crash with "has not been attached yet."

Hi I have a strange problem. MainActivity has a framelayout and a main fragment EnterFragment.
EnterFragment has a framelayout and i'd like to see other 2 fragments. SignupFragment and LoginFragment.
When EnterFragment starts, it shows SignupFragment.java.. after MainActivity invoke a method of EnterFragment to change fragment to LoginFragment but app crash:
java.lang.IllegalStateException: Fragment EnterFragment{95bc7de (70f980b2-eff8-4e4c-a644-e95544b73584)} has not been attached yet.
at androidx.fragment.app.Fragment.getChildFragmentManager(Fragment.java:923)
Here the project: https://www.dropbox.com/s/qq1kl4qw688ipjr/example.zip?dl=0
Here the code:
//MainActivity
class MainActivity : AppCompatActivity() {
private var enterFragment: EnterFragment?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showEnter()
}
private fun showEnter() {
enterFragment= EnterFragment.newInstance()
this.replaceFragmentTransition(R.id.fragmentContainer, EnterFragment.newInstance(), EnterFragment.TAG )
var handler= Handler()
handler.postDelayed(Runnable {gotoLoginFromSignup() }, 8000)
}
private fun gotoLoginFromSignup() {
enterFragment?.gotoLogin()
}
}
//EnterFragment
class EnterFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_enrer, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
this.replaceFragmentTransition(R.id.fmContainer, SignuFragment.newInstance(), SignuFragment.TAG )
}
fun gotoLogin(){
this.replaceFragmentTransition(R.id.fmContainer, LoginFragment.newInstance(), SignuFragment.TAG )
}
companion object {
val TAG: String="EnterFragment"
#JvmStatic
fun newInstance() = EnterFragment()
}
}
//SignupFragment
class SignuFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_signup, container, false)
}
companion object {
val TAG: String="SignuFragment"
#JvmStatic
fun newInstance() = SignuFragment()
}
}
//LoginFragment
class LoginFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_login, container, false)
}
companion object {
val TAG: String="LoginFragment"
#JvmStatic
fun newInstance() = LoginFragment()
}
}
//EXTENSIONS
fun FragmentActivity.replaceFragmentTransition(
container: Int,
fragment: Fragment?,
tag: String
) {
fragment?.let {
var transition = this.supportFragmentManager
.beginTransaction()
transition.replace(container, it, tag)
.commitNowAllowingStateLoss()
}
}
fun Fragment.replaceFragmentTransition(
container: Int,
fragment: Fragment?,
tag: String
) {
fragment?.let {
var transition = this.childFragmentManager
.beginTransaction()
transition.replace(container, it, tag)
.commitNowAllowingStateLoss()
}
}
//main activity layout
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentContainer"
android:layout_width="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_height="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
//enterFragment
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".EnterFragment">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fmContainer"
android:layout_width="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_height="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
Replace this
this.replaceFragment(R.id.fragmentContainer, FatherFragment.newInstance(), FatherFragment.TAG )
with
this.replaceFragment(R.id.fragmentContainer, fatherFragment!!, FatherFragment.TAG )
while transaction, you are passing different instance & calling openLogin() with another instance.
FatherFragment.newInstance() will give new instance.

Kotlin Android Recycler view inside Fragment with Bottom Navigation not working

I am trying to develop social networking app which uses Android Jetpack Libraries but while using Navigation Component to use bottom navigation to navigate through fragments inside an activity , This Adapter throws an error at LayoutInflator() which causes app to crash
Can anyone help me through this:
My Adapter Class:
class FeedAdapter : PagedListAdapter<feed,FeedAdapter.ViewHolder>(FeedDiffCallBack()){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val userPost = LayoutInflater.from(parent.context)
.inflate(R.layout.feedrow,parent,false)
return ViewHolder(userPost)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val feedItem = getItem(position)
if(feedItem != null){
holder.bind(feedItem)
}
}
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView) {
//Retrieve data
private val username:TextView = itemView.post_name
private val userPic:ImageView = itemView.feedImage1
private val location:TextView = itemView.postLocation
private val time:TextView = itemView.postTime
private val post:ImageView = itemView.postImage
fun bind(feed: feed) = with(itemView){
//TODO:Bind Data with View
showFeedData(feed)
}
private fun showFeedData(feed: feed) {
username.text = feed.username
userPic.setImageURI(null)
userPic.visibility = View.GONE
location.text = feed.location
time.text = feed.timeStamp.toString()
post.setImageURI(Uri.parse(feed.mUrl))
}
}
}
class FeedDiffCallBack : DiffUtil.ItemCallback<feed>() {
override fun areItemsTheSame(oldItem:feed, newItem: feed): Boolean {
return oldItem?.id == newItem?.id
}
override fun areContentsTheSame(oldItem: feed, newItem: feed): Boolean {
return oldItem == newItem
}
}
Fragment Class:
class FeedFragment : Fragment() {
companion object {
fun newInstance() = FeedFragment()
}
private lateinit var viewModel: FeedViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.feed_fragment, container, false)
val context = getContext() ?: return view
val factory = InjectorUtils.provideViewModelFactory(context)
viewModel =
ViewModelProviders.of(this,factory).get(FeedViewModel::class.java)
val adapter = FeedAdapter()
view.findViewById<RecyclerView>(R.id.feedView).adapter = adapter
view.findViewById<RecyclerView>(R.id.feedView).layoutManager =
LinearLayoutManager(MyApplication.getContext())
subscribeUI(adapter)
return view
}
private fun subscribeUI(adapter: FeedAdapter) {
viewModel.showFeed().observe(this, object:Observer<PagedList<feed>>{
override fun onChanged(t: PagedList<feed>?) {
adapter.submitList(t)
adapter.notifyDataSetChanged()
}
})
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}
}
feed_row.xml
Individual item for recycler view -->
<RelativeLayout
android:id="#+id/postContainer"
android:layout_margin="10dp"
android:elevation="2dp"
android:background="#drawable/bg_parent_rounded_corner"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--TODO:Change to Circle Image View-->
<ImageView
android:id="#+id/profileImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
<LinearLayout
android:id="#+id/postDetail_1"
android:orientation="vertical"
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="20dp"
android:layout_alignParentRight="true">
<TextView
android:id="#+id/post_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/LabelStyle"
android:textSize="15sp"
android:fontFamily="#font/sf_pro_display_semibold" />
<LinearLayout
android:id="#+id/postDetail_2"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/postLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/postTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="120dp" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="#+id/postImage"
android:layout_height="200dp"
android:layout_width="match_parent"
android:layout_below="#+id/postDetail_1"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:layout_marginTop="6dp"
/>
</RelativeLayout>
Your issue might be in this method,
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val feedItem = getItem(position)
if(feedItem != null){
holder.bind(feedItem)
}
}
Your ViewHolder is using poisition from method parameter which might be inconsistent
See from here,
So, you should change line to this:
val feedItem = getItem(holder.adapterPosition)
instead of
val feedItem = getItem(position)
I hope it resolves the issue.
This might help some one looking for the same Exception to be cleared :
What I did is that my feed_row.xml above is included inside < layout > tags and I changed it in this way , So the exception got cleared:
Before Exception:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
After changing to this Exception cleared:
<layout 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">
I don't know how it works but it did work!!! So Anyone who knows what happens there can explain please!

Categories

Resources