Kotlin rotateAnimation is not working. Nothing is happening when the spinBtn is clicked - kotlin

***I am trying to create af spinningWheel animation with my imageView spinning once the button is pressed. I found a youtube video with some java code i converted into kotlin. With the code below nothing happens when the button is pushed. I have used the rotateAnimation class and some calculations in the GameViewModel thus far, and call the spin() in the GameFragment class' onCreateView().
The code below are the two classes i use for the rotateAnimation but it doesn't seem to work. I am not sure which part is the problem. Does anyone know?***
class GameViewModel : ViewModel() {
private val fields = arrayOf("1","2","3","4","5","6","7")
private val fieldDegrees = IntArray(fields.size)
private var degree = 0
var isSpinning = false
fun getDegreesForSectors(){
val oneFieldsDegrees = 360/fields.size
for (i in fields.indices){
fieldDegrees[i] = (i+1) * oneFieldsDegrees
}
}
fun spin(imageView: ImageView){
isSpinning = true
degree = (0..fields.size).random()
val rotateAnimation = RotateAnimation(0.toFloat(),
(360 * fields.size + fieldDegrees[degree]).toFloat(),
RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f)
rotateAnimation.setDuration(3600)
rotateAnimation.setFillAfter(true)
rotateAnimation.setInterpolator(DecelerateInterpolator())
rotateAnimation.setAnimationListener(object : Animation.AnimationListener{
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {
isSpinning = false
}
override fun onAnimationRepeat(animation: Animation) {}
})
imageView.startAnimation(rotateAnimation)
}
}
class GameFragment : Fragment() {
private lateinit var gameViewModel: GameViewModel
private var _binding: GameFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
gameViewModel =
ViewModelProvider(this).get(GameViewModel::class.java)
val spinBtn: Button? = view?.findViewById(R.id.spinBtn)
val wheelImage: ImageView? = view?.findViewById(R.id.Spinning_Wheel)
gameViewModel.getDegreesForSectors()
spinBtn?.setOnClickListener() {
if (!gameViewModel.isSpinning) {
gameViewModel.spin(wheelImage!!)
}
}
_binding = GameFragmentBinding.inflate(inflater, container, false)
val root: View = binding.root
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

You are accessing fragment view inside onCreateView(). This will return null since nothing has been inflated yet.
Use the binding variable to access the views.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
gameViewModel = ViewModelProvider(this).get(GameViewModel::class.java)
_binding = GameFragmentBinding.inflate(inflater, container, false)
gameViewModel.getDegreesForSectors()
binding.spinBtn.setOnClickListener() {
if (!gameViewModel.isSpinning) {
gameViewModel.spin(binding.spinningWheel)
}
}
return binding.root
}

Related

E/RecyclerView: No adapter attached; skipping layout ERROR (FragmentHome)

