RandomCoder
RandomCoder

Reputation: 405

TextView Id keeps calling on null no matter what way I try to change its content

I have a TabLayout with 3 fragments inside. Fragment 0 contains details, 1 is a description and 2 is location.

But no matter how I try to call the fragment Ids inside my carDetailsActivity or I try to pass the value from the activity to the fragments it keeps calling on null and giving the following result: Tab Fragment

enter image description here

Now I tried multiple ways one of them to Bundle the text value from the activity to the fragment but I still got null "IllegaleStateException". (which was the best option to my knowledge) but it still didnt work.

This is how I want it to look (the design is in arabic):

enter image description here

Here is my Activity where im calling them at the moment but I just get empty strings (had to give them ? because the app kept crashing if I didnt):

class CarDetailsActivity : BaseActivity() {
    val TAG = "CarDetailsActivity"

    override fun onCreate(savedInstanceState: Bundle?) {
        val car = intent.extras!!.getParcelable<Car>(DETAILS_TRANSFER) as Car
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_car_details)
        activateToolbar(true)

        carPriceDetails?.text = car.price.toString()
        carDate?.text = car.date
        carCategory?.text = car.category
        carModel?.text = car.brandModel
        carYear?.text = car.modelYear
        carKilometer?.text = car.kilometer.toString()
        carGear?.text = car.gearType
        carFuel?.text = car.fuelType

        val fragmentAdapter = FragmentCarInfo(supportFragmentManager)
        viewPager.adapter = fragmentAdapter

        tabLayout.setupWithViewPager(viewPager)


        carBrand.text = car.brand
        carModel.text = car.brandModel
        carYear.text = car.modelYear


        Picasso.with(this).load(car.image)
            .error(R.drawable.ic_cars)
            .placeholder(R.drawable.ic_cars)
            .into(carImage)

    }
}

Here is the main Fragment that contains that layout that contains the details:

class InfoFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_car_details_info, container, false)
    }
}

I have asked this similar question before but it was'nt clear enough so I hope this was a better way to post my problem.

-EDIT My tabLayout viewPager uses this

class FragmentCarInfo (fm : FragmentManager) : FragmentPagerAdapter(fm){

    override fun getItem(position: Int): Fragment {
        return when (position) {
            0-> {
                InfoFragment()
            }
            1 -> {
                AboutFragment()
            }
            else -> {
                return LocationFragment()
            }

        }
    }

    override fun getCount(): Int {
        return 3
    }

    override fun getPageTitle(position: Int): CharSequence? {
        return when (position) {
            0-> "Details"
            1-> "About"
            else -> {
                return "Locations"
            }
        }
    }
}

Upvotes: 0

Views: 154

Answers (2)

M&#39;aiq the Coder
M&#39;aiq the Coder

Reputation: 812

First of all, I would recommend ditching the old and nasty TabLayout and switch to the new ViewPager2 instead of the old ViewPager. UI wise, instead of the TabLayout, create custom 3 buttons inside a LinearLayout or ConstraintLayout.

Now, in your case I would use the ViewPager2 with a FragmentStateAdapter because it was designed to work well with Fragments and you can create as many of them as you need. I even had a project where I had 80 fragments created with a FragmentStateAdapter.

Second of all, you need a newInstance constructor for your fragments as a safe pattern. If you don't use that, you might get some exceptions later in your app, but other than that you will also use the function to pass your data to each fragment.

Here is how I do it:

class FragmentStateAdapter(
    fragmentManager: FragmentManager,
    lifecycle: Lifecycle,
    car: Car
) : FragmentStateAdapter(fragmentManager, lifecycle) {

    override fun getItemCount(): Int = 3

    override fun createFragment(position: Int): Fragment {
        return when (position) {
            0 -> InfoFragment.newInstance(car)
            1 -> AboutFragment.newInstance(car)
            else -> LocationFragment.newInstance(car)
    }
}

Basically I am passing your car object (presuming it's the only data object that needs to reach the fragments) as a parameter to the FragmentStateAdapter, locking the itemCount to 3 because you have 3 fragments and then returning an instance of each fragment in the override method createFragment(). Now, creating the instance functions:

class InfoFragment : Fragment() {

    companion object {
        private const val BUNDLE_KEY = "car_object"

        fun newInstance(car: Car) = InfoFragment().apply {
            arguments = Bundle().apply {
                putParcelable<Car>(BUNDLE_KEY, car)
            }
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_car_details_info, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         val car = arguments.getParcelable<Car>(BUNDLE_KEY)
    }
}

And this is how you get your car object inside your fragments. Basically in the newInstance() function you create a new instance of your fragment and pass data to it via bundle. Inside your activity this is how you initialise your adapter:

val adapter = FragmentStateAdapter(supportFragmentManager, lifecycle, car)

Now, about the replacement for the tab view, do a custom LinearLayout like this:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:weightSum="1.0">

<Button
        android:id="@+id/button1"
        style="@style/Widget.AppCompat.Button.Borderless"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="0.333"
        android:minWidth="0dp"
        android:minHeight="0dp"
        android:paddingTop="14dp"
        android:paddingBottom="14dp"
        android:text="Details"
        android:textAllCaps="true" />

<Button
        android:id="@+id/button2"
        style="@style/Widget.AppCompat.Button.Borderless"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="0.333"
        android:minWidth="0dp"
        android:minHeight="0dp"
        android:paddingTop="14dp"
        android:paddingBottom="14dp"
        android:text="About"
        android:textAllCaps="true" />

<Button
        android:id="@+id/button3"
        style="@style/Widget.AppCompat.Button.Borderless"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="0.333"
        android:minWidth="0dp"
        android:minHeight="0dp"
        android:paddingTop="14dp"
        android:paddingBottom="14dp"
        android:text="Locations"
        android:textAllCaps="true" />

and then set the initial color of the buttons after your own preference with android:background="#COLOR_OR_CHOICE"

In activity, add the three buttons in an ArrayList of buttons and use this listener on the viewPager2:

viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
 // here, get the position of the viewPager2 and change the color of the current button using the arrayList index that matches the viewPager2 position.

})

Then, on every button's click listener, do viewPager2.setCurrentItem(position_you_want_to_go_to, true) where true is a check for a smooth scroll.

Hope this helps.

Upvotes: 1

user1693251
user1693251

Reputation:

Shouldn't you be assigning the values to the fields in your fragment?

Upvotes: 0

Related Questions