2010-05-11 4 views
3

Je commence avec Scala + Android (et en utilisant le plugin sbt android). J'essaye de câbler une action de bouton à un bouton sans l'activité mettant en application View.OnClickListener.Impossible de trouver la méthode dans l'activité

Le clic sur le bouton échoue au moment de l'exécution car la méthode est introuvable. Le document que je suis en train de travailler indique qu'il suffit de déclarer une méthode void publique en prenant une vue sur l'action, et d'utiliser ce nom de méthode dans la mise en page.

Qu'ai-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 

MISE À JOUR

En pensant que cela peut avoir été une erreur avec le plugin sbt android, 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; 
} 

Répondre

6

Le plug-in android SBT inclut une tâche ProGuard que les bandes sur tout le code utilisé. Des trucs très sympas et qui aident vraiment à amincir le fichier .apk qui en résulte mais malheureusement les méthodes de callback ne sont généralement pas référencées dans votre code donc, par defalt, ProGuard va les jeter. Pour le voir vous-même, essayez d'utiliser javap sur la classe MainActivity mais définissez le classpath sur target/your_scala_version/classes.min.jar. Vous devez dire à proguard ce qu'il faut garder explicitement. Il existe déjà un ensemble d'options -keep dans la configuration par défaut de sbt android plugin mais ceci est spécifique à votre projet, vous devrez donc éditer votre définition de projet dans project/build/YourProjectName.scala. Regardez le code de sbt android plugin et lok pour la définition de proguardTask. Vous devrez surcharger cela et ajouter vos options -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'ai ajouté le -printseeds et une option -keep pour maintenir la méthode Calculate() du 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ées. ProGuard dispose d'un grand nombre d'options de configuration et vous devrez 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.

Questions connexes