Issue with Android Studio and Main Activity.kt expecting member declaration - android

I have been trying to learn through following YouTube tutorials. I am using Android Studio 3.1 Canary and I get to the same point in the tutorials and get stuck. For instance if you go to this YouTube tutorial https://youtu.be/3RMboPhUbmg?t=210 at the 3:30 min mark.
When they are inputting the MaterialSearchView searchView; it shows up for me with a red underline saying "expecting member declaration" and no matter how many searches I try I cannot find an answer. What is the solution to this error? Thanks
This is the code contained in the Main2Activity.kt. The result should be calling or knowing the toolbar and materialsearchview objects
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.Toolbar
import com.miguelcatalan.materialsearchview.MaterialSearchView
import kotlinx.android.synthetic.main.activity_main2.*
class Main2Activity : AppCompatActivity () {
**MaterialSearchView searchView;** "expecting member declaration error"
**Toolbar toolbar;** "expecting member declaration error"
Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
toolbar=(Toolbar()) findViewbyId(R.id.toolbar);
setSupportActionBar(toolbar);
}
*private void searchViewCode()
{
searchView=(MaterialSearchView)findViewById(R.id.search_view);
}
}

1) Understand your language syntax
Your tutorial is in Java. You try to write in Kotlin. Java and Kotlin have different syntax and if you reproduce this tutorial word for word in Kotlin, it will fail.
Follow the turorial and write your code in Java. Switch to Kotlin later when you're more confident with what you're doing. Focus on one thing at a time.
2) Find views at the right time
The Activity object is instatiated for you by the framework with a public empty constructor. At this time there are no views to be found. If you call findViewById any time before setContentView it will return null and crash if you try to assign it to a non-nullable variable.
The above applies for both Java and Kotlin.
For Java follow the tutorial. It should look something like this:
Toolbar toolbar; // Declare the variable here so it's accessible outside of onCreate.
#Override
public void onCreate(#Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2); // Inflate view hierarchy.
toolbar = (Toolbar) view.findViewById(R.id.toolbar); // Find your views.
setSupportActionBar(toolbar);
}
In Kotlin there are several options.
You can use lateinit modifier which allows you to declare a non-nullable variable but assign it later in onCreate.
lateinit var toolbar: Toolbar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
toolbar = findViewById(R.id.toolbar) as Toolbar
setSupportActionBar(toolbar)
}
Or you can use lazy delegate. The variable will be assigned when you first access it.
val toolbar: Toolbar by lazy(LayzThreadSafetyMode.NONE) {
toolbar = findViewById(R.id.toolbar) as Toolbar
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
setSupportActionBar(toolbar)
}
Don't use the delegate. It creates an unnecessary holder object for each lazy, that's wasteful.
You can also use Kotlin Android Extensions and just access toolbar directly because all of the heavy lifting is done for you.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
setSupportActionBar(toolbar)
}
Alternatively you can use View Binding available since Android Studio 3.6 Canary 11.
private lateinit var binding: ActivityMain2Binding
#Override
fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ActivityMain2Binding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
}
You get high performance of the first option and strong type safety on top.

It seems you are declaring your searchView and toolbar variables using Java syntax, and not the Kotlin syntax, so the compiler does not understand what you are declaring.
So change the declaration to:
var searchView: MaterialSearchView? = null
val toolbar: Toolbar = view.findViewById(R.id.toolbar) as Toolbar
or if you are using Kotlin Android extensions you should be able to retrieve the toolbar like so (using the view id directly):

In my case, the problem was a invisible char due copy and paste from .PDF
Note that there are two chars between '{' and 'onClick' in the snippet below.
setOnClickListener { ​onClick(item) }
This is the cause of my nightmares.

The main problem is the syntax, Whenever upgrade 3 series to 4. Java syntax transform to Kotlin syntax.
Kotlin Syntax:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
}
}
Java Syntax:
#Override
public void onCreate(#Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2); // Inflate view hierarchy.
}
}
If you want to change Kotlin syntax to Java Syntax.
Came back to mainActivity programming panel after that
TOOLS -> KOTLIN -> Show KOTLIN Bytecodes
Then Check on the JVM 8 target and Tick that.
After that invalid catch/Restart
Problem has been resolved.

Related

UninitializedPropertyAccessException: lateinit property binding has not been initialized

