New to android and kotlin here. Making my first app and I'm trying to use the getPageTitle
function to give my tabs their titles (of which are string resources). The full implementation is as follows:
class FAAMainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar as Toolbar)
val pagerAdapter = SectionsPagerAdapter(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
pager.adapter = pagerAdapter
tabs.setupWithViewPager(pager)
}
private class SectionsPagerAdapter : FragmentPagerAdapter{
constructor(fm: FragmentManager, behavior: Int) : super(fm, behavior)
override fun getItem(position: Int): Fragment {
when (position) {
0 -> return HomeFragment()
1 -> return KittensFragment()
2 -> return CatsFragment()
3 -> return FosterersFragment()
4 -> return FAAUsersFragment()
else -> {
return HomeFragment()
}
}
}
override fun getCount(): Int {
return 5
}
override fun getPageTitle(position: Int): CharSequence? {
when(position) {
0 -> Resources.getSystem().getText(R.string.home_tab)
1 -> Resources.getSystem().getText(R.string.kitten_tab)
2 -> Resources.getSystem().getText(R.string.cat_tab)
3 -> Resources.getSystem().getText(R.string.fosterer_tab)
4 -> Resources.getSystem().getText(R.string.faa_user_tab)
else -> "Error"
}
return "Error"
}
}
}
Trying to run the application gives the following error:
java.lang.RuntimeException: Unable to start activity ComponentInfo{uk.ac.aber.dcs.cs31620.faa/uk.ac.aber.dcs.cs31620.faa.ui.FAAMainActivity}: android.content.res.Resources$NotFoundException: String resource ID #0x7f0d0027
I don't understand why it cannot find the String resource.
My strings.xml;
<resources>
<string name="app_name">Feline Adoption Agency</string>
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="home_tab">Home</string>
<string name="kitten_tab">Kittens</string>
<string name="cat_tab">Cats</string>
<string name="fosterer_tab">Fosterers</string>
<string name="faa_user_tab">FAA Users</string>
</resources>
More information as I am putting a bounty on this:
app/build/generated/not_namespaced_r_class_sources/debug/r/uk/ac/aber/dcs/cs31620/faa/R.java
file correctly.app/build/generated/not_namespaced_r_class_sources/debug/r/androidx/appcompat/R.java
and I am not sure that is the cause of the issue. My imports for the FAAMainActivity
class are:
import android.content.res.Resources import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentPagerAdapter import kotlinx.android.synthetic.main.activity_main.* import uk.ac.aber.dcs.cs31620.faa.R import uk.ac.aber.dcs.cs31620.faa.ui.cats.CatsFragment import uk.ac.aber.dcs.cs31620.faa.ui.faa_users.FAAUsersFragment import uk.ac.aber.dcs.cs31620.faa.ui.fosterers.FosterersFragment import uk.ac.aber.dcs.cs31620.faa.ui.home.HomeFragment import uk.ac.aber.dcs.cs31620.faa.ui.kittens.KittensFragment
I've uploaded the project here if anyone want's to try it out.
Calling Resources.getSystem()
provides System level resources and not your application level resources as per the doc:
Return a global shared Resources object that provides access to only system resources (no application resources), and is not configured for the current screen (can not use dimension units, does not change based n orientation, etc).
You'll need application or activity level context in order to retrieve the strings from your strings.xml. I have changed your SectionsPagerAdapter
to this in order to fix your error:
private class SectionsPagerAdapter(fm: FragmentManager, behavior: Int, private val context: Context) :
FragmentPagerAdapter(fm, behavior) {
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> HomeFragment()
1 -> KittensFragment()
2 -> CatsFragment()
3 -> FosterersFragment()
4 -> FAAUsersFragment()
else -> {
HomeFragment()
}
}
}
override fun getCount(): Int {
return 5
}
override fun getPageTitle(position: Int): CharSequence? {
return when(position) {
0 -> context.getString(R.string.kitten_tab)
1 -> context.getString(R.string.kitten_tab)
2 -> context.getString(R.string.cat_tab)
3 -> context.getString(R.string.fosterer_tab)
4 -> context.getString(R.string.faa_user_tab)
else -> context.getString(R.string.home_tab)
}
}
}
FAAMainActivity
val pagerAdapter = SectionsPagerAdapter(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, this) // Passed "this" as context
Here, I have primarily changed following things in your code,
From
private class SectionsPagerAdapter : FragmentPagerAdapter{
constructor(fm: FragmentManager, behavior: Int) : super(fm, behavior)
...
}
To
private class SectionsPagerAdapter(fm: FragmentManager, behavior: Int, private val context: Context) :
FragmentPagerAdapter(fm, behavior) {
...
}
I have changed your JAVA style constructor to kotlin's primary constructor and added Context
as third parameter. Now we will use that Context
to get the string instead of Resources.getSystem()
.
Use
Resources.getSystem().getString(R.string.faa_user_tab)
instead of
Resources.getSystem().getText(R.string.faa_user_tab)
As stated in the documentation of Resources , the method getSystem() :
Return a global shared Resources object that provides access to only system resources (no application resources), is not configured for the current screen (can not use dimension units, does not change based on orientation, etc), and is not affected by Runtime Resource Overlay.
So you cannot access your application resources by this method. But looking at your code you can simply change
private class SectionsPagerAdapter
to:
private inner class SectionsPagerAdapter
and when you want to get the string instead of
Resources.getSystem().getString(R.string.kitten_tab)
do this:
getString(R.string.kitten_tab)
Explanation: When you mark a class as inner
you are making every method along with other things in outer class visible and accessible in the inner class . In every Activity (any Context ) there are methods for accessing resources. One of them is getString
which you can simply use to get your strings by their id.
I know I'm not answering your question directly but the issue can be solved by changing the way you create/use the adapter.
The main change is that instead of letting the adapter decide how to populate the view we give that "responsibilty" to the user.
We do that by requesting a list of pages, in this example I used a Pair<String, Fragment>
but you can create an actual data model with all the necessary information you need.
class SectionsPagerAdapter(
fragmentManager: FragmentManager,
behavior: Int,
val pages: List<Pair<String, Fragment>>
) : FragmentPagerAdapter(fragmentManager, behavior) {
override fun getItem(position: Int): Fragment = pages[position].second
override fun getCount(): Int = pages.size
override fun getPageTitle(position: Int): CharSequence = pages[position].first
}
And when you are creating the adapter you create it inside your Activity/Fragment
like this
val pages = listOf( /* Change the list to fit your exact needs*/
context.getString(R.string.kitten_tab) to HomeFragment(),
context.getString(R.string.kitten_tab) to KittensFragment(),
context.getString(R.string.cat_tab) to CatsFragment(),
context.getString(R.string.fosterer_tab) to FosterersFragment(),
context.getString(R.string.faa_user_tab) to FAAUsersFragment()
)
val adapter = SectionsPagerAdapter(
supportFragmentManager,
FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,
pages
)
This way, you do not only solve the problem, but you also achive a scalable solution that supports not only this use case but any other use case
The code that works with your method are:
Resources.getSystem().getText(android.R.string.cancel) // returns Cancel
Resources.getSystem().getString(android.R.string.cancel) // returns Cancel
Resources.getSystem().getResourceName(android.R.string.cancel)) //returns android:string/cancel
Resources.getSystem().getResourceEntryName(android.R.string.cancel)) returns cancel
But in all the cases the string used starts with android.R that means you can access only the androids built in resources using these methods.
I dont know why you are trying to access your string resource using the above method.
Below I have listed certains methods to access your app related resources:
getResources().getText(R.string.app_name)); // returns Test App
getResources().getString(R.string.app_name)); // returns Test App
getResources().getResourceEntryName(R.string.app_name)); // returns app_name
getResources().getResourceName(R.string.app_name)); //returns com.example.testapp:string/app_name
context.getText(R.string.app_name)); // returns Test App
context.getString(R.string.app_name)); // returns Test App
Also you can use the context to access the methods starting with getResources() as well.
I think you are calling the string resources in a wrong way. it should be something like this:-
override fun getPageTitle(position: Int): CharSequence? {
when(position) {
0 -> getString(R.string.home_tab) // just call getString() bcz, calling from AppCompatActivity
1 -> getString(R.string.kitten_tab) // if you use fragment and try to call
2 -> getString(R.string.cat_tab) // getString(), use resources.getString(...)
3 -> getString(R.string.fosterer_tab)
4 -> getString(R.string.faa_user_tab)
else -> "Error"
}
return "Error"
}
Try this, hope your problem will be solved. Let me know if any issue arises further. Happy coding.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.