Fragment View is null when try to access from Activity - android

I have created a Fragment which is in ViewPager. Fragment is loading fine.
I am using a button to swipe fragments in viewpager and before swiping, I want to check validations. So I did this in Activity :
val pos: Int = viewPager.currentItem
val activeFragment: Fragment? = mAdapter.getItem(pos)
if (viewPager.currentItem == 0) {
if (pos == 0) (activeFragment as FragmentOne).checkValidation()
Below is my Fragment code.
class FragmentOne: BaseFragment() {
fun newInstance(): FragmentOne{
val args = Bundle()
val fragment = FragmentOne()
fragment.setArguments(args)
return fragment
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view : View = inflater.inflate(R.layout.fragment_one, container, false);
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initiateUI()
}
fun checkValidation(): Boolean {
if (TextUtils.isEmpty(etDateOfBirth.text.toString())) {
showMessage(etDateOfBirth, getString(R.string.str_empty_dob))
return false
}
return true
}
}
When I press next I get error:
java.lang.IllegalStateException: etDateOfBirth must not be null
Can anyone help out?

Didn't you define etDateOfBirth? Define edit text on onViewCreated()
EditText etDateOfBirth = view.findViewById(R.id.sample);

Related

Get reference of viewPager2 from child fragment

I have a fragment which contains a ViewPager2, It has 3 child fragments, I want to receive an reference viewpager in child fragment to be able to change viewPager programmatically. Since parent fragment which contains viewPager is not activity I can not access the viewPager with findViewById. On another question a usersuggested val pager = container as ViewPager2 but it's returning null. Can someone help me with this. As seen in code snippets, I am trying to get an reference to viewPager in BillingFragment from OneFragment.
Please let me know if you need to see ViewPagerAdapter code snippet.
Child fragment
class OneFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_billing, container, false)
val pager = container as ViewPager2
// ^^^^^ page is null
return view;
}
ViewPager main fragment
class BillingFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_billing_main, container, false)
setupStepView(view)
setupViewPager(view)
setupButtons(view)
return view
}
private fun setupStepView(view: View) {
view.stepView.state
// You should specify only stepsNumber or steps array of strings.
// In case you specify both steps array is chosen.
.steps(
listOf(
"First Step",
"Second Step",
"Third Step"
)
) // You should specify only steps number or steps array of strings.
// In case you specify both steps array is chosen.
.stepsNumber(3)
.animationDuration(resources.getInteger(android.R.integer.config_shortAnimTime))
.commit()
view.stepView.setOnStepClickListener { position ->
// viewPager.setCurrentItem(position, false)
}
}
private fun setupViewPager(view: View) {
// viewPager = view.viewPager;
view.viewPager.adapter = ViewPagerAdapter(childFragmentManager, lifecycle)
view.viewPager.registerOnPageChangeCallback(
object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
view.stepView.go(position, true)
setButtons(position, view)
}
}
)
}
private fun setupButtons(view: View) {
view.backButton.setOnClickListener {
view.viewPager.setCurrentItem(view.viewPager.currentItem - 1, false)
}
view.nextButton.setOnClickListener {
view.viewPager.setCurrentItem(view.viewPager.currentItem + 1, false)
}
}
private fun setButtons(position: Int, view: View) {
when (position) {
0 -> {
view.backButton.visibility = View.INVISIBLE
view.nextButton.visibility = View.VISIBLE
}
1 -> {
view.backButton.visibility = View.VISIBLE
view.nextButton.visibility = View.VISIBLE
}
2 -> {
view.backButton.visibility = View.VISIBLE
view.nextButton.visibility = View.INVISIBLE
}
}
}
}
Accessing the child fragment's view in parent fragment is not a good idea. If you want to trigger any action from the fragment. You can follow these steps:
class MyViewPagerAdapter(...) : FragmentStateAdapter(...){
lsit : List<Fragment>
fun getItem(position :Int) = list[position]
}
class OneFragment : Fragment(){
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_billing, container, false)
val position = viewPager2.currentItem
(adapter.getItem(position) as? BillingFragment)?.
return view;
}
}
class BillingFragment : Fragment(){
override fun onViewCreated(...){
...
}
// pass the data in the parameter that you want to use or update
fun updateViewPager(position : Int){
// perform your actions
view.viewPager.currentItem = position
}
}
Alternatives
If you know about EventBus, then you can use EventBus.
The android's default way is to use BroadcastReceiver.

