我最近采用了一种方式of programming for Android using Scala and Eclipse,它可以在不使用Proguard或Treeshake的情况下减少代码和编译时间。
在本文之后,我应该能够使用最后一个Eclipse构建(3.7),几乎是在模拟器版本10上更新的Scala(2.8.1)的最新版本,Eclipse中的版本2.8.3以及提供的插件英寸
提供的方法是提供特定的ramdisk映像版本,我们可以上传scala库,这会大大缩小上传到模拟器的代码大小。
我按照步骤,创建了一个hello world,添加了scala本质,添加了一个虚拟scala类,在Android Package Installer之前移动了Scala构建器,一切都构建完美,但是当我在Eclipse的模拟器上启动apk时,应用程序崩溃,我得到以下错误,看起来像 same as presented here(在文件末尾):
03-29 10:29:38.505: E/AndroidRuntime(839): java.lang.NoClassDefFoundError: upg.TestSinceInstallation.ComputeSum
如果我删除了活动文件中的scala引用,它运行良好。
这是TestSinceInstallation.java文件:
package upg.TestSinceInstallation;
import android.app.Activity;
import android.os.Bundle;
import upg.TestSinceInstallation.ComputeSum;
public class TestSinceInstallationActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int a = 1;
int b = 5;
ComputeSum cs = new ComputeSum(a, b);
if(cs.getResut() == 6) {
setContentView(R.layout.main);
}
}
}
这是ComputeSum.scala文件
package upg.TestSinceInstallation
class ComputeSum(a: Int, b: Int) {
def getResut() : Int = a + b
}
你认为我应该做些什么来完成这项工作?我觉得这个目标非常接近。
答案 0 :(得分:56)
以下是使用Android Eclipse 3.7和Scala 3.0.0时没有任何问题的解决方案。
https://androidproguardscala.s3.amazonaws.com/UpdateSiteForAndroidProguardScala
现在,要创建一个scala项目,
Configure
,Add Scala nature
Add AndroidProguardScala nature
你已经完成了。
现在好事发生了。 首先,您可以对任何活动进行scalafy,并且您将可以访问scala的独特功能,例如:
以下是其中一些例子。
package com.example.testing;
import android.app.Activity
import android.os.Bundle
import scala.collection.mutable.Map
import android.view.View
import android.widget.SeekBar
import android.widget.ImageButton
import android.graphics.drawable.Drawable
import android.widget.TextView
trait ActivityUtil extends Activity {
implicit def func2OnClickListener(func: (View) => Unit):View.OnClickListener = {
new View.OnClickListener() { override def onClick(v: View) = func(v) }
}
implicit def func2OnClickListener(code: () => Unit):View.OnClickListener = {
new View.OnClickListener() { override def onClick(v: View) = code() }
}
private var customOnPause: () => Unit = null
override def onPause() = {
super.onPause()
if(customOnPause != null) customOnPause()
}
def onPause(f: =>Unit) = {
customOnPause = {() => f}
}
private var customOnCreate: Bundle => Unit = null
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
if(customOnCreate != null) customOnCreate(savedInstanceState)
}
def onCreate(f: Bundle => Unit) = {
customOnCreate = f
}
// Keep references alive when fetched for the first time.
private implicit val vMap = Map[Int, View]()
private implicit val ibMap = Map[Int, ImageButton]()
private implicit val sbMap = Map[Int, SeekBar]()
private implicit val tvMap = Map[Int, TextView]()
private implicit val dMap = Map[Int, Drawable]()
def findView[A <: View](id: Int)(implicit v: Map[Int, A]): A = v.getOrElseUpdate(id, findViewById(id).asInstanceOf[A])
def findDrawable[A <: Drawable](id: Int)(implicit v: Map[Int, A]): A = v.getOrElseUpdate(id, getResources().getDrawable(id).asInstanceOf[A])
implicit class RichView(b: View) { // Scala 2.10 !
def onClicked(f: =>Unit) = b.setOnClickListener{ () => f }
}
// Implicit functions to operate directly on integers generated by android.
implicit def findViewImageButton(id: Int): ImageButton = findView[ImageButton](id)
implicit def findViewSeekBar(id: Int): SeekBar = findView[SeekBar](id)
implicit def findViewTextView(id: Int): TextView = findView[TextView](id)
implicit def findDrawable(id: Int): Drawable = findDrawable[Drawable](id)
implicit def findRichView(id: Int): RichView = toRichView(findView[View](id))
}
现在在完成所有以前的样板后,编写简洁的活动非常有用。请注意我们如何直接对id进行操作,就像它们是视图一样。如果可以从各种结构推断出方法,则需要消歧为(view: TextView).requestFocus()
。
// Now after all the boilerplate, it is very useful to write consise activities
class MyActivity extends Activity with ActivityUtil {
import R.id._ // Contains R.id.button, R.id.button2, R.id.button3, R.id.mytextview
lazy val my_button: ImageButton = button //In reality, R.id.button
lazy val his_button: ImageButton = button2
onCreate { savedInstanceState => // The type is automatically inferred.
setContentView(R.layout.main)
my_button.setOnClickListener(myCustomReactClick _)
his_button.setOnClickListener { () =>
//.... Scala code called after clicking his_button
}
button3.onClicked {
// Awesome way of setting a method. Thanks Scala.
}
mytextview.setText("My text") // Whoaaa ! setText on an integer.
(mytextview: TextView).requestFocus() // Disambiguation because the method is ambiguous
// Note that because of the use of maps, the textview is not recomputed.
}
def myCustomReactClick(v: View) = {
// .... Scala code called after clicking my_button
}
onPause{
// ... Custom code for onPause
}
}
确保scala文件的名称与其中包含的主要活动相匹配,在这种情况下,它应该是MyActivity.scala
。
其次,要将scala项目设置为库项目,要使用它作为具有不同资源的应用程序的基础,请遵循regular way of setting up a library project。右键单击要作为基础库项目的scala项目Properties
,Android
,然后选中isLibrary
。
要使用此库创建派生项目并为其生成APK,请创建一个新的android项目,而不添加任何scala或androidproguardscala性质,只需右键单击Properties
,Android
,然后添加以前的scala项目作为库。
更新使用新版Android插件,您应该转到Project Properties > Build Päth > Order and Export
并检查Android Private Libraries
。这将允许导出库项目和主项目中的scala库,即使主项目未分配Scala。
测试使用插件Robolectric可以轻松测试Android scala项目。只需按照创建测试项目的步骤,添加Scala特性。您甚至可以使用新的scala调试器,通过添加scalatest库,您可以使用Scala和Scala的许多其他功能。