I have a problem with RecyclerView in a Fragment. In FeedActivity. I want to show the RecyclerView in my first tab but I keep getting the following error:
I am getting this error when displaying recylerview inside home fragment.
recyclerview is not displayed on the screen at all.
I tried to run it under Oncreate() but it didn't work, how can I do it?
Can you help me how can I solve this?enter image description here
**class HomeFragment : Fragment() {**
private lateinit var auth : FirebaseAuth
private lateinit var db : FirebaseFirestore
private lateinit var binding: FragmentHomeBinding
val postArrayList : ArrayList<Post> = ArrayList()
var adapter : FeedRecyclerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_home, container, false)
return view
** binding.recyclerView.layoutManager = LinearLayoutManager(activity)
adapter = FeedRecyclerAdapter(postArrayList)
binding.recyclerView.adapter = adapter**
auth = FirebaseAuth.getInstance()
db = FirebaseFirestore.getInstance()
getDataFromFirestore()
}
fun getDataFromFirestore() {
db.collection("Posts").orderBy("date",
Query.Direction.DESCENDING).addSnapshotListener { snapshot, exception ->
if (exception != null) {
Toast.makeText(context,exception.localizedMessage, Toast.LENGTH_LONG).show()
} else {
if (snapshot != null) {
if (!snapshot.isEmpty) {
postArrayList.clear()
val documents = snapshot.documents
for (document in documents) {
val comment = document.get("comment") as String
val useremail = document.get("userEmail") as String
val downloadUrl = document.get("downloadUrl") as String
//val timestamp = document.get("date") as Timestamp
//val date = timestamp.toDate()
val post = Post(useremail,comment, downloadUrl)
postArrayList.add(post)
}
adapter!!.notifyDataSetChanged()
}
}
}
}
}
}
**class FeedRecyclerAdapter(private val postList : ArrayList<Post>) : RecyclerView.Adapter<FeedRecyclerAdapter.PostHolder>() {**
class PostHolder(val binding: RecyclerRowBinding) : RecyclerView.ViewHolder(binding.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostHolder {
val binding = RecyclerRowBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return PostHolder(binding)
}
override fun getItemCount(): Int {
return postList.size
}
override fun onBindViewHolder(holder: PostHolder, position: Int) {
holder.binding.recyclerEmailText.text = postList.get(position).email
holder.binding.recyclerCommentText.text = postList.get(position).comment
Picasso.get().load(postList[position].downloadUrl).into(holder.binding.recyclerImageView)
}
}
Here you should return the view at the ed of the function, after you set your recyclerView:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_home, container, false)
return view //here this should be at the end
** binding.recyclerView.layoutManager = LinearLayoutManager(activity)
adapter = FeedRecyclerAdapter(postArrayList)
binding.recyclerView.adapter = adapter**
auth = FirebaseAuth.getInstance()
db = FirebaseFirestore.getInstance()
getDataFromFirestore()
}
Must be like this:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_home, container, false)
** binding.recyclerView.layoutManager = LinearLayoutManager(activity)
adapter = FeedRecyclerAdapter(postArrayList)
binding.recyclerView.adapter = adapter**
auth = FirebaseAuth.getInstance()
db = FirebaseFirestore.getInstance()
getDataFromFirestore()
return view //should be here
}
The solution is like this :
To solve the problem, I took the val view to the end, and then I encountered a new error, which I understood while solving it.
binding = FragmentHomeBinding.inflate(layoutInflater)
val view = binding.root
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentHomeBinding.inflate(layoutInflater)
val view = binding.root
binding.recyclerView.layoutManager = LinearLayoutManager(activity)
adapter = FeedRecyclerAdapter(postArrayList)
binding.recyclerView.adapter = adapter
auth = FirebaseAuth.getInstance()
db = FirebaseFirestore.getInstance()
getDataFromFirestore()
return view

MVVM Navigation fragments switch, but change is not visible

I am trying to build a simple app in Kotlin using MVVM architecture, and I'm struggling with switching fragments. I get it to "change" - on a button press it does nothing, and on a second press it crashes saying "Navigation action/destination MAIN_to_RESULT cannot be found from the current destination RESULT" - so it clearly changed the fragments from MAIN to RESULT, but I still see the MAIN fragment)
Here is my MAIN fragment:
class MainFragment : Fragment() {
private lateinit var viewModel: MainViewModel
lateinit var binding: FragmentMainBinding
companion object {
fun newInstance() = MainFragment()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = (activity as MainActivity).viewModel
binding.setVariable(myViewModel, viewModel)
viewModel.bnd = binding
binding.button2.setOnClickListener { viewModel.chng() }
binding.submitButton.setOnClickListener {
val controller = NavHostFragment.findNavController(this)
controller.navigate(R.id.action_mainFragment_to_resultFragment)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false)
binding.setLifecycleOwner(this)
return binding.root
}
}
OTHER fragment:
class ResultFragment : Fragment() {
private lateinit var viewModel: MainViewModel
lateinit var binding: FragmentMainBinding
companion object {
fun newInstance() = ResultFragment()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false)
binding.setLifecycleOwner(this)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = (activity as MainActivity).viewModel
binding.setVariable(BR.myViewModel, viewModel)
binding.button2.setOnClickListener { viewModel.chng() }
viewModel.bnd = binding
}
}
MainViewModel:
class MainViewModel(application: Application) : AndroidViewModel(application) {
val rps = Repository()
lateinit var bnd : FragmentMainBinding
var changing = false
var SPEED_OF_CHANGING = 100L
var actualCountedCards = 0
var numberOfCardsToRotate = 0
var numberOfRotatedCards = 0
lateinit var actualCard : Card
var mainHandler: Handler = Handler(Looper.getMainLooper())
private val changeCard = object : Runnable {
override fun run() {
if (changing) {
actualCard = rps.Cards.random()
bnd.cardView.setImageResource(actualCard.image)
actualCountedCards += actualCard.power
numberOfRotatedCards += 1
Log.d("tagicek", "${numberOfRotatedCards}/${numberOfCardsToRotate}, ${actualCountedCards}")
if (numberOfRotatedCards >= numberOfCardsToRotate) {
chng()
bnd.editTextTextPersonName.visibility = View.VISIBLE
bnd.submitButton.visibility = View.VISIBLE
bnd.button2.visibility = View.INVISIBLE
}
mainHandler.postDelayed(this, SPEED_OF_CHANGING)
}
}
}
fun chng() {
if (changing == true) {
mainHandler.removeCallbacks(changeCard)
actualCountedCards = 0
numberOfRotatedCards = 0
bnd.cardView.setImageResource(R.drawable.bg)
changing = false
} else {
numberOfCardsToRotate = (1..51).random()
mainHandler.post(changeCard)
changing = true
}
}
}
MainActivity:
class MainActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this)[MainViewModel::class.java]
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow()
}
}
}
I am pretty sure the problem is with my bindings and views, but I can't wrap my head around it. I have tried probably every positioning of defining the NavController and the different views passed into it, but it still doesn't work.