I am getting a random crash "lateinit property binding has not been initialized". Most of the time it's working fine but a few time randomly we are getting this crash on crashlytics.
Please let me know what's wrong here
I have a BaseActivity with following code
abstract class BaseActivity<D : ViewDataBinding> : AppComptActivity() {
abstract val layoutId: Int
lateinit val binding: D
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState:Bundle)
binding = DataBindingUtil.setContentView(this, layoutId)
....
}
}
I have a HomeActivity which override BaseActivity with following code
class HomeActivity : BaseActivity<ActivityHomeBinding>() {
override val layoutId: Int get() = R.layout.activity_home
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState:Bundle)
....
}
}
I am using bottomNavigation menu and one of the fragment is HomeFragment
class HomeFragment : BaseFragment<FragmenntHomeBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState:Bundle)
(activity as HomeActivity).binding.appBarHome.visible(false)
//HERE I AM GETTING lateinit property binding has not been initialized crash
}
}
I don't want to use isInitialized property of lateinit as this will not solve my issue
As mentioned in the comment, I'd suggest instead of calling parent container (Activity) objects directly, register a listener to a navigation change like this in HomeActivity:
navController.addOnDestinationChangedListener { controller, destination, arguments ->
if(destination.id = R.id.homeFragment) {
// TODO hide/show your view here
}
}
In that case, you are sure that the view gets hidden/shown when it should be without relying on the HomeFragment being only in HomeActivity as this can change in the future and your app will start crashing
If you have an orientation change or other config change, or the OS process is killed while in the background and the user returns to the app, Android will recreate the Activity and the Fragments.
Unfortunately, it creates the Fragments first, before creating the Activity. So you cannot rely on the existence of the Activity until the Fragment has been attached to the Activity. You should move code that relies on the existence of the Activity to
onActivityCreated().
Note: I also agree with the comment about not doing it this way. Your Fragment should not make assumptions like this (that it is hosted by HomeActivity), but instead should make some callback to the hosting Activity and let the hosting Activity set the visibility of the app bar (or whatever else it wants to do).

Android findViewById() returns null when trying to make a utility/helper class

I am currently developing an app and I am trying to make a Utils file with some general functions that I can use throughout my application to avoid having to copy/paste the same function many times. I have a Utils class file and I am passing the current Activity into that class. For some reason, when I call findViewById() in init, it returns null and the app crashes. I have tried logging the activity that is being passed into the Utils class, and to my eyes it appears like the right activity, so I am not sure why it is failing. I will show the relevant code below:
Utils.kt (There is more, but it isn't relevant to the question)
class Utils(activity: Activity) {
private var currentActivity: Activity
private var fragmentManager: FragmentManager
private var topAppBar: MaterialToolbar
val activitiesList = listOf(
"recipes", "budget", "inventory", "customers",
"reports"
)
init {
currentActivity = activity
println(currentActivity)
fragmentManager = (activity as AppCompatActivity).supportFragmentManager
topAppBar = currentActivity.findViewById(R.id.top_app_bar)
}
}
MainActivity.kt (Again there is more, but not relevant)
class MainActivity : AppCompatActivity() {
private lateinit var drawerLayout: DrawerLayout
private lateinit var topAppBar: MaterialToolbar
private lateinit var utils: Utils
private val fragmentManager = supportFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fragmentManager.beginTransaction().replace(R.id.frame_layout, HomeFragment()).commit()
utils = Utils(this#MainActivity)
topAppBar = findViewById(R.id.top_app_bar)
drawerLayout = findViewById(R.id.drawer_layout)
val navigationView: NavigationView = findViewById(R.id.navigation_view)
The error comes from the Utils.kt file in the last line of actual code where I try to assign topAppBar to the view of id top_app_bar, but it returns null. My println (yes I know I should be using Log.i but println is quicker to type) in the Utils class returns com.example.bakingapp.MainActivity#501533d, which is what I would expect if I am understanding what is happening correctly. Any insight as to why I can't find the views would be appreciated.
I figured out the problem. It is a scope issue. I am trying to find the id of a view outside of the current Activity/Fragment I am looking at. I realized then a lot of my code could/should be moved into the Fragment I was trying to target and this has fixed my issues.

Why .text only works inside onCreate ? At least at my code :

I am learning android studio and I can't find out why it happens, I would appreciate if someone could explain me this:
When I place xxxxanything.text after the onCreate I get the error " expected member declaration" but it works inside the onCreate metod.Why does it happen ?
I saw activity life cycle some times but im still in doubt about where to put things, like onclick listener.
I were wondering at several guides already and I am working on udacity at moment, having a hard time to understand recyclerview and I also trying to develop good programing practices.
I really appreciate any help you can provide.
Works like this
package app.helloworld.dashimir.com.diceroller
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.roll_button)
rollButton.text = "Let's Roll"
}
}
but i get error when i place it after on create : expected member declaration;
package app.helloworld.dashimir.com.diceroller
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
val rollButton: Button = findViewById(R.id.roll_button)
rollButton.text = "Let's Roll"
}
In kotlin (.kt) files your code should be inside a function (normally main). In Android development this means that usually your code will be inside methods like the onCreate from an activity, but other methods from classes or top level functions (outside of a class) work as well.
In your example the code you moved outside the onCreate includes a value declaration val rollButton: Button = findViewById(R.id.roll_button) which is valid inside the body of a class since it will be turned into a property of said class. But the second line: rollButton.text = "Let's Roll" is an assignment an those can only be performed inside a function.
Additionally, in Android with kotlin you have available kotlin android extensions which let you reference views from the xml directly using their id without the need of using findViewById
One of best ways it build out function.
first: add OnClick to button in xml file:
android:OnClick="OnClick"
second: build Onclick function in MainActivity class and don't forget the constructor (v:View):
package app.helloworld.dashimir.com.diceroller
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun OnClick(v:View){
val rollButton: Button = findViewById(R.id.roll_button)
rollButton.text = "Let's Roll"
}

