I'm tring to pass the data of label to the LivePreviewActivity. Is there a better way to do this ? I don't know if the UIThread is working. I have also need the UIThread because i need to log the data continuously. For now the Log doesn't print on the console. My goal is accessing the label text on the Activity. Thanks. Please correct me if I'm doing this wrong. This is a quick-start project MLKit on Firebase. Any help will be wonderful. I have also need the UIThread because i need to log the data continiously.
class LivePreviewActivity : AppCompatActivity(), OnRequestPermissionsResultCallback,
OnItemSelectedListener, CompoundButton.OnCheckedChangeListener {
var labelName: String? = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_preview)
runOnUiThread {
Log.d("LivePreviewActivity", labelName)
}
}
ImageLabelProcessor
class ImageLabelingProcessor : VisionProcessorBase<List<FirebaseVisionImageLabel>>() {
val livePreviewActivity = LivePreviewActivity()
for (label in labels) {
livePreviewActivity.labelName = label.text
}
}
}
The typical way of passing values to Activities is via the intent you used to create the Activity. You shouldn't be creating Activities directly, as they have lifecycles.
val intent = Intent(context, LivePreviewActivity::class.java)
intent.putExtra(labelKey, labelValue)
where context is your current Activity, labelKey just a little const your will use for a lookup, labelValue your value you want to set.
and then in your activity's onCreate, you look up the extra using the bundle.
Hope this helps and that I understood your question correctly!
Related
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.
I am kind of new to Android. I can't figure this out. I want to create an object that is accessible from two different functions. Here is the object:
class Person(var firstName: String="", var lastName: String="", var order: List<Orders> )
class Order(var orderId: String="", var orderTitle: String="")
Then in an activity:
class MainActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var order: Order()
var person: Person(order) //I am sure I am not doing this right
}
fun Function1(){
person.order[1].orderTitle = "New Order" //to update order title
}
fun Function2(){
// to read new order title
var newOrderTitle = person.order[1].orderTitle
}
}
You created your Person instance as a local variable inside the onCreate() function, so it is only accessible inside the onCreate() function. To make it accessible from your other functions, it needs to be a property member of the class (defined outside any functions). You also need to use the = symbol to set the initial value. The : symbol is for declaring what type the property or variable is, and is optional in most cases.
By the way, in Kotlin, the convention is to always start function names with a lower-case letter, so it is easy to distinguish them from constructors. (This differs from languages like C#, where the new keyword makes constructor calls obvious.)
class MainActivity : AppCompatActivity(){
var order = Order()
var person = Person(order)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun function1(){
person.order[1].orderTitle = "New Order" //to update order title
}
fun function2(){
// to read new order title
var newOrderTitle = person.order[1].orderTitle
}
}
As well as what #TenFour04 says about making the variables visible to the functions, there's a couple of problems with how you're creating your Person object.
First, you're using default values for everything so you don't need to pass in a value for every parameter, right? That's how you can call Order() without providing any other data. But if you are passing in data, like with your Person(order) call, you need to tell it which parameter you're passing by using a named argument:
Person(order = order)
using the same name for the variable you're passing in and the name of the argument maybe makes it look more confusing, but you're specifically saying "the argument called order, here's a value for it".
You can pass in arguments without names, but you have to provide them in the order they're declared - so the 1st argument (a String), or the 1st and 2nd, or the 1st, 2nd and 3rd. Since you want to jump straight to the 3rd argument, you need to explicitly name it.
Second issue is your 3rd argument's type isn't Order, it's a List of orders. You can't just pass in one - so you need to wrap it in a list:
Person(order = listOf(order))
that's all you need to do!
The third problem is you've actually written the type as List<Orders> (sorry about the formatting). The type is Order, so we say List<Order> because it's a list holding objects of the Order type. You can use plurals in your variable names though (like val listOfOrders: List<Order>)
Objective:
I would like to modularize my code, but I am not well familiarized with Kotlin/Java -- considerable new to both languages, I was coding with React Native.
The following code is how I was making the model-view implementation. If anyone has a better suggestion in how to make a MVVM.
I try to do direct access as in the following code and I try to create instances of the CustomerModel() and CustomerActivity(), but it gives a black blank screen.
This CustomerModel will be responsible to connect to the Customer Database using firebase.
The CustomerActivity will ask to save info, load info and it will display infos using result form database consulting.
There will be other activities accessing CustomerModel (such settings activity, list view activity, ...)
How can I make this code working and better to be handling all Database stuff outside the View?
Issues:
The hasInfo is not being update. It is always false, and checking the Database is true for my test user
I received this error, I must be doing some mistake, but can't find out what:
java.lang.NullPointerException: You cannot start a load on a not yet
attached View or a Fragment where getActivity() returns null (which
usually occurs when getActivity() is called before the Fragment is
attached or after the Fragment is destroyed).
Apparently is happening on tring to update the TextView and the ImageView, maybe there is not created yet(my guess)
CustomerModel.kt (Model)
private lateinit var mCustomerDatabase: DatabaseReference
internal fun getUserInfo(uid: String) {
mUserDatabase = FirebaseDatabase.getInstance().reference.child("Users").child("Customers").child(uid)
mCustomerDatabase.addValueEventListener(object: ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists() && dataSnapshot.childrenCount > 0) {
val map: (Map < String, Any > ) = dataSnapshot.value as(Map < String, Any > )
if (map["profileImageUrl"] != null) {
Glide.with(CustomerActivity().application).load(map["profileImageUrl"].toString()).into(CustomerActivity().mNavigationHeaderImage)
}
if (map["name"] != null) {
CustomerActivity().mNavigationHeaderText.text = map["name"].toString()
}
if (map["hasCarInfo"] != null) {
CustomerActivity().hasInfo = map["hasInfo"].toString().toBoolean()
}
}
}
}
CustomerActivity.kt (View and Activity)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_activity)
(...)
// load all UI settings
handleUI()
// Update User Info
CustomerDatabase().getUserInfo(mAuth.currentUser!!.uid)
}
override fun onStart(savedInstanceState: Bundle?) {
super.onStart()
(...)
// Update User Info
CustomerDatabase().getUserInfo(mAuth.currentUser!!.uid)
}
mNavigationHeaderImage is imported from xml (ImageView)
mNavigationHeaderText is imported from xml (TextView)
hasInfo is a var that is used to check if the user has info or need to fetch by default is false
Your problem is that in your getUserInfo you try to update some text fileds of your activity using following notion
CustomerActivity().mNavigationHeaderText.text = map["name"].toString()
Problematic thing is that you are trying to create instance of your activity using CustomerActivity(). This is wrong, because in Android Instance of activities are created by the Android SDK.
So how do you use instance of your activity?
As you probably know activities has a certain Lifecycle, So after creating instance of your activity and initialization, Android calls lifecycle functions such as onCreate and onRestart, in those functions this refers to your activity's instance.
Ultimate solution to your problem is the LiveData, read this to learn about LiveData.
Following are two alternate solutions, both of these will work but if you can configure your project to use LiveData then avoid the following.
To solve your problem, a simple yet wrong thing would be to do the following, here you pass the instance of activity as a parameter to getUserInfo
CustomerDatabase().getUserInfo(this, mAuth.currentUser!!.uid)
Second approach would be to use callback interface. so in your activity define an interface as following
interface Callback {
fun onFinished(map: Map <String, Any> )
}
Now modify your activity to implement this interface, so in your activity you will have a function as following
onFinished(map: Map <String, Any> ){
// use map to set your data
}
After this modify your getUserInfo to take a parameter of type Callback, and this callback will be called by getUserInfo when it gets data.
internal fun getUserInfo(uid: String, callback: Callback) {
mUserDatabase = FirebaseDatabase.getInstance().reference.child("Users").child("Customers").child(uid)
mCustomerDatabase.addValueEventListener(object: ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists() && dataSnapshot.childrenCount > 0) {
val map: (Map < String, Any > ) = dataSnapshot.value as(Map < String, Any > )
/** When data is available, pass it to activity by calling the callback */
callback.onFinished(map);
}
}
}
I am trying to write a function in kotlin but I am not able reassign value to function parameters ,its saying val cannot be reassigned .
class WebView{
var homepage = "https://example.com"
fun webViewLoad(url: String, preferredOrientation: String) {
if (url.equals("homepage")){
url = homepage
}
}
}
when I am trying to assign a value to url = homepage .it is giving me error val cannot be reassigned , I am new to kotlin ,I do not understand what is the issue , little help will be appreciated.
Function parameters works like val variables that couldn't be reassigned. Here you need to add variable with conditional initialization:
fun webViewLoad(url: String, preferredOrientation: String) {
val urlValue = if (url.equals("homepage")){
homepage
} else {
url
}
... //use here "urlValue" variable
}
By the way, in kotlin you don't need to use equals function to compare string: common operator == will be automatically replaced with equals in byte code.
Kotlin parameters are immutable since Kotlin M5.1
(Reference)
The main reason is that this was confusing: people tend to think that this means passing a parameter by reference, which we do not support (it is costly at runtime). Another source of confusion is primary constructors: “val” or “var” in a constructor declaration means something different from the same thing if a function declarations (namely, it creates a property). Also, we all know that mutating parameters is no good style, so writing “val” or “var” infront of a parameter in a function, catch block of for-loop is no longer allowed.
It is giving you error "val cannot be reassigned" because Kotlin function parameters are immutable i.e "val" by default. You don't need to mention the "val" keyword for it.
Quick Solution would be:
class WebView{
var homepage = "https://example.com"
fun webViewLoad(url: String, preferredOrientation: String) {
val finalUrl = if (url.equals("homepage")) homepage else url
}
}
Kotlin function parameters are final. There is no val or final keyword because that's the default (and can't be changed). Have a look at this.
By default parameters passed in the function are final what you can do is to add var. Hope it helps.
fun webViewLoad(var url: String, preferredOrientation: String) {
if (url.equals("homepage")){
url = homepage
}
}
I have an AddressesViewModel which holds the user's favorite addresses and another SearchViewModel which holds searched addresses. when user searched an address I have to check whether this address is favorite or not by checking in the favorites array. what is the proper way to do it?
I've already tried subscribing to the AddressesViewModel from the SearchViewModel but I'm looking for other options as it's creating too much dependency between those view models.
Another alternative if I understand the question correctly.
Assuming that you first have this:
ViewModelChild(constructor etc) : ViewModelParent(){
// you can create a var/val to observe a variable in viewmodel parent.
// upon observation of
//this you can change other variables assigned here.
}
You will have to attach two ViewModels to the same lifecycle owner. eg You have an activity named MainActivity, two ViewModels named AddressesViewModel and SearchViewModel and you need to get a variable named address for SearchViewModel to AddressesViewModel
class MyActivity: AppCompactAvtivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
.
.
// Attach the ViewModels
val addressViewModel = ViewModelProviders.of(this).get(AddressesViewModel::class.java)
val searchViewModel = ViewModelProviders.of(this).get(SearchViewModel::class.java)
// Listen to address which is in SearchViewModel
searchViewModel.address.observe(this, Observer { address ->
// Send the variable to AddressesViewModel using a public method
val favOrNot addressViewModel.isAddressFavourite(address)
// or
addressViewModel.favouriteAddress = address
})
}
}
Hope this answers your question.