I want to add multilingual support to my app using the Android Studio Translations editor. But it only supports .xml strings. I have some strings in data class and I use data class for making ArrayList. I really want to call these strings from .xml but I couldn't figure out how.
my data class:
data class Infodc (
val id: Int,
val header: String,
val image: Int,
val infoOne: String,
val infoTwo: String
)
my list
object Constants{
fun getInfo(): ArrayList<Infodc>{
val infoList = ArrayList<Infodc>()
val inf1 = Infodc(
1, "header_str", R.drawable.image0,
"string_1", "string_2")
infoList.add(inf1)
return infoList
}
}
I tried R.string.string_header for header = "header_str" but that only show 10 digits, not the string itself.
After that, I tried getString but again I failed.
You can do it like this:
class MainActivity : AppCompatActivity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val infodc = Infodc(
1,
R.string.header,
R.drawable.ic_launcher_foreground,
R.string.info_one,
R.string.info_two,
this
)
Log.d("MyTag", "$infodc") // D/MyTag: Infodc(id=1, header=Header, image=2131099744, infoOne=Info One, infoTwo=Info Two)
}
}
data class Infodc(
val id: Int,
val header: String,
val image: Int,
val infoOne: String,
val infoTwo: String
)
{
constructor(
id: Int,
#StringRes header: Int,
#DrawableRes image: Int,
#StringRes infoOne: Int,
#StringRes infoTwo: Int,
context: Context
) : this(
id,
context.getString(header),
image,
context.getString(infoOne),
context.getString(infoTwo)
)
}
Now You can pass to a constructor id of String and image but as a class field You will get String
Another way is to have a class that only contains an id of string and then when You want to use it use getString of that field.
Related
imageSorry, I'm new to Java/Kotlin mobile apps...
Below code snippet from RegisterFragment.kt which is the main class:-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val username = binding.text
PackageSdk.getInstance().hasDuplicateUserKey(**username**, object : PackageResponseCallback<ResultResponse> {
override fun onSuccess(result: ResultResponse) {
Log.i(TAG, "Result code : " + result.rtCode)
}
override fun onFailed(errorResult: ErrorResult) {
Log.e(TAG, "Error code : " + errorResult.errorCode)
}
})
whereas below is the data class named RegisteredUserView.kt
data class RegisteredUserView(
val username: String
//... other data fields that may be accessible to the UI
)
I usually used toString() to the param value of "username" but I will get this bug
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference.
but if I just leave only the username, I will have "Type mismatch. Required:String Found: EditText" type of error. Do I have to create a function to pass value from data class to RegisterFragment class in order to have proper param input. If yes then how? If no then what way to assign param input? Btw, the
val username = binding.text
is fetch from layout_fragmentregister (an EditText) which its id = text
The error you are getting is because
val username: String
is null
your data class doesn't do much besides pass data along. You should use an instance of it and a view model to pass data to another fragment. A typical domain data class looks like this
data class User(
val userID: String,
val userName: String,
val emailAddress: String,
val photoUrl: String,
val firstName: String,
val lastName: String,
val age: Int,
val score: Int,
val sex: Int,
val emailVerified:Boolean)
to create an instance of the data class create a private var to store that data.
private var currentUser = User(someFunctionThatReturnsUserData)// the data must match the model
for test purposes and better understanding you can create a function with a test user
fun generateTestUser():User{return User("TEST ID","TEST NAME","TEST EMAIL")}
you can decide how to display the data in currentUser by accessing any of the properties. i prefer to do this in the view model as it removes a lot of logic from the UI thread and doesn't get reset on config changes.
currentUser.userId
currentUser.userName
currentUser.emailAddress
and so on. Usually best practice to create a private and public variables. Private to manipulate data, public to allow views to see the data but not modify it.
in your view model
private val _uid: MutableLiveData<String> by lazy { MutableLiveData<String>() }
val uid:LiveData<String> //this returns a string of live data for the ui to observe changes on.
get() = _uid //get the value from the private val
fun getUserId(){_uid.value = "some string"}
these are just examples and probably wont work with a simple copy/paste but hopefully helps a little.
when i try to initialize data class it gives me error, kotlin.UninitializedPropertyAccessException: lateinit property article_ has not been initialized. How do i resolve this error and whats way to initialize data class.
model class
data class Article(
val id: Int,
val author: String,
val content: String,
val description: String?)
MainActivity
class TestActivity : AppCompatActivity() {
lateinit var binding: ActivityTestBinding
lateinit var article_: List<Article>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_test)
val mainAdapter = MainAdapter(article_)
binding.recView.apply {
this.layoutManager = LinearLayoutManager(this#TestActivity)
this.adapter = mainAdapter
}
}
}
Change code like this
binding = DataBindingUtil.setContentView(this,R.layout.activity_test) article_ = ArrayList()
ANSWER:
In onCreate() before this line val mainAdapter = MainAdapter(article_) just initialize the article_ by writing article_ = ArrayList() and the error will be gone.
Cause of Error
You are encountering this error because You need to initialize the lateinit variable article_ before using it and you are not doing it.
If you want to check that either the variable is initialized or not then use ::article_.isInitialized.
Feel free to ask if something is unclear.
lateinit should only be used when you have a specific reason not to initialize the variable on construction. Here I can see no reason not to make it
var article_: List<Article> = listOf()
or perhaps
val article_: MutableList<Article> = mutableListOf()
and guarantee such error can't happen in the first place.
I need to pass this:
private lateinit var memes: MutableList<Memes>
which has this model:
class Memes (
#SerializedName("id") var id: Long,
#SerializedName("title") var title: String
)
from activity a to b.
I've altready seen couple "solutions" and none of them works!
This is the last that I've tried:
val extras = Bundle()
val memesArrayList = ArrayList(memes)
val i = Intent(context, GalleryShow::class.java)
i.putExtras(extras)
i.putStringArrayListExtra("list", memesArrayList)
(context as Activity).startActivityForResult(i, 777)
However, I get Type mismatch: inferred type is ArrayList<Memes!> but ArrayList<String!>? was expected on memesArrayList.
EDIT:
This is my latest attempt now:
In activity A inside recyclerview item:
val extras = Bundle()
extras.putString("gal", "animals")
extras.putString("query", "")
val i = Intent(context, GalleryShow::class.java)
i.putExtra("list", memes.toTypedArray())
i.putExtras(extras)
(context as Activity).startActivityForResult(i, 777)
and this is inside activity B:
private lateinit var memes: MutableList<Memes>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_gallery_show)
memes = (this?.intent?.extras?.get("list") as? Array<Memes>)?.asList()!!.toMutableList()
}
You can use simply intent.putExtra instead of worrying about which variant like put_____Extra to use.
When extracting the value, you can use intent.extras to get the Bundle and then you can use get() on the Bundle and cast to the appropriate type. This is easier than trying to figure out which intent.get____Extra function to use to extract it, since you will have to cast it anyway.
The below code works whether your data class is Serializeable or Parcelable. You don't need to use arrays, because ArrayLists themselves are Serializeable, but you do need to convert from MutableList to ArrayList.
// Packing and sending the data:
val i = Intent(context, GalleryShow::class.java)
i.putExtra("list", ArrayList(memes)) // where memes is your MutableList<Meme> property
startActivityForResult(i, 777)
// Unpacking the data in the other activity:
memes = intent.extras?.get("list") as MutableList<Meme>
i am trying to display data from API using retrofit2 in koltin
but the API shows that there is categoryId before the data i am calling as showing below:
[
{
"categoryId": 0,
"categoryName": "string",
"ilmFinders": [
{
"eventId": 0,
"eventName": "string",
"eventLocation": "string",
"eventPhoto": {},
"eventDescription": "string",
"eventDate": "string",
"eventLink": "string",
"isFavourite": {}
}
]
}
]
i tried to call only (eventName, eventPhoto, eventLink) in my data class as showing below:
data class IimfinderData(
val eventLink: String,
val eventPhoto: String,
val eventName: String
)
API interface:
interface finderService {
#GET(" ")
fun getServices() : Call<List<IimfinderData>>
companion object Factory {
fun create(): finderService {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("PRIVATE URL")
.build()
return retrofit.create(finderService::class.java)
}
}
}
Adapter:
class IimfinderAdapter(var countryList: List<IimfinderData>, var activity: MainActivity): RecyclerView.Adapter<IimfinderAdapter.ViewHolder>(){
lateinit var context: Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IimfinderAdapter.ViewHolder {
context = parent.context!!
val view = LayoutInflater.from(context).inflate(R.layout.list_item2, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return countryList.size
}
override fun onBindViewHolder(holder: IimfinderAdapter.ViewHolder, position: Int) {
holder.eventName.text = countryList[position].eventName
holder.eventLink.text = countryList[position].eventLink
Picasso.with(activity)
.load(countryList[position].eventPhoto)
.error(R.drawable.qiblacompass)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(holder.eventPhoto)
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val eventName: TextView = itemView.findViewById(R.id.title_tv2)
val eventLink: TextView = itemView.findViewById(R.id.tv_url2)
val eventPhoto: ImageView = itemView.findViewById(R.id.thumbnail_tv2)
}
}
after i run the application, it shows that the data are null
do i need to call "categoryId"?
and if yes, how can i call 2 arrays in the same data class?
the Adapter and the code in my activity is correct because i tested it with another API
so what am i doing wrong here?
------------------------------- Solution ------------------------
refer to the answer down and i have to change how i call the data from the adapter
instead of this:
holder.eventName.text = countryList[position].eventName
do this:
holder.eventName.text = countryList[position].ilmFinders[position].eventName
Check point
data that you want is in List of List
jsonArray > jsonArray > IimfinderDataJson
IimfinderData by List
eventPhoto is Object, not String
try this code
Call<List<IimfinderData>> -> Call<List<IlmFinders>>
fun getServices() : Call<List<IlmFinders>>
and
data class IlmFinders(
#SerializedName("ilmFinders")
val ilmFinders: List<IimfinderData>
)
data class IimfinderData(
#SerializedName("eventLink")
val eventLink: String,
#SerializedName("eventPhoto")
val eventPhoto: Any,
#SerializedName("eventName")
val eventName: String
)
if your data class properties name is same with json element, you don't need #SerializedName
As you are trying to access jsonArry in API response you can do as below:
Create data class for "IimfinderData" with serialization as:
data class ResponseData(
#SerializedName("name")
val userName: String,
#SerializedName("age")
val userAge: Integer,
#SerializedName("date_of_birth")
val userDOB: String)
than use this class as the list of data in data class as:
data class Response(
#SerializedName("userData")
val ilmFinders: List<ResponseData>)
access this data class with function as :
fun getServices() : Call<List<Response>>
Make sure to use #SerializedName Annotation if you are not using same name as response data
Hope this will solve your problem !!
I declared a data class in a Kotlin function, but the data is empty after gson conversion.
fun writeAndFlush(context: StateMachine) {
data class Temp(val model: TaskModel, val totalTime: String?, val state: String)
val temp = Temp(context.businessObj, context.totalTime, context.state.toString())
Log.e("test", temp.toString()) // print data here.
val json = Gson().toJson(temp)
Log.e("test", json) // problem here.....print null
}
Is there any problem with this way?