Android Navigation Component saveState and restoreState

Guys I need your help.
I use android navigation component and want to save backstack after user press button and restore it after. I found 2 methods
navController.saveState(): Bundle and navController.restoreState(bundle: Bundle).
But i have problem in use it. Seems like saveState work greate (i see bundle, and backstack inside), but i dont understand how to use restoreState, because the documentation says:
Restores all navigation controller state from a bundle. This should be called before any call to setGraph.
https://developer.android.com/reference/kotlin/androidx/navigation/NavController#restorestate
Okay, i did it, seems like backstack restored, but on screen i see first fragment (instead of the one I had when I saved it). What i do wrong?
Code:
FirstFragment
private val TAG = this::class.java.name
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
btn_forward.setOnClickListener { findNavController().navigate(R.id.action_firstFragment_to_secondFragment) }
btn_back.setOnClickListener { requireActivity().onBackPressed() }
}
}
SecondFragment
class SecondFragment : Fragment() {
private val TAG = this::class.java.name
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
btn_forward.setOnClickListener { findNavController().navigate(R.id.action_secondFragment_to_thirdFragment) }
btn_back.setOnClickListener { requireActivity().onBackPressed() }
}
}
ThirdFragment
class ThirdFragment : Fragment() {
private val TAG = this::class.java.name
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_third, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
btn_finish.setOnClickListener {
(requireActivity() as MainActivity).saveState() //here save bundle
requireActivity().finishAfterTransition()
}
btn_back.setOnClickListener { requireActivity().onBackPressed() }
}
}
MainActivity
class MainActivity : AppCompatActivity() {
private val TAG = "MySuperActivity"
lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(TAG, "onCreate($savedInstanceState) called")
initNavController()
}
private fun initNavController() {
val navHostFragment = nav_host_fragment as NavHostFragment
val graphInflater = navHostFragment.navController.navInflater
val graph = graphInflater.inflate(R.navigation.main_graph)
navController = navHostFragment.navController
navHostFragment.childFragmentManager
if (App.instance.savedBundle != null) {
Log.d(TAG, "bundle: ${App.instance.savedBundle}")
navController.restoreState(App.instance.savedBundle)
graph.startDestination = R.id.thirdFragment
}
navController.graph = graph
Log.d(TAG, "navController.currentDestination: ${navController.currentDestination}")
Log.d(TAG, "navController.graph.startDestination: ${navController.graph.startDestination}")
}
fun saveState(){
App.instance.savedBundle = navController.saveState()
Log.d(TAG, "saveState() : ${App.instance.savedBundle}")
}
}
here some logs: logs
full code:github
I am not sure if my answer helps you, but I had many issues trying to save the navigation state from handling rotations. The issue that I had comes from an old version of the navigation component, I update to the most recent, and it fixes the issue:
def android_navigation = '2.3.4'
implementation "android.arch.navigation:navigation-fragment-ktx:$android_navigation"
implementation "android.arch.navigation:navigation-ui-ktx:$android_navigation"
implementation "androidx.navigation:navigation-dynamic-features-fragment:$android_navigation"

Listview adapter in a fragment giving null pointer exception

I made a listview in a fragment
There is a null pointer exception for the adapter of the list view
But the Arraylist and the adapter are not null.
Error: lvElevator cannot be null
The error is for the below line
lvElevator.adapter = EAdapter
Fragment Class
var EList=ArrayList<E1List>()
class EDetails: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
EList.add(E1List(1))
EList.add(E1List(2))
var EAdapter = EleAdapter(EList)
lvElevator.adapter = EAdapter
return inflater.inflate(R.layout.fragment_elevators, container, false)
}
inner class EleAdapter : BaseAdapter {
var EListAdapter = ArrayList<E1List>()
constructor(EListAdapter: ArrayList<E1List>) : super() {
this.EListAdapter = EListAdapter
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
var myView = layoutInflater.inflate(R.layout.elevator_ticket, null)
var myEle = EListAdapter[position]
myView.tvEnum.text = myEle.elevatornum.toString()
return myView
}
override fun getItem(position: Int): Any {
return EListAdapter[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return EListAdapter.size
}
}
}
You are trying to define lvElevator before fragment inflate the xml layout file.
There is two way to fix it.
1) Use onViewCreated() method instead to load data into views. And use onCreateView method to just inflate the view only.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
EList.add(E1List(1))
EList.add(E1List(2))
var EAdapter = EleAdapter(EList)
lvElevator.adapter = EAdapter
}
2) Modify onCreateView() method like below.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Define views manually..
val view = inflater.inflate(R.layout.fragment_elevators, container, false)
val lvElevator = view.findViewById<ListView>(R.id.lvElevator)
EList.add(E1List(1))
EList.add(E1List(2))
var EAdapter = EleAdapter(EList)
lvElevator.adapter = EAdapter
return view
}
As an experienced developer, I would recommended to use the first way as that method will only come in scope after layout completely inflated.