How can I pass data to the first Fragment whilst using the Navigation Architecture?

I'm trying to pass a bundle of object instances down from my main activity to the first fragment in a chain of other fragments using the NavHostFragment. I've tried all sorts but the bundle always seems to be null once it reaches the first fragment.
Here's how I'm initiating the NavHostFragment (frameContainer is a Frame Container element in my layout xml)
NavHostFragment navHost = NavHostFragment.create(R.navigation.claim_nav_graph);
getSupportFragmentManager().beginTransaction()
.replace(R.id.frameContainer, navHost)
.setPrimaryNavigationFragment(navHost)
.commit();
The documentation says there are 2 different .create functions, one of them you can pass a second arguments to as a bundle, but Android Studio doesn't allow me to use this version.
Does anyone have any ideas?
Thanks in advance!
It does seem to be a flaw with the NavHostFragment, passing data down to the first fragment does not seem to be possible, as the Bundle you can set as a second argument on the create function is overwritten along the way.
In the end I resolved this by building the bundle in the first fragment of the activity instead. I was able to access the activities intent properties using the below.
// Kotlin
activity.intent?.extras?.getBundle(KEY_BUNDLE_ID)
// Java
getActivity().getIntent().getBundleExtra(KEY_BUNDLE_ID)
This was enough of a workaround for me in this situation, but it would be great if it was possible
If you're using viewModels, you can do this:
your viewmodel:
class NiceViewModel: ViewModel() {
var dataYouNeedToPass = "initialValue"
}
your activity:
class MainActivity : AppCompatActivity() {
val niceViewModel: NiceViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
niceViewModel.dataYouNeedToPass = "data You Need To Pass"
}
}
your fragment:
class YourFragment : Fragment() {
private lateinit var niceViewModel: NiceViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
niceViewModel = (activity as MainActivity).niceViewModel
niceViewModel.dataYouNeedToPass //do whatever you need to do with this
}
}

Streamlined Adding and Replacing Fragments in Kotlin

I am working on a small App with just a few screens that I want to implement as a single Activity which calls several fragments. I have some experience in Java for Android but using Kotlin is new territory.
I have understood how to basically add fragments in Kotlin, but I'd like to do it in a reusable way.
I came across the solution mentioned here and it works, but there is something I don't quite understand. My following MainActivity works so far, but I have to create the var welcomeFragment in the onCreate method like I did it here:
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val toggle = ActionBarDrawerToggle(
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
drawer_layout.addDrawerListener(toggle)
toggle.syncState()
nav_view.setNavigationItemSelectedListener(this)
var welcomeFragment = Fragment.instantiate(this#MainActivity,
WelcomeFragment::class.java!!.getName())
addFragment( welcomeFragment, welcome)
}
inline fun android.support.v4.app.FragmentManager.inTransaction(func: android.support.v4.app.FragmentTransaction.() -> android.support.v4.app.FragmentTransaction) {
beginTransaction().func().commit()
}
fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int){
supportFragmentManager.inTransaction { add(frameId, fragment) }
}
fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int) {
supportFragmentManager.inTransaction{replace(frameId, fragment)}
}
}
I don't seem to get why I can't declare the property and later assign it. It's possible to declare it with lateinit but when I then try to assign it like this:
var welcomeFragment = Fragment.instantiate(this#MainActivity,
WelcomeFragment::class.java!!.getName())
I get "Type mismatch - Required: Fragment, found: WelcomeFragment!"
Also, if I try to do this:
addFragment(WelcomeFragment, welcome)
Android Studio complains about WelcomeFragment ´(which is right now just empty except for a TextView saying "Welcome") not having a companion object. I have tried to make one but I don't really understand what it has to do. Reading the documentation didnt help me either with this one.
So my question is: How do I do the instantiating right, and or do I have to do it at all when most of my fragments will be one instance only. Can I avoid this by properly adding a companion object to my fragments? How would that look?
Any help on this, hints at misconceptions I have here, or general stupidity pointed out would be greatly appreciated!
Please be careful with the imports, sometimes we have a android.support.v4.app.Fragment
and then we can't use android.app.Fragment.instantiate function in it.

Categories

Resources