class AndroidWay extends Activity {
TextView name;
ImageView thumbnail;
LocationManager loc;
Drawable icon;
String myName;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name = (TextView) findViewById(R.id.name);
thumbnail = (ImageView) findViewById(R.id.thumbnail);
loc = (LocationManager)
getSystemService(Activity.LOCATION_SERVICE);
icon = getResources().getDrawable(R.drawable.icon);
myName = getString(R.string.app_name);
name.setText(myName);
}
}
The method onCreate is full of boilerplate code that makes maintaining painful. To reduce them, peoples thought about injection frameworks. Roboguice is one of them. It can improve the original code to the following way:
class RoboWay extends RoboActivity {
@InjectView(R.id.name) TextView name;
@InjectView(R.id.thumbnail) ImageView thumbnail;
@InjectResource(R.drawable.icon) Drawable icon;
@InjectResource(R.string.app_name) String myName;
@Inject LocationManager loc;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name.setText(myName);
}
}
Although it seems to be much improved, there are some drawbacks in this approach:
- Runtime performance
- Wiring verbose XML layout
- Not type-safe
class ScaloidWay extends SActivity {
lazy val name = findView(TR.id.name)
lazy val thumbnail = findView(TR.id.thumbnail)
lazy val icon = R.drawable.icon.r2Drawable
lazy val myName = R.string.app_name.r2String
lazy val loc = locationManager
onCreate {
contentView(R.layout.main)
name.setText(myName)
}
}
Lazy values replace injections, with no runtime performance degradation. And the code becomes much cleaner. However, this is not a final improvement. Another clutter that did not shown in this example is XML layout. It is too wordy to write a simple idea, and careful wiring with your code is mandatory. We can eliminate the source of the problem, by not writing layout in XML. The following example builds UI layout within an ordinary Scala code:
class ScaloidWay extends SActivity {
lazy val name = new STextView(R.string.app_name)
lazy val thumbnail = new SImageView()
onCreate {
contentView(new SVerticalLayout += name += thumbnail)
}
}
Less significant `lazy val loc` is omitted because we can use `locationManager` whenever it needed, and implicit conversion replaces the position of `myName` and `icon`.
How about dealing with different scree sizes? Using xml layout, I could just create several layout folders and forget (partially :) ) about it (as described here: http://developer.android.com/training/basics/supporting-devices/screens.html). Using your approach, I'll need to check screen size manually.
ReplyDeleteAs for runtime type checking in findViewById, in sbt-android-plugin you could create typed version of R (named TR) and then use it like following:
lazy val button = findView(TR.button)
It is better than find[TextView](R.id.name) but still require guessing on what Id should I use for current layout.
Thanks for a good suggestion. I revised the post to use typed resource (TR), and mentioned that the type-safety is its merit.
ReplyDeleteBecause Scaloid layout is an ordinary Scala code, assigning different layout for each screen dimension is not too hard. However, it would be much better that the library support it systematically. Supporting multiple screens is one of the biggest issues of next version. I am thinking about a design like this:
import Device._
contentView = device match {
case LARGE && LANDSCAPE => STextView("Large and landscape")
case LARGE => SEditText("Just large")
case SMALL && KO => STextView("작은 화면입니다")
case _ => SButton("Otherwise")
}
Looks cool!
Delete