Launching activity from Fragment, Kotlin - android

I'm new to Kotlin/Android development and I'm making an app to display quizzes. Recently I decided to begin using fragments. On my MainActivity which has three fragments, I'd like one to have a method of clicking a subject and being taken to that particular quiz activity.
Note, there is only one quiz activity, but the intents pass a variable to display the relevant data for the quiz.
I had correctly implemented this when this page was not a fragment but struggling to find a solution this time.
Subject Fragment:
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.financialapp.InformationPage
import com.example.financialapp.databinding.FragmentModuleBinding
import android.content.Intent
class ModuleFragment : Fragment(com.quizapp.R.layout.fragment_module) {
private var _binding: FragmentModuleBinding ? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentModuleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val subjectOne = binding.tvEnglish
subjectOne.setOnClickListener {
sendIntent(0)
}
val subjectTwo = binding.tvGeography
subjectOne.setOnClickListener {
sendIntent(1)
}
val subjectThree = binding.tvHistory
subjectThree.setOnClickListener{
sendIntent(2)
}
...
}
private fun sendIntent(passedVariable: Int) {
val intent = Intent(this, SubjectPage::class.java)
intent.putExtra("subject", passedVariable)
startActivity(intent)
finish()
}
...
At present I have errors from Intent asking to create a function, same with finish().
Having looked through several tutorials I can't seem to see whether it's possible or not.

finish() is actually called on activity so you can use requireActivity() get hold of the hosting activity of your fragment & instead of using this in Intent params you can use requireContext()
Example:
private fun sendIntent(passedVariable: Int) {
val intent = Intent(requireContext(), MainActivity::class.java)
intent.putExtra("subject", passedVariable)
startActivity(intent)
requireActivity().finish()
}

Actually, as you started to use fragments you can avoid to start another activity.
Use childFragmentManager and just create for your quiz another fragment.
https://developer.android.com/reference/androidx/fragment/app/Fragment#getChildFragmentManager()

Related

FindNavController - None of the following candidates is applicable because of receiver type mismatch:

I've been doing this Android Studio course and I've stumbled upon a problem with this particular method. For whatever reason, I can use it in every fragment except one, in which apparently can't be imported.
Here is the fragment: (StartFragment)
package com.example.cupcake
import android.app.Fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.example.cupcake.databinding.FragmentStartBinding
/**
* This is the first screen of the Cupcake app. The user can choose how many cupcakes to order.
*/
class StartFragment : Fragment() {
// Binding object instance corresponding to the fragment_start.xml layout
// This property is non-null between the onCreateView() and onDestroyView() lifecycle callbacks,
// when the view hierarchy is attached to the fragment.
private var binding: FragmentStartBinding? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val fragmentBinding = FragmentStartBinding.inflate(inflater, container, false)
binding = fragmentBinding
return fragmentBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.apply {
// Set up the button click listeners
orderOneCupcake.setOnClickListener { orderCupcake(1) }
orderSixCupcakes.setOnClickListener { orderCupcake(6) }
orderTwelveCupcakes.setOnClickListener { orderCupcake(12) }
}
}
/**
* Start an order with the desired quantity of cupcakes and navigate to the next screen.
*/
fun orderCupcake(quantity: Int) {
findNavController().navigate(R.id.action_startFragment_to_flavorFragment) //here is the error
}
/**
* This fragment lifecycle method is called when the view hierarchy associated with the fragment
* is being removed. As a result, clear out the binding object.
*/
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
The whole code is in the code link, thanks
Your fragment extends android.app.Fragment - that's the deprecated, should never be used framework fragment. You need to replace your import android.app.Fragment with import androidx.fragment.app.Fragment to actually extend the AndroidX Fragment you need to use with Navigation.

snapHelper.findSnapView() always returns null

I'm trying to implement left and right buttons on the home Fragment of my app, which should scroll to the next card of my RecyclerView. To do this I need to determine the current position of the view so that I can smooth scroll to this position +-1. But helper.findSnapView(linearLayoutManager) always returns null. When this should be fetched there is a
I/ViewConfigCompat: Could not find method getScaledScrollFactor() on ViewConfiguration
in the logcat, which I think is causing the problem. But searching online doesn't yield anything. The code is below. Please try to help me to understand the issue.
package com.example.storybook
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSnapHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SnapHelper
import com.example.storybook.Model.Book
import com.example.storybook.Utils.MarginItemDecoration
import com.example.storybook.Utils.RecyclerViewAdapter
import kotlinx.android.synthetic.main.fragment.*
class Fragment : Fragment(), View.OnClickListener {
private var mBooks = mutableListOf<Book>()
private val helper: SnapHelper = LinearSnapHelper()
private val linearLayoutManager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val listOfBookFilenames: Array<String> = context?.assets?.list("PDFs")!!
for (filename in listOfBookFilenames) {
val tempBook = Book(filename)
mBooks.add(tempBook)
}
return inflater.inflate(R.layout.fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
books_recycler.apply {
layoutManager = linearLayoutManager
adapter = RecyclerViewAdapter(mBooks, context)
}
books_recycler.addItemDecoration(MarginItemDecoration(65))
helper.attachToRecyclerView(books_recycler)
left_button.setOnClickListener {onClick(left_button)}
right_button.setOnClickListener {onClick(right_button)}
}
override fun onClick(view: View) {
val snapView: View? = helper.findSnapView(linearLayoutManager)
var currentFocusPosition = RecyclerView.NO_POSITION
if (snapView == null) {
println("View is null")
} else {
currentFocusPosition = linearLayoutManager.getPosition(snapView)
}
when(view) {
right_button -> books_recycler.smoothScrollToPosition(currentFocusPosition + 1)
left_button -> books_recycler.smoothScrollToPosition(currentFocusPosition - 1)
}
}
}
I have already tried declaring the snapHelper and linearLayoutManagers locally, within the onViewCreated and onClick methods, and it seems to make no difference. This is my first ever app, so there might be a lot I don't understand.
Despite having tried every configuration several times, and rebuilding over an over again, I rebuilt just one more time with the above configuration and it just worked. Sometimes it works out that way.
If you're here from the future and you're having the same problem, try declaring your snapHelper and linearLayoutManager as private values outside the functions and try rebuilding again.

How do I us OnClickListener in a Tabbed Activity with Viewpager and Fragment Placeholder

I am trying to get a button to work correctly when it is clicked. for basic example I am using a chronometer. Using Android Studio 3.4.2, I can uses this code in a Basic Activity project in the Main Activity and it works fine. the same code does not work using a Tabbed Activity when I put it in the fragment, fragment page holder or main activity for my Tabbed Activity.
Main Activity of Basic Activity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity;
import android.view.Menu
import android.view.MenuItem
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
mport kotlinx.android.synthetic.main.content_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val chronometer = chronometer
val btnstart = (Button)rootView.findViewById(R.id.btnstart);
btnstart.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
chronometer.start()
}
})
}
Fragment of Tabbed Activity (no matter where i put it, it does not work)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_retention, container, false)
}
This is the Debug error i keep getting:
PID: 5241
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
}
the suggestions here do not seems to give me any positive results either
onClickListener doesnt work in Tabbed Activity Android
that did the trick. thank you. I placed this in the fragment under inflater as:
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_bph, container, false)}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val chronometer = chronometer
btnstart.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
chronometer.start()

Error declaring an Intent in from a Fragment in Kotlin:

