I'm setting up my RecyclerView's adapter, and i need to use Activity Context to be filled in my viewmodel variable, and later i will using it to do move intent. Why i got Unresolved Reference even i put my context inside the class?
I've tried casting to FragmentActivity since the of() method asks for FragmentActivity, but still the same.
class TodoAdapter constructor(x:ArrayList<Notes>, c: Context) : RecyclerView.Adapter<TodoAdapter.Handler>() {
private var lists:ArrayList<Notes> = x
private var context:Context = c
private lateinit var viewmodel:TodoViewModel
class Handler(private val itemBinding:NotesListBinding): RecyclerView.ViewHolder(itemBinding.root) {
fun bind(note:Notes){
itemBinding.dataclass = note
itemBinding.viewmodel = ViewModelProviders.of(context).get(TodoViewModel::class.java)
itemBinding.notesCardView.setCardBackgroundColor(note.color)
}
}
I expect that context will be well referenced since it's on the same class. But it returns error like:
e: /media/cua/Projectah/Android Studio/TODOLisT/app/src/main/java/com/cua/todolist/adapter/recyclerviewadapter/TodoAdapter.kt: (24, 59): Unresolved reference: context
The context variable is unresolved, because Handler doesn't have a reference to TodoAdapter. To achieve this you'd have to declare it as inner class Handler. Though you should not do it!
Instead provide the viewmodel with bind:
fun bind(note: Notes, viewmodel: TodoViewModel) {
itemBinding.dataclass = note
itemBinding.viewmodel = viewmodel
itemBinding.notesCardView.setCardBackgroundColor(note.color)
}
Also consider setting the card background color within the data binding as well.
Related
I'm not very clear about the best way to inject into a static methods helper class (lets say a Custom class).
I'm kinda new to Kotlin, and as I've learnt we can access a method statically in two ways:
Object class.
Class + companion object.
To start, I'm not sure which one is the most recommended one (if there is a best practice regarding this), but my "problem" arises when needing to inject dependencies into a static method class.
Let's go with a simple example:
I have a static methods class called AWUtils (not decided if it should be an object class or a class with companion object yet though, and this will most likely depend on the injection mechanism recommended) with the next method:
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
app.drawerFooterColor + ";\">" + target + "</span>"
)
}
Here, app is the instance of my AppSettings class which holds all app configuration so, as you see setAmpersand2Yellow needs AppSettings, and of course I would't pass it as a parameter by any means, so it's a AWUtils dependence.
Using AWUtils as a class with companion object for the static methods I cannot inject directly AppSettings into company object as far as I know (at least I cannot do constructor injection, let me know if I'm wrong) and if I inject into companion object parent class (AWUtils) constructor then I don't know how to access those dependences from the companion object itself (the child).
If I use fields injection in AWUtils as a class then it complains than lateinit field has not been initialised and I don't know how to deal with this, because as far as I know lateinit fields are initialised in onCreate, which does not exist in this kind of classes.
One other possibility is to use an object with fields and set the dependencies values from caller in a static way before calling the method, for example:
object AWUtils {
var app: AppSettings? = null
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
app.drawerFooterColor + ";\">" + target + "</span>"
)
}
}
#AndroidEntryPoint
class OtherClass
#Inject constructor(private val app: AppSettings) {
fun AnyFunction() {
var mystr = "whatever"
AWUtils.app = app
var yellowStr = AWUtils.setAmpersand2Yellow(myStr)
}
}
In the end, I'm not sure on how to supply dependencies to a static methods class and which form of "static" class should I choose.
Edit 1:
Apart from my ApSettings class, I need a context, like for example in this next isTablet method:
val isTablet: String
get() {
return ((context.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
}
In the end, I need a context and my AppSettings (or any other custom classes) to be injected anyway in a class with static methods.
Edit 2:
I could do (from the activity):
AWUtils.context = this
AWUtils.app = app
var isTablet = AWUtils.isTablet
And it works, but rather to be in the need of assigning a value to two fields (or more) every time I need to call a static method, I would prefer the fields to be injected in any way.
That's what dependency injection is meant for, isn't it?
Edit 3: I'm starting to be fed up with Hilt, what is supposed would have been created to simplify our life, only makes our programming life much more complicated.
As you clarified in the comments, you want to have your utils class accessible in an easy way across your codebase, so this answer will focus on that and on your original questions.
I'm kinda new to Kotlin, and as I've learnt we can access a method statically in two ways: Object class or Class + companion object.
Kotlin does not have Java-style statics. One reasoning behind it was to encourage more maintainable coding practices. Static methods and static classes are also a nightmare for testing your code.
In Kotlin you would go with an object (but a class + companion object would work in the same way)
object AWUtils {
lateinit var appContext: Context
lateinit var appSettings: AppSettings
fun initialize(
appContext: Context,
appSettings: AppSettings,
// more dependencies go here
) {
this.appContext = appContext
this.appSettings = appSettings
// and initialize them here
}
val isTablet: Boolean
get() = ((appContext.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
appSettings.drawerFooterColor + ";\">" + target + "</span>"
)
}
}
Since this object should be accessible across the whole application it should be initialized as soon as possible, so in Application.onCreate
#HiltAndroidApp
class Application : android.app.Application() {
// you can inject other application-wide dependencies here
// #Inject
// lateinit var someOtherDependency: SomeOtherDependency
override fun onCreate() {
super.onCreate()
// initialize the utils singleton object with dependencies
AWUtils.initialize(applicationContext, AppSettings())
}
Now anywhere in your app code you can use AWUtils and AppSettings
class OtherClass { // no need to inject AppSettings anymore
fun anyFunction() {
val mystr = "whatever"
val yellowStr = AWUtils.setAmpersand2Yellow(myStr)
// This also works
if (AWUtils.isTablet) {
// and this as well
val color = AWUtils.appSettings.drawerFooterColor
}
}
}
There is another way in Kotlin to write helper/util functions, called extension functions.
Your isTablet check might be written as an extension function like this
// This isTablet() can be called on any Configuration instance
// The this. part can also be omitted
fun Configuration.isTablet() = ((this.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
// This isTablet() can be called on any Resources instance
fun Resources.isTablet() = configuration.isTablet()
// This isTablet() can be called on any Context instance
fun Context.isTablet() = resources.isTablet()
With the above extension functions in place the implementation inside AWUtils would be simplified to
val isTablet: Boolean
get() = appContext.isTablet()
Inside (or on a reference of) any class that implements Context, such as Application, Activity, Service etc., you can then simply call isTablet()
class SomeActivity : Activity() {
fun someFunction() {
if (isTablet()) {
// ...
}
}
}
And elsewhere where Context or Resources are available in some way, you can simply call resources.isTablet()
class SomeFragment : Fragment() {
fun someFunction() {
if (resources.isTablet()) {
// ...
}
}
}
Edit 3: I'm starting to be fed up with Hilt, what is supposed would have been created to simplify our life, only makes our programming life much more complicated.
Yeah, Hilt is focusing on constructor injection and can only do field injection out-of-the-box in very limited cases, afaik only inside Android classes annotated with #AndroidEntryPoint and inside the class extending the Application class when annotated with #HiltAndroidApp.
Docs for #AndroidEntryPoint say
Marks an Android component class to be setup for injection with the standard Hilt Dagger Android components. Currently, this supports activities, fragments, views, services, and broadcast receivers.
If you feel that you need a lot of field injection, because you are working with "static"-like objects in Kotlin, consider using Koin instead of Hilt for your next project.
I made a functions.kt file for global variables and I made this:
import android.app.Application
class variable : Application() {
var currentLesson: String? = null
}
After that, I used it in main.kt like so:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button: Button = findViewById(R.id.button1)
var functions = variable()
var currentLesson = functions.currentLesson
button.onClickListener {
currentLesson = "text"
}
}
override fun onBackPressed() {
someview: View =
findViewById(R.id.view1)
var functions = variable()
var currentLesson = functions.currentLesson
if (currentLesson == "text") {
someview.visibility = View.VISIBLE
}
}
}
In onBackPressed() it's always null. But not in onCreate(). Where is the problem?
Every time you call variable() you are creating a new instance of the class variable so it has its own memory and state that it's holding.
Incidentally, you should not be subclassing Application to create this class, since your simple data holder is not an Application!
If you want a class to hold some shared state for the whole app, that's commonly called a singleton (a class with only one instance), and you can easily create one in Kotlin using an object instead of class.
object Variable {
var currentLesson: String? = null
}
Then when you use it in your Activity, you can call it directly with Variable.currentLesson without having to create an instance of the class.
Alternatively, you can put the variable outside of any class, and it will be a global property that can be accessed from anywhere. In my opinion, that's kind of an ugly solution because it pollutes the namespace.
Note, you should be careful about what you store in global variables like this. It is not good practice to put large, memory-hungry objects in a global variable that will cause that memory to be used for longer than necessary.
Also, it is convention to make class names always start with a capital letter. It will make your code much easier to read and understand, especially in languages like Kotlin which omit the new keyword used for constructor calls.
For my code I am passing in this for the context. This is in mainActivity.kt file
This is the error I am getting in my constructor I am calling it like this
class ForecastAdapter(val forecast: Forecast, val context: Context) : RecyclerView.Adapter<ForecastData>(){
and then I am passing it in the class like this:
runOnUiThread {
view.adapter = ForecastAdapter(weather, this)
}
So I have no idea why this isn't working for context. I am new to Kotlin and new to android dev so I am little confused right now.
What you're observing is called SAM Conversion. Basically you're implementing a Runnable within your {} block. Therefore this refers to the inner class and to access the outer class you have to add the outer qualified scope this#MainActivity to it.
runOnUiThread { view.adapter = ForecastAdapter(weather, this#MainActivity) }
This is actually the same as
val runnable = Runnable { view.adapter = ForecastAdapter(weather, this#MainActivity) }
runOnUiThread(runnable)
I want to create a singleton class, but unfortunately, Android needs Context for almost anything so I need it to create an instance. So I just assumed the user called init(), and then return the instance. As you see below, if the _instance is null, an exception will be thrown, so the get method cannot return null.
But Kotlin says I must initialise instance. The things is, that MyClass cannot be created without a context. So I would like not to specify an initial value. How can I do that?
companion object
{
protected var _instance:MyClass? = null;
fun init(context:Context)
{
_instance = MyClass(context)
}
var instance:MyClass //<---This causes a compile error.
get()
{
if(_instance==null) throw RuntimeException("Call init() first.");
return _instance!!;
}
}
Change the var to val and it should work:
....
val instance: MyClass
....
A variable property (var) not only assumes a getter, but also a setter. Since you provided no setter, a default one was generated set(value) { field = value }. Despite is uselessness in this situation, the default setter uses field, thus requires its initialization.
Use lateinit property
public class MyTest {
lateinit var subject: TestSubject
fun setup() {
subject = TestSubject()
}
fun test() {
subject.method()
}
}
In Java and Android, we can do this:
public static MyApplication extends Application {
private static Context appContext;
public void onCreate() {
appContext = this;
}
public static Context getAppContext() {
return appContext;
}
}
so that, somewhere else, we can do this:
appContext = MyApplication.getAppContext();
How do we do this in Kotlin? I've been going round in circles for the past hour or so.
Thanks in advance.
//Edit
Perhaps I should have been clearer. I meant how can we write the above in Kotlin and use it in Kotlin.
In Kotlin this is called the 'companion object':
class MyApplication: Application {
companion object {
var appContext: Context? = null
private set
}
}
The key element I was missing was the use of an init block to set the appContext that is inside the companion object (I had already tried the companion object path but was struggling to actually get appContext set).
See code below:
class MyApplication : Application() {
init {
appContext = this
}
companion object {
lateinit var appContext: Context
private set
}
}
This is then callable as usual via:
val testContext = MyApplication.appContext
Assumed you have some java code in android and you want to convert it to kotlin code:
Visit Link
find Convert from java
it's help me to convert java code I've found on internet and converting it to kotlin code,
may this answer not help you about your question, but it would help you to convert what you know in java that you don't know how-to-do in kotlin
you can use it this way
companion object{
//your static fields
}
to call it from kotlin ==> ClassName.FieldName
to call it from java ==> ClassName.Companion.getFieldName()
It seems like you want only one object of this class at runtime. This is called a singleton. There are recommendations to implement that properly in Java. Luckily Kotlin directly allows you to declare singleton objects on the top scope:
val o = object { your attributes and methods here}