I don't know how to use currnet item in viewpager

I want to use currentItem on Fragments in viewpager2
ViewPager is also fragment. and my all fragments have viewbinding.
My fragment call viewpager by findViewById. But It isn't worked. It's data is always 'null'. Can I use findviewbyid and viewbinding both of them? I already tried to use findViewbyID in onviewcreated or oncreateview Please help me
pagertest.kt(first page fragment)
class pagertest : Fragment() {
private var _binding: FragmentPagertestBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentPagertestBinding.inflate(inflater, container, false)
binding.button5.setOnClickListener {
val pager = activity?.findViewById<ViewPager2>(R.id.dayspager22)
pager?.currentItem = 2
}
return binding.root
}
}
dayspager22.kt(ViewPagerFragment)
class dayspager22 : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
private var _binding: FragmentDayspager22Binding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentDayspager22Binding.inflate(inflater, container, false)
val fragmentList = arrayListOf<Fragment>(
pagertest(),
days_1(),
days_2(),
days_3()
)
val adapter = ViewPagerAdapter(
fragmentList,
requireActivity().supportFragmentManager,
lifecycle
)
binding.views.adapter = adapter
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment dayspager22.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
dayspager22().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
ViewPage Adapter
class ViewPagerAdapter(
list: ArrayList<Fragment>,
fm: FragmentManager,
lifecycle: Lifecycle
) : FragmentStateAdapter(fm, lifecycle) {
private val fragmentList = list
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
}
I think it might be because you're creating and adding pagertest to the adapter inside dayspager22's onCreateView method. You haven't returned its layout view yet, so it's not added to the Activity's layout - and if it's not added, you won't be able to find it with findViewById. (I'm not sure exactly when pagertest's onCreateView runs here, but I'd look into that possibility.)
Also make sure fragment_dayspager_22.xml or whatever actually has a ViewPager2 with an id of dayspager22 - you're setting your adapter on something called views:
val adapter = ViewPagerAdapter(
fragmentList,
requireActivity().supportFragmentManager,
lifecycle
)
// you're setting a ViewPagerAdapter on something that's probably a ViewPager,
// and its id is R.id.views
binding.views.adapter = adapter
Are you sure that's not the ID you want to look up with findViewById?

How to pass data from a BottomSheetFragment to another fragment?

I have an Activity, called 'HomeActivity', where I have a bottom navigation menu and four fragments. The activity has a FloatingActionButton and when I hit this, it opens a bottomSheetFragment which contain a RecyclerView and when I hit an item from it, I want some values to send to the fragment ('HomeFragment') which is part of the 'HomeActivity'
After doing a lot of searches online, I have done the following but I am not able to get the data in the fragment.
Following is the fragment from which I want to send the data.
'HomeBottomSheetFragment.kt'
class HomeBottomSheetFragment: BottomSheetDialogFragment() {
private lateinit var binding: FragmentHomeBottomSheetBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
FirestoreClass().getCategoryList(this)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
binding = FragmentHomeBottomSheetBinding.inflate(inflater, container, false)
return binding.root
}
fun successCategoryList(categoryList: ArrayList<ProdCategories>) {
binding.rvHomeCategories.visibility = View.VISIBLE
binding.rvHomeCategories.layoutManager = GridLayoutManager(context, 4)
binding.rvHomeCategories.setHasFixedSize(true)
val categoryAdapter = CategoryListAdapter(requireContext(), categoryList)
binding.rvHomeCategories.adapter = categoryAdapter
categoryAdapter.setOnClickListener(object :CategoryListAdapter.OnClickListener{
override fun onClick(position: Int, category: ProdCategories) {
val myFragment = HomeFragment()
val bundle = Bundle()
bundle.putString("category", category.category_name)
myFragment.arguments = bundle
fragmentManager?.beginTransaction()?.replace(R.id.nav_host_fragment,HomeFragment())?.commit()
}
})
}
}
Following is the adapter of the above fragment ('HomeBottomSheetFragment.kt'), 'CategoryListAdapter.kt'.
open class CategoryListAdapter(private val context: Context, private var list: ArrayList<ProdCategories>)
: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var onClickListener: OnClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return MyViewHolder(
HomeBottomsheetCategoryListLayoutBinding.inflate(
LayoutInflater.from(
parent.context
), parent, false
)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val model = list[position]
if (holder is MyViewHolder) {
GlideLoader(context).loadProductPicture(model.category_image, holder.binding.ivCategoryImage)
holder.binding.tvCategoryName.text=model.category_name
}
holder.itemView.setOnClickListener {
if (onClickListener != null) {
onClickListener!!.onClick(position, model)
}
}
}
override fun getItemCount(): Int {
return list.size
}
fun setOnClickListener(onClickListener: OnClickListener) {
this.onClickListener = onClickListener
}
interface OnClickListener{
fun onClick(position: Int, category: ProdCategories)
}
private class MyViewHolder(val binding: HomeBottomsheetCategoryListLayoutBinding) : RecyclerView.ViewHolder(binding.root)
}
Following is the fragment where I want to receive the data.
class HomeFragment : BaseFragment() {
private var filterCategory: String?= null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
val bundle = this.arguments
if (bundle!=null) {
filterCategory = bundle.getString("category")
}
return binding.root
}
}
All I know is that I am not doing things right, please help me fix it.
In this line,
fragmentManager?.beginTransaction()?.replace(R.id.nav_host_fragment,HomeFragment())?.commit()
Replace HomeFragment() with myFragment because that's where you are attaching the bundle.
See if this solves your problem.

