Originally by Beka Westberg
This document describes how to add a dropdown block to a component. Currently it focuses on upgrading old components, not creating new ones.
import com.google.appinventor.components.common.OptionList; |
Before defining your dropdown you need to make sure you are in the correct directory. This is because the component processor needs to get special information out of dropdowns that is only available if they are compiled first.
If you are working in the runtime directory:
components
﹂src
﹂com
﹂appinventor
﹂components
﹂annotations
﹂common
﹂runtime ← here!
﹂scripts
Then put your definition in the common directory:
components
﹂src
﹂com
﹂appinventor
﹂components
﹂annotations
﹂common ← here!
﹂runtime
﹂scripts
If you are working in your own extension directory:
components
﹂src
﹂MyDirectory ← here!
﹂com / google / appinventor / components / …
Create a new directory named “helpers” and put your dropdown definition there:
components
﹂src
﹂MyDirectory
﹂helpers ← here!
﹂com / google / appinventor / components / …
If you do not put your dropdown in the correct directory you will receive an error when you try to build the component that looks something like this:
OptionList Class: <classname> is not available. Make sure that it is available to the compiler.
Dropdowns are defined using special enums which implement the OptionList<T> interface. This interface requires that:
import com.google.appinventor.components.common.OptionList; }
|
→ For more information about defining enums see Java Enums and Java Enum String Example
If you are upgrading a component and using a dropdown to replace a set of constants the backing values should be the values of the constants.
For example if you have a function like this:
// Align takes in values: -1 (left) 0 (center) or 1 (right). |
Your enum should look like this:
import com.google.appinventor.components.common.OptionList; // These values are the values the Align function takes in.
|
The names displayed in your dropdown block are the names of your enum options.
This is slightly more complicated if you are creating a build-in component, because they support i18n. For more information see i18n.
The order of options in your dropdown is determined by the order of options in your enum.
Options at the top of your enum are displayed at the top of the dropdown list.
The default value of a dropdown is the value that is selected when the dropdown block is first created. By default the default value is the first enum constant. So in the following definition:
import com.google.appinventor.components.common.OptionList;
|
Lion is the default value.
You can change this by importing the @Default tag, and adding it to one of your enum constants options. In this definition:
import com.google.appinventor.components.common.OptionList; @Default
|
Giraffe is the default value.
Versioning means changing your enum/dropdown after you have already released a version of your component including the dropdown.
You deprecate an option when you no longer want users to use it. If someone has a project that is currently using that option the block will be highlighted in red:
But it will still generate code correctly. The option will also be removed from the dropdown so that users can no longer select it.
To deprecate an option simply add an @Deprecated tag to it.
import com.google.appinventor.components.common.OptionList;
|
Reordering the options of a dropdown is very simple. Just reorder them in your Java definition and that will be reflected by the blocks.
import com.google.appinventor.components.common.OptionList; Elephant("elephant"), Giraffe("giraffe"),
|
Internationalization support is only available for built-in components. You can internationalize both the “tag” of the dropdown block, and the actual options.
To internationalize the tag of a dropdown block add the following to the correct OdeMessages file:
<camelCaseTagName>OptionList = Translated Tag |
For example if I wanted to translate the Animal tag into Hungarian I would say:
animalOptionList = Állat |
To internationalize an option of a dropdown block add the following to the correct OdeMessages file:
<camelCaseTagName><OptionName>Option = Translated Option |
For example if I wanted to translate all of the Animal options into Hungarian I would say:
animalLionOption = Oroszlán |
This section is for people that have already released a component into the wild, and they would like to associate some existing properties/methods/events with dropdowns.
@DesignerComponent(version = 1,
@Options(Animal.class) String animal ) { |
The basic idea is that you take any parameter or return type that is the more primitive type (e.g. String, int, etc) and annotate it with an @Options annotation. You pass the annotation the dropdown definition you created in Part 1.
In this example we have a ZooOld component that we have already released. In the past we had users give it one of the strings “lion”, “giraffe”, or “elephant”. But now we would like them to be able to use a dropdown.
Our FavoriteAnimal property looked like this before:
@SimpleProperty |
And then we add annotations to make it look like this:
@SimpleProperty |
The annotation on the getter’s return type allows it to be compared against a dropdown.
And the annotation on the setter’s parameter allows it to accept a dropdown.
You should always include both.
Our ZooOld component has two methods. MakeDance has a void return type, and MakeRun has a String return type (which returns a String representing an animal).
Before our methods looked like this:
@SimpleFunction |
And then we add annotations to make it look like this:
@SimpleFunction |
The annotations on the functions’ parameters allows them to accept dropdowns.
And the annotations on the MakeRun function’s return type allows it to be compared against a dropdown.
Our OnHungry function looked like this before:
@SimpleEvent |
And then we add an annotation to make it look like this:
@SimpleEvent |
The annotation on the parameter adds a dropdown block to the variable setter in the flydown.
And the annotation allows that variable to be compared against a dropdown.
Including dropdowns in new components is even easier than including them in upgraded components.
@DesignerComponent(version = 1, |
All you have to do is use the enum/dropdown you defined in Part 1 instead of the more primitive type.
You cannot use this method to upgrade components you have already released because those components need to be backwards compatible.
Take the FavoriteAnimal function for example. If it previously accepted a string:
You want it to still accept that string, even after you’ve allowed it to also accept dropdowns.
New components don’t need this backwards compatibility. It’s actually better from an abstraction/design point of view if new blocks only accept dropdowns.
So if you try to upgrade your FavoriteAnimal function by changing the parameter type (instead of using an @Option annotation) this will break:
And your users won’t be happy :/
Instead of defining our FavoriteAnimal property using Strings (or another primitive type):
@SimpleProperty |
We define it using Animals:
@SimpleProperty |
This means that your setter can accept Animal dropdowns (and only Animal dropdowns)
And your getter can be compared against Animal dropdowns (and only Animal dropdowns)
Instead of defining our methods using Strings (or another primitive type):
@SimpleFunction |
We define them using Animals:
@SimpleFunction |
This means that your methods can accept Animal dropdowns (and only Animal dropdowns)
And that your MakeRun function can be compared against Animal dropdowns (and only Animal dropdowns)
Instead of defining our event using a String (or another primitive type):
@SimpleEvent |
We define it using an Animal:
@SimpleEvent |
This allows that variable to be compared against an Animal dropdown (and only an Animal dropdown).
And in the future it may allow a dropdown block to be included in the parameter’s flydown: