Search code examples
javaandroidkotlinanko

In the MainActivity java, how to call the anko class directly?


I used the anko library to create a login view.

class SingInView : AnkoComponent<SingleInActivity> {
   override fun createView(ui: AnkoContext<SingleInActivity>) = with(ui) {
       verticalLayout {
          lparams(width = matchParent, height = matchParent) 

          textView("Member Login")
          editText {
              hint = "E-mail"
          }
          editText {
              hint = "PassWord"
          }
          button("Login")
       }
    }
}

and SingleInActivity.kt

class SingleInActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState)
   SingInView().setContentView(this)

and MainActivity.java

public class MainActivity extends AppCompatActivity {

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       startActivity(new Intent(this, SingInView.class));
       finish();
   }
}

current My app MainActivity -> SingleInActivity -> SingInView .

of course it can be made simply.

but there is a condition 1. MainActivity is java (kotlin prohibition) 2. use only MainActivity, SingInView.

How to solve this problem?

How to call the Anko class directly from a Java class


Solution

  • If you dig through the Anko source code you'll quickly find this:

    interface AnkoComponent<in T> {
        fun createView(ui: AnkoContext<T>): View
    }
    

    And from the wiki (where MyActivityUI is the component): MyActivityUI().setContentView(this). Now, the AnkoComponent is just an interface and the setContentView method is an extension function that returns createView.

    Anyways, the setContentView extension function passes the last variable of the AnkoContextImpl as true. The last variable is whether or not to actually set the content view, which is the reason the activity is passed in the first place.

    TL;DR (and possibly more sensible summary of my point):

    • The component is not an Activity
    • The setContentView method is not a replacement for setContentView in an Activity; just a wrapper for it.

    And since it isn't an activity, you can't use an intent into it. And, as a result of that, you cannot use it standalone. You need an activity. Now, you can of course use the regular approach, but there's also another way. Since the AnkoComponent itself doesn't have any fields, it can be serialized without much trouble. Just to clarify: some fields can be serialized even if it isn't serializable (all though some classes like Context cannot be serialized). Anyways, you create an activity:

    class AnkoComponentActivity : AppCompatActivity(){//Can be a regular Activity too
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState);
            val component = intent.getSerializableExtra("uiComponent") as AnkoComponent<AnkoComponentActivity>//The type has to match this activity, or setContentView won't allow passing `this`
            component.setContentView(this)//The context of the activity doesn't get passed until this point, which enables the use of this system. 
        }
    }
    

    Or it's equivalent in Java:

    public class AnkoComponentActivity extends AppCompatActivity {
    
        public void onCreate(Bundle sis){
            super.onCreate(sis);
            AnkoComponent<AnkoComponentActivity> component = (AnkoComponent<AnkoComponentActivity>) getIntent().getSerializableExtra("uiComponent");
            org.jetbrains.anko.AnkoContextKt.setContentView(component, this);//For reference, this is how you call Kotlin extension functions from Java
        }
    }
    

    Note that any UI component sent to this class has to be declared with <AnkoComponentActivity>. In addition, the components have to implement Serializable. Otherwise they can't be passed through the Bundle. Alternatively, you can use ints or Strings as identifiers and use the value to pick which AnkoComponent to show.

    All though, the absolutely easiest way is just creating one activity per component.

    TL;DR: AnkoComponent is not an Activity, meaning you can't use intents into it. You have to use an Activity, but using Serializable enables you to pass the component through a bundle to an Activity made for manual creation of multiple AnkoComponents without specifying specific types.