I'm adding data to my list but it doesn't appear in RecyclerView (Kotlin, MVVM)

I am making a simple application. There are 2 screens in the application: List screen, Add screen.
Scenario: When the application is opened, there is a list page on the screen. The user goes to the Adding page with the help of the Floating Action Button on the list page. Here, he enters the items with the help of Edit Text and comes to the List page by pressing the Add Button.
I am using MVVM in this application and I am a beginner. So I can't find the problem. Here is the problem: I am adding new elements to my list in the Add Fragment, but these elements are not seems in the List Fragment.
Thanks in advance.
Model Class
data class Movie(
val movieName: String,
val releaseDate: String
)
ViewModel class
class MovieViewModel : ViewModel() {
val movies = MutableLiveData<ArrayList<Movie>>()
var movieList = arrayListOf<Movie>()
fun addMovie(movie: Movie) {
movieList.add(movie)
movies.value = movieList
}
}
Adapter Class
class MovieAdapter : RecyclerView.Adapter<MovieAdapter.MyViewHolder>() {
private var movieList = emptyList<Movie>()
class MyViewHolder(private val binding: RowItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(movie: Movie) {
binding.textViewMovieName.text = movie.movieName
binding.textViewReleaseDate.text = movie.releaseDate
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = RowItemBinding.inflate(layoutInflater, parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = movieList[position]
holder.bind(currentItem)
}
override fun getItemCount(): Int {
return movieList.size
}
fun setData(movie: List<Movie>) {
val movieDiffUtil = MovieDiffUtil(movieList, movie)
val movieDiffResult = DiffUtil.calculateDiff(movieDiffUtil)
this.movieList = movie
movieDiffResult.dispatchUpdatesTo(this)
}
}
Add Fragment
class AddFragment : Fragment() {
private var _binding: FragmentAddBinding? = null
private val binding get() = _binding!!
private val movieViewModel: MovieViewModel by viewModels<MovieViewModel>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentAddBinding.inflate(inflater, container, false)
binding.buttonAdd.setOnClickListener {
insertData()
}
return binding.root
}
private fun insertData() {
val movieName = binding.editTextMovieName.text.toString()
val releaseDate = binding.editTextReleaseDate.text.toString()
val movie = Movie(movieName, releaseDate)
movieViewModel.addMovie(movie)
findNavController().navigate(R.id.action_addFragment_to_listFragment)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
List Fragment
class ListFragment : Fragment() {
private var _binding: FragmentListBinding? = null
private val binding get() = _binding!!
private val adapter: MovieAdapter by lazy { MovieAdapter() }
private val movieViewModel: MovieViewModel by viewModels<MovieViewModel>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentListBinding.inflate(inflater, container, false)
setAdapter()
movieViewModel.movies.observe(viewLifecycleOwner, { data ->
adapter.setData(data)
binding.textView.setText(data.get(0).movieName)
})
binding.floatingActionButton.setOnClickListener {
findNavController().navigate(R.id.action_listFragment_to_addFragment)
}
return binding.root
}
private fun setAdapter() {
val recyclerView = binding.recyclerView
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireActivity())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
After adding an item to a List<> you need to notify the Adapter that the data has changed.
You can do so by calling adapter.notifyDataSetChanged().
I added my List directly into the Adapter but you can create a function like in the following stackoverflow post.
How to update my Recyclerview using kotlin android?