android kotlin all Fragments are called

I'm having problems that all my fragments are called when the application start so when I do toast on the the second fragment it appear on the first fragment
here is my activity code
class HomePage : AppCompatActivity() {
var adapter: FPA? = null
val manager = supportFragmentManager
private lateinit var drawer: DrawerLayout
private lateinit var toggle: ActionBarDrawerToggle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(null)
setContentView(R.layout.home_page_act)
adapter = FPA(supportFragmentManager)
container.adapter = adapter
container.addOnPageChangeListener(android.support.design.widget.TabLayout.TabLayoutOnPageChangeListener(tabs))
tabs.addOnTabSelectedListener(android.support.design.widget.TabLayout.ViewPagerOnTabSelectedListener(container))
}
class FPA(fm: FragmentManager) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
if(position==0 )
return TrainingForSale()
else if(position==1)
return Shops()
else
return Health()
}
override fun getCount(): Int {
return 3
}
}
fun ShowFramentShops (){
val transaction = manager.beginTransaction ()
val fragmet = Shops()
transaction.replace(R.id.container,fragmet)
transaction.addToBackStack(null)
transaction.commit()
}
}
and here is my fragment pages
fragment (1)
class Shops : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.shops_fragment, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onCreate(null);
//UserInformation.TrainingFilter=""
//UserInformation.TrainingList.clear()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(null)
Toast.makeText(activity,"Shopes",Toast.LENGTH_LONG).show()
}
}// Required empty public constructor
my second fragment
class Health : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.health_fragment, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onCreate(null);
super.onViewCreated(view, savedInstanceState)
Toast.makeText(activity,"Health", Toast.LENGTH_LONG).show()
}
}// Required empty public constructor
So when I run the the app both toast appears in the same page
The ViewPager will not only create the current page, but also the some before or after. With setOffscreenPageLimit(int) you can adapt these values a little.
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.

How do I correctly use Kotlin to call a blank fragment from the Home fragment?

class HomeFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val v = inflater.inflate(R.layout.fragment_home, container, false)
val fab = v.findViewById(R.id.fab) as FloatingActionButton
fab.setOnClickListener {
val blankFragment = BlankFragment()
val manager = childFragmentManager
manager.beginTransaction().replace(R.id.frame_container, blankFragment, blankFragment.tag).commit()
// System.out.println("You have reached the floating action button")
}
return v
}
}
Getting a no view found error. I may have issues with the R.id.frame_content but Kotlin doesn't immediately identify all id values...
This may not be the best way and I apologize for the horrible formatting I does this while answer out on my phone and it's hard to get it perfect. Anyways In the activity that holds your fragments, such as MainActivity.kt add...
supportFragmentManager.beginTransaction.add(R.id.fragment_home, HomeFragment.newInstance, "HomeFragment").commit()
In your HomeFragment.kt change the following.
class HomeFragment: Fragment(){
companion object {
fun newInstance(): HomeFragment{
return HomeFragment() }
fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater!!.inflate(R.layout.fragment_home, container, false)
val activity = activity
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val clickListener = View.OnClickListener { view ->
when (view.getId()) {
R.id.fab -> NextFragment()
}
}
fab.setOnClickListener(clickListener)
}
NextFragment(){
val fragManager = activity?.supportFragmentManager
fragManager?.beginTransaction()?.replace(R.id.frame_container, BlankFragment.newInstance(), "blankFragment")?.commit()
}
Make sure you make the same changes to BlankFragment.kt
class BlankFragment: Fragment(){
companion object {
fun newInstance(): BlankFragment{
return BlankFragment() }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater!!.inflate(R.layout.fragment_blank, container, false)
val activity = activity
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Do some stuff with your views in BlankFragment here
}
}

Categories

Resources