Question

Je commence avec Scala + Android (et en utilisant le plug-in android SBT). Je suis en train de câbler une action de touche à un bouton sans l'activité mise en œuvre View.OnClickListener.

Le bouton clic échoue à l'exécution parce que la méthode ne peut pas être trouvé. Le document que je travaille à travers dit que je dois déclarer seulement une méthode public void se prononcer sur l'action, et utiliser ce nom de méthode dans la mise en page.

Qu'est-ce que je fait de mal?

MainActivity.scala

package net.badgerhunt.hwa

import android.app.Activity
import android.os.Bundle
import android.widget.Button
import android.view.View
import java.util.Date

class MainActivity extends Activity {
  override def onCreate(savedInstanceState: Bundle) = {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)
  }
  def calculate(button: View): Unit = println("calculating with %s ...".format(button))
}

res / layout / main.xml

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/button"
    android:text=""
    android:onClick="calculate"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>

l'échec onclick

D/AndroidRuntime(  362): Shutting down VM
W/dalvikvm(  362): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
E/AndroidRuntime(  362): Uncaught handler: thread main exiting due to uncaught exception
E/AndroidRuntime(  362): java.lang.IllegalStateException: Could not find a method calculate(View) in the activity
E/AndroidRuntime(  362):    at android.view.View$1.onClick(View.java:2020)
E/AndroidRuntime(  362):    at android.view.View.performClick(View.java:2364)
E/AndroidRuntime(  362):    at android.view.View.onTouchEvent(View.java:4179)
E/AndroidRuntime(  362):    at android.widget.TextView.onTouchEvent(TextView.java:6540)
E/AndroidRuntime(  362):    at android.view.View.dispatchTouchEvent(View.java:3709)
E/AndroidRuntime(  362):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
E/AndroidRuntime(  362):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
E/AndroidRuntime(  362):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
E/AndroidRuntime(  362):    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
E/AndroidRuntime(  362):    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
E/AndroidRuntime(  362):    at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
E/AndroidRuntime(  362):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
E/AndroidRuntime(  362):    at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
E/AndroidRuntime(  362):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(  362):    at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime(  362):    at android.app.ActivityThread.main(ActivityThread.java:4363)
E/AndroidRuntime(  362):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(  362):    at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime(  362):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
E/AndroidRuntime(  362):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
E/AndroidRuntime(  362):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(  362): Caused by: java.lang.NoSuchMethodException: calculate
E/AndroidRuntime(  362):    at java.lang.ClassCache.findMethodByName(ClassCache.java:308)
E/AndroidRuntime(  362):    at java.lang.Class.getMethod(Class.java:1014)
E/AndroidRuntime(  362):    at android.view.View$1.onClick(View.java:2017)
E/AndroidRuntime(  362):    ... 20 more

UPDATE

Pensant que cela pourrait avoir été une erreur avec le plugin Android SBT j'ai fait doublement sûr que la méthode était présente après la compilation. En utilisant javap ...

Compiled from "MainActivity.scala"
public class net.badgerhunt.hwa.MainActivity extends android.app.Activity implements scala.ScalaObject{
    public net.badgerhunt.hwa.MainActivity();
    public void calculate(android.view.View);
    public void onCreate(android.os.Bundle);
    public int $tag()       throws java.rmi.RemoteException;
}
Était-ce utile?

La solution

Le SBT plug-in Android comprend une tâche ProGuard que les bandes sur tout le code utilisé. trucs très cool et aide à vraiment mincir le fichier .apk résultant mais malheureusement des méthodes de rappel ne sont généralement pas référencés dans votre code afin, par defalt, ProGuard va les jeter. Pour voir vous-même essayez d'utiliser javap sur la classe MainActivity mais définir le classpath pour cibler / your_scala_version / classes.min.jar. Vous devez dire proguard ce qu'il faut garder explicitement. Il existe déjà un ensemble d'options dans -Keep la configuration par défaut de SBT plug-in android mais est spécifique à votre projet de sorte que vous devrez modifier la définition de votre projet dans le projet / construction / YourProjectName.scala. Regardez le code de SBT plug-in android et lok pour la définition proguardTask. Vous devrez passer outre cela et ajouter vos options de -Keep supplémentaires. Voilà ce que je l'ai fait:

import sbt._
import java.io._
import proguard.{Configuration=>ProGuardConfiguration, ProGuard, ConfigurationParser}
import sbt._
import Process._

trait Defaults {
  def androidPlatformName = "android-1.6"
}
class TestAndro2(info: ProjectInfo) extends ParentProject(info) {
  override def shouldCheckOutputDirectories = false
  override def updateAction = task { None }

  lazy val main  = project(".", "testAndro2", new MainProject(_))

  class MainProject(info: ProjectInfo) extends AndroidProject(info) with Defaults {
    val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test"
    override def proguardTask = task { 
      val args = "-injars" ::  mainCompilePath.absolutePath+File.pathSeparator+
      scalaLibraryJar.getAbsolutePath+"(!META-INF/MANIFEST.MF,!library.properties)"+
      (if (!proguardInJars.getPaths.isEmpty) File.pathSeparator+proguardInJars.getPaths.map(_+"(!META-INF/MANIFEST.MF)").mkString(File.pathSeparator) else "") ::                             
        "-outjars" :: classesMinJarPath.absolutePath ::
        "-libraryjars" :: libraryJarPath.getPaths.mkString(File.pathSeparator) :: 
        "-dontwarn" :: "-dontoptimize" :: "-dontobfuscate" :: 
        "-dontwarn" :: "-dontoptimize" :: "-dontobfuscate" :: "-printseeds" ::
        """-keep public class com.test.android.MainActivity {
          public void calculate(android.view.View);
        }""" ::
        "-keep public class * extends android.app.Activity" ::
        "-keep public class * extends android.app.Service" ::
        "-keep public class * extends android.appwidget.AppWidgetProvider" ::
        "-keep public class * implements junit.framework.Test { public void test*(); }" :: proguardOption :: Nil

        val config = new ProGuardConfiguration
        new ConfigurationParser(args.toArray[String], info.projectPath.asFile).parse(config)    
        new ProGuard(config).execute
        None
      }
    }
  }

Essentially, j'ajouté les -printseeds et une option -keep afin de maintenir la méthode calculate () de la MainActivity. -printseeds est bon pour le débogage car il dit proguard d'imprimer les noms des classes et des méthodes qui ont été conservés. ProGuard a un vaste ensemble d'options de configuration et vous aurez besoin de garder un œil sur eux lors de la construction de votre projet car il y a beaucoup de situations ambiguës où ProGuard ne fera pas la bonne chose par défaut.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top