I am trying to create an intent that uses a button within a fragment in the main activity to open a new activity. The error pops up underlining the 'Intent' object with a red line. The error is:
None of the following functions can be called with the arguments supplied.
(Context!, Class<*>!) defined in android.content.Intent
(String!, Uri!) defined in android.content.Intent
I tried the following code
val fieldIntent = Intent(this#Fragment1, ProductsAndInputs::class.java )
instead of the initial
val fieldIntent = Intent(this, ProductsAndInputs::class.java )
Based on an answer provided on a different forum but it also didn't work
class Fragment1 : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater!!.inflate(R.layout.fragment_fragment1, container, false)
view.btn_field.setOnClickListener { view ->
val fieldIntent = Intent(this#Fragment1, ProductsAndInputs::class.java )
Log.i("Button clicked", "Field button")
}
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
the issue is that the Intent constructor wants a context, but you're passing it a Fragment. But you can get a context from the Fragment: try this:
val fieldIntent = Intent(this#Fragment1.context, Products...
Oh, and you do need to call startActivity too, as Ajay said.
You need to add this code
val fieldIntent = Intent(this#Fragment1, ProductsAndInputs::class.java )
startActivity(fieldIntent)

Kotlin recyclerview gave me null pointer exception

i started learing Kotlin after using Java, curently trying to create recyclerview using Kotlin, but everytime reach this code:
recyclerview!!.layoutManager = LinearlayoutManager(context)
inside a fragment, it always returns null for me, and by converting existing Java Code using converter in android studio returns error.Can anyone help me why this is always happened? code below is my current fragment code:
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_notes.*
import java.sql.Timestamp
class NotesFragment : Fragment() {
companion object {
fun newInstance(): NotesFragment {
return NotesFragment()
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_notes, container, false)
val messageList : MutableList<message> = prepareMessage()
recycler_view!!.layoutManager = LinearLayoutManager(context)
recycler_view!!.adapter = NotesAdapter(context)
(recycler_view!!.adapter as NotesAdapter).setMessage(messageList)
return view
}
private fun prepareMessage(): MutableList<message> {
var messageList : MutableList<message> = ArrayList()
for(i in 0..10){
val timestamp: Long = System.currentTimeMillis()/1000L
val message = message(0,
"Judul Catatan ke $i",
resources.getString(R.string.lipsum),
timestamp
)
messageList.add(message)
}
return messageList
}
}
Thats because the views are not created yet in onCreateView. Since you are using kotlinx.android.synthetic you need to access it inside onViewCreated.Like this
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_notes, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
recycler_view = view.findViewById(R.id.your_recycler_id)
recycler_view?.layoutManager = LinearLayoutManager(context)
recycler_view?.adapter = NotesAdapter(context)
val messageList : MutableList<message> = prepareMessage()
(recycler_view?.adapter as NotesAdapter).setMessage(messageList)
}
Try this...
You should declare your recyclerview object, before accessing their properties.
.....
private var recycler_view: RecyclerView? = null
.....
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_notes, container, false)
recycler_view = view.findViewById(R.id.your_recycler_id)
recycler_view?.layoutManager = LinearLayoutManager(context)
recycler_view?.adapter = NotesAdapter(context)
val messageList : MutableList<message> = prepareMessage()
(recycler_view?.adapter as NotesAdapter).setMessage(messageList)
return view
}
have you declare your recylerview like this
polishrecyler = view .findViewById<View>(R.id.polish_recyler) as RecyclerView
I just now had a similar situation.
In my project, I have a RecyclerView working perfectly. In my case, it has five files involved: Foo.kt (the model), FooList.kt (the AppCompatActivity class), FooListAdapter.kt, foo_list.xml (the layout with the RecyclerView) and foo_list_row.xml (my custom row for the ReyclerView). Not sure if it's important or not, but the icon for Foo.kt is while the icon for the FooList.kt and FooListAdapter.kt is
I needed another RecyclerView, so I simply created five new files: Bar.kt, BarList.kit, BarListAdapter.kit, bar_list.xml and bar_list_row.xml. I copied/pasted the code from one to the other, and made the appropriate Foo->Bar changes in the code. This is when I got the same error as the OP. Looking in the import section, the import for RecyclerView was flagged as "superfluous". Odd.
Most of the suggested answers here is to declare the variable and use findViewById to reference it. But, my working RecyclerView doesn't do that, so why should it be necessary in the second RecyclerView? It doesn't make sense.
I continued to search and found this solution:
Save the code found in two files I had created manually (BarList.kit
(the AppCompatActivity file) and the 'corresponding' bar_list.xml)
Delete those files.
Right-click on the package name and select New > Activity > Empty Activity. This re-created the files I had just deleted with exactly the same names.
Restore the code saved from step 1 to the appropriate files.
Now both my RecyclerViews work correctly.
I guess when you use the New > Activity > Empty Activity, Android Studio does some work in the background which is not done if you just create the files manually.

Categories

Resources