Kotlin Android start new Activity

Viewed 188479

I want to start another activity on Android but I get this error:

Please specify constructor invocation; classifier 'Page2' does not have a companion object

after instantiating the Intent class. What should I do to correct the error? My code:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun buTestUpdateText2 (view: View) {
        val changePage = Intent(this, Page2) 
        // Error: "Please specify constructor invocation; 
        // classifier 'Page2' does not have a companion object"

        startActivity(changePage)
    }

}
19 Answers

To start a new Activity ,

startActivity(Intent(this@CurrentClassName,RequiredClassName::class.java)

So change your code to :

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun buTestUpdateText2 (view: View) {
        startActivity(Intent(this@MainActivity,ClassName::class.java))

        // Also like this 

        val intent = Intent(this@MainActivity,ClassName::class.java)
        startActivity(intent)
    }

You can generally simplify the specification of the parameter BlahActivity::class.java by defining an inline reified generic function.

inline fun <reified T: Activity> Context.createIntent() =
    Intent(this, T::class.java)

Because that lets you do

startActivity(createIntent<Page2>()) 

Or even simpler

inline fun <reified T: Activity> Activity.startActivity() {
    startActivity(createIntent<T>()) 
} 

So it's now

startActivity<Page2>() 

Well, I found these 2 ways to be the simplest of all outcomes:

Way #1:

accoun_btn.setOnClickListener {
            startActivity(Intent(this@MainActivity, SecondActivity::class.java))
        }

Way#2: (In a generic way)

    accoun_btn.setOnClickListener {
        startActivity<SecondActivity>(this)
    }

    private inline fun <reified T> startActivity(context: Context) {
            startActivity(Intent(context, T::class.java))
        }

sample

From activity to activity

val intent = Intent(this, YourActivity::class.java)
startActivity(intent)

From fragment to activity

val intent = Intent(activity, YourActivity::class.java)
startActivity(intent)

How about this to consider encapsulation?

For example:


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_contents)

        val title = intent.getStringExtra(EXTRA_TITLE) ?: EXTRA_TITLE_DEFAULT

        supportFragmentManager.beginTransaction()
            .add(R.id.frame_layout_fragment, ContentsFragment.newInstance())
            .commit()
    }

    // Omit...

    companion object {

        private const val EXTRA_TITLE = "extra_title"
        private const val EXTRA_TITLE_DEFAULT = "No title"

        fun newIntent(context: Context, title: String): Intent {
            val intent = Intent(context, ContentsActivity::class.java)
            intent.putExtra(EXTRA_TITLE, title)
            return intent
        }
    }

another simple way of navigating to another activity is

Intent(this, CodeActivity::class.java).apply {
                    startActivity(this)
                }
fun Context.launchActivity(
    cls: Class<*>,
    flags: Int = 0,
    intentTransformer: Intent.() -> Unit = {}
) {
    val intent = Intent(this, cls).apply {
        addFlags(flags)
        intentTransformer()
    }
    this.startActivity(intent)
}
val intentAct: Intent = Intent(this@YourCurrentActivity, TagentActivity::class.java)
startActivity(intentAct)

I had a similar issue, I started to write my application in Kotlin, after I rewrote one of my activities I wanted to see if there are any issues, the problem was that I was not sure how to send an intent from java file to kotlin file.

In this case I created a static function in kotlin (companion object), this function is getting a context (from the current activity) and returning the new intent while using the current context ("java" context) while using the kotlin class ("::class.java").

Here is my code:

 //this code will be in the kotlin activity - SearchActivity
 companion object {

    fun newIntent(context: Context): Intent {
        return Intent(context, SearchActivity::class.java)
    }
}

    //this is how you call SearchActivity from MainActivity.java
Intent searchIntent = SearchActivity.Companion.newIntent(this);
startActivity(searchIntent);

extension Functions

fun Activity.showToast(message: String, toastLength: Int){
    //LENGTH_SHORT = 0;
    //LENGTH_LONG = 1;
    Toast.makeText(this, message, toastLength).show()
}

fun Fragment.showToast(message: String, toastLength: Int){
    //LENGTH_SHORT = 0;
    //LENGTH_LONG = 1;
    Toast.makeText(requireContext(), message, toastLength).show()
}

fun Context.launchActivity(
    cls: Class<*>,
    flags: Int = 0,
    intentTransformer: Intent.() -> Unit = {}
) {
    val intent = Intent(this, cls).apply {
        addFlags(flags)
        intentTransformer()
    }
    this.startActivity(intent)
}

in an activity call

showToast("message to be shown", 1)

in a fragment call

showToast("message to be shown", 1)

start activity from any place

 launchActivity(MainActivity::class.java, Intent.FLAG_ACTIVITY_NEW_TASK)

Kotlin Extension function

Details

  • Android Studio 3.1.4
  • Kotlin version: 1.2.60

Step 1. Application()

Get link to the context of you application

class MY_APPLICATION_NAME: Application() {

    companion object {
        private lateinit var instance: MY_APPLICATION_NAME
        fun getAppContext(): Context = instance.applicationContext
    }

    override fun onCreate() {
        instance = this
        super.onCreate()
    }

}

Step 2. Add Router object

object Router {
    inline fun <reified T: Activity> start() {
         val context =  MY_APPLICATION_NAME.getAppContext()
         val intent = Intent(context, T::class.java)
         context.startActivity(intent)
    }
}

Usage

// You can start activity from any class: form Application, from any activity, from any fragment and other  
Router.start<ANY_ACTIVITY_CLASS>()

Remember to add the activity you want to present, to your AndroidManifest.xml too :-) That was the issue for me.

You can use both Kotlin and Java files in your application.

To switch between the two files, make sure you give them unique < action android:name="" in AndroidManifest.xml, like so:

            <activity android:name=".MainActivityKotlin">
                <intent-filter>
                    <action android:name="com.genechuang.basicfirebaseproject.KotlinActivity"/>
                    <category android:name="android.intent.category.DEFAULT" />
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.genechuang.basicfirebaseproject.MainActivityJava"
                android:label="MainActivityJava" >
                <intent-filter>
                    <action android:name="com.genechuang.basicfirebaseproject.JavaActivity" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>

Then in your MainActivity.kt (Kotlin file), to start an Activity written in Java, do this:

       val intent = Intent("com.genechuang.basicfirebaseproject.JavaActivity")
        startActivity(intent)

In your MainActivityJava.java (Java file), to start an Activity written in Kotlin, do this:

       Intent mIntent = new Intent("com.genechuang.basicfirebaseproject.KotlinActivity");
        startActivity(mIntent);
Related