环境搭建:
- 安装JDK和Scala(本人使用2.1)
- 下载Android SDK,最新版本里面已经包含了Eclipse和 ADT插件
- 根据Eclipse版本和Scala版本下载对应的Scala IDE插件。http://download.scala-ide.org/
- 安装AndroidProguardScala,地址https://androidproguardscala.s3.amazonaws.com/UpdateSiteForAndroidProguardScala,安装过程可能会升级Ecplise版本,自动的。
- 创建Android Project
- 增加Scala特性,在工程的右键菜单,Scala菜单项下点击Add Scala Nature按钮
- Add AndroidProguardScala Nature,在android项目上点右键,Add AndroidProguardScala Nature
开始Android Scala之旅:
- 使用Scala Class重写 MainActivity。
class MainActivity extends Activity { override def onCreate( bundle :Bundle) { super.onCreate(bundle) setContentView(R.layout.activity_main) val btn = findViewById(R.id.button1).asInstanceOf[Button] val txt = findViewById(R.id.editText1).asInstanceOf[EditText] btn.setOnClickListener( new View.OnClickListener(){ def onClick( v : View) { txt.setText("Main Activity Using Scala.") } }) }}
实例代码,模仿Java的风格,用Scala的代码重写了MainActivity类。这是使用Scala编写Android的最基本的方式,但是Scala的特性无法得到应有的发挥,这种方式编写android和使用Java几乎是一样的。下面我使用Scala的风格来重写MainActivity方法。
最简单的Scala风格,上面的实例代码中,使用的是接口来处理事件,在Scala中,一般使用函数,而不是接口。
让我在MainActivity类中增加一个隐士转换方法,后的代码。
class MainActivity extends Activity{
/*隐士转换方法*/
implicit def fun2ClickHandler( fun : View=>Unit) = new View.OnClickListener() { def onClick(v : View) = fun(v) } override def onCreate( bundle : Bundle) { super.onCreate(bundle); this.setContentView(R.layout.activity_main); val btn = findViewById(R.id.button1).asInstanceOf[Button] val txt = findViewById(R.id.editText1).asInstanceOf[EditText] btn.setOnClickListener( (v :View)=>{ txt.setText("使用Scala隐士转换1") }) } }
上面的代码,发现,在处理Click方法时,一般都不需要使用到View参数,因此试着把该参数给去掉。后的效果
class MainActivity extends Activity{ implicit def fun2ClickHandler( fun : ()=>Unit) = new View.OnClickListener() { def onClick(v : View) = fun() } override def onCreate( bundle : Bundle) { super.onCreate(bundle); this.setContentView(R.layout.activity_main); val btn = findViewById(R.id.button1).asInstanceOf[Button] val txt = findViewById(R.id.editText1).asInstanceOf[EditText] btn.setOnClickListener( ()=>{ txt.setText("使用Scala隐士转换1") }) } }
不过还有一点别扭,就是在设置btn.btn.setOnClickListener方法的时候多了 ()=>,如果能把这个去掉该多好,这里需要使用到Scala的传名方法,当把lambal当做函数参数时,并且该lambal函数没有接收任何参数时,可以把()给省略。进一步修改后的代码如下:
class MainActivity extends Activity{ implicit def fun2ClickHandler( fun : =>Unit) = new View.OnClickListener() { def onClick(v : View) = fun } override def onCreate( bundle : Bundle) { super.onCreate(bundle); this.setContentView(R.layout.activity_main); val btn = findViewById(R.id.button1).asInstanceOf[Button] val txt = findViewById(R.id.editText1).asInstanceOf[EditText] btn.setOnClickListener( { txt.setText("使用Scala隐士转换1") }) } }
btn.setOnClickListener( { txt.setText("使用Scala隐士转换1") }) 代码开起来还是有点奇怪,如果可以把()去掉,只留{...}就更完美了,还记得Scala中,如果方法只有一个参数,可以使用{}替代(),因此上面的代码btn.setOnClickListener( { txt.setText("使用Scala隐士转换1") })完全可以修改为btn.setOnClickListener { txt.setText("使用Scala隐士转换1") },自己测试吧。
重构,以便复用。在上面的所有代码中,隐士方法(implicit)是在MainActivity中定义的,到了其它地方就不能使用了,因此很有必要封装一个可以复用的。
可以定义一个RichButton类和其伴生类。
import android.widget.Buttonimport android.view.Viewimport android.app.Activityclass RichButton(val button : Button){ def onClick( handler : =>Unit) { button.setOnClickListener( new View.OnClickListener(){ def onClick(arg0 : View) { handler } }) } }object RichButton { implicit def button2RichButton(button: Button)= new RichButton(button)}
让后在MainActivity中,导入该类即可。
import android.app.Activityimport android.os.Bundleimport android.widget.Buttonimport RichButton._import android.widget.EditTextimport android.view.View
class MainActivity extends Activity{ override def onCreate( bundle : Bundle) { super.onCreate(bundle); this.setContentView(R.layout.activity_main); val btn = findViewById(R.id.button1).asInstanceOf[Button] val txt = findViewById(R.id.editText1).asInstanceOf[EditText] btn onClick { txt setText "button click,Using Scala By ID!!!" } } }
上面的代码,应该说是非常Scala的风格了,大部分情况下,按钮都是用来设置一个事件处理方法,如果可以像
R.id.button1 onClick { txt setText "button click,Using Scala By ID!!!" }
这样注册事件处理方法就更完美了。想法完全可以,不过需要额外的Scala特性的支持,那就是implicit参数。
首先需要在RichButton的伴生类中增加如下方法:
implicit def button2RichButton(id : Int)(implicit cur_activity : Activity)= { new RichButton(cur_activity.findViewById(id).asInstanceOf[Button]) }
上面的方法定义了一个implicit参数,调用该方法的地方有一个叫cur_activity的参数。这既是个隐士方法,同时还需要用户提供隐士参数。太多隐士(implicit)了,头不要被弄晕了。
让后在Mainactivity类中,新增一个隐士变量(又来一个隐士)。
class MainActivity extends Activity{ implicit var cur_activity :Activity = null override def onCreate( bundle : Bundle) { cur_activity = this super.onCreate(bundle); this.setContentView(R.layout.activity_main); // val btn = findViewById(R.id.button1).asInstanceOf[Button] val txt = findViewById(R.id.editText1).asInstanceOf[EditText] R.id.button1 onClick { txt setText "button click,Using Scala By ID!!!" } } }
上面代码,定义了一个隐士变量implicit var cur_activity :Activity = null,并且在onCreate中对齐进行了初始化。
至此,已经使用了非常Scala的风格来编写android代码了。当然,很小的程序,写起来比Java的代码还多,但是随着工程的变大,使用Scala的代码量是相当少的,至于少多少,用过了就知道了。30%~50%那是很正常的。10%,说明,你还在使用Java风格来编写代码。