我是 Scala 和 Android 开发的新手,所以我相信错误可能无处不在。实际上,这段代码或多或少是数独应用程序 Java 教程的 Scala 翻译。我得到的错误是常见的:
10-10 17:41:26.743: E/AndroidRuntime(17091): FATAL EXCEPTION: main
10-10 17:41:26.743: E/AndroidRuntime(17091): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.sudokuscala/com.example.sudokuscala.Game}: java.lang.NullPointerException
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2106)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.app.ActivityThread.access$600(ActivityThread.java:141)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.os.Handler.dispatchMessage(Handler.java:99)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.os.Looper.loop(Looper.java:137)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.app.ActivityThread.main(ActivityThread.java:5041)
10-10 17:41:26.743: E/AndroidRuntime(17091): at java.lang.reflect.Method.invokeNative(Native Method)
10-10 17:41:26.743: E/AndroidRuntime(17091): at java.lang.reflect.Method.invoke(Method.java:511)
10-10 17:41:26.743: E/AndroidRuntime(17091): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
10-10 17:41:26.743: E/AndroidRuntime(17091): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
10-10 17:41:26.743: E/AndroidRuntime(17091): at dalvik.system.NativeStart.main(Native Method)
10-10 17:41:26.743: E/AndroidRuntime(17091): Caused by: java.lang.NullPointerException
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.content.ContextWrapper.getResources(ContextWrapper.java:89)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.view.ContextThemeWrapper.getResources(ContextThemeWrapper.java:78)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.view.View.<init>(View.java:3226)
10-10 17:41:26.743: E/AndroidRuntime(17091): at com.example.sudokuscala.PuzzleView.<init>(PuzzleView.scala:15)
10-10 17:41:26.743: E/AndroidRuntime(17091): at com.example.sudokuscala.Game.<init>(Game.scala:21)
10-10 17:41:26.743: E/AndroidRuntime(17091): at java.lang.Class.newInstanceImpl(Native Method)
10-10 17:41:26.743: E/AndroidRuntime(17091): at java.lang.Class.newInstance(Class.java:1319)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.app.Instrumentation.newActivity(Instrumentation.java:1054)
10-10 17:41:26.743: E/AndroidRuntime(17091): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2097)
10-10 17:41:26.743: E/AndroidRuntime(17091): ... 11 more
请在下面找到我的代码:
安卓清单:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sudokuscala"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.sudokuscala.SudokuScala"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.sudokuscala.About"
android:label="@string/about_label"
android:theme="@android:style/Theme.Dialog" >
</activity>
<activity
android:name="com.example.sudokuscala.Game"
android:label="@string/game_title" >
</activity>
<activity
android:name="com.example.sudokuscala.PuzzleView">
</activity>
</application>
SudokuScala.scala (MainActivity)
package com.example.sudokuscala
import com.example.sudokuscala.R.layout
import android.app.Activity
import android.os.Bundle
import android.view.View
import android.view.View.OnClickListener
import android.content.Intent
import android.app.AlertDialog
import android.content.DialogInterface
import android.util.Log
class SudokuScala extends Activity {
override def onCreate(savedState : Bundle) : Unit = {
super.onCreate(savedState)
setContentView(R.layout.main)
//Set up click listeners for all the buttons
val newButton = findViewById(R.id.new_button)
newButton.setOnClickListener(new View.OnClickListener() {
def onClick(view : View) = {
openNewGameDialog
}
})
val aboutButton = findViewById(R.id.about_button)
aboutButton.setOnClickListener(new OnClickListener() {
def onClick(view : View) = {
startActivity(new Intent(SudokuScala.this, classOf[About]))
}
})
val exitButton = findViewById(R.id.exit_button)
exitButton.setOnClickListener(new OnClickListener() {
def onClick(view : View) = {
finish()
}
})
}
val TAG : String = "Sudoku"
//Open the window for difficulty choices
def openNewGameDialog() {
new AlertDialog.Builder(this)
.setTitle(R.string.new_game_title)
.setItems(R.array.difficulty,
new DialogInterface.OnClickListener() {
def onClick(dialoginterface : DialogInterface , i : Int) {
startGame(i)
}
})
.show()
}
// Start game with the chosen difficulty
def startGame(i : Int) {
Log.d(TAG, "clicked on " + i);
val intent = new Intent(this, classOf[Game])
intent.putExtra("org.example.sudoku.difficulty",i)
startActivity(intent)
}
}
游戏.scala
package com.example.sudokuscala
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
class Game extends Activity {
val TAG : String ="Sudoku"
val KEY_DIFFICULTY : String ="org.example.sudoku.difficulty"
val DIFFICULTY_EASY : Int = 0
val DIFFICULTY_MEDIUM : Int = 1
val DIFFICULTY_HARD : Int = 2
var puzzle = Array.fill(81)(0)
var puzzleView = new PuzzleView(this)
override def onCreate(savedState : Bundle) {
super.onCreate(savedState)
Log.d(TAG, "onCreate")
def diff : Int = getIntent.getIntExtra(KEY_DIFFICULTY, DIFFICULTY_EASY)
puzzle = getPuzzle(diff)
calculateUsedTiles
puzzleView = new PuzzleView(this)
setContentView(puzzleView)
puzzleView.requestFocus()
}
def showKeypadOrError( x : Int, y : Int) : Unit = {
var tiles = getUsedTiles(x, y)
if (tiles.length == 9) {
var toast = Toast.makeText(this, R.string.no_moves_label, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0)
toast.show(); }
else {
Log.d(TAG, "showKeypad: used=" + toPuzzleString(tiles))
var v = new Keypad(this, tiles, puzzleView);
v.show();
} }
def setTileIfValid(x : Int, y : Int, value : Int) : Boolean = {
var tiles = getUsedTiles(x, y)
if (value != 0) {
for ( tile <- tiles) {
if (tile == value)
return false;
} }
setTile(x, y, value)
calculateUsedTiles()
true
}
val x0 = 0
val y0 = 0
val z0 = 0
var used = Array.fill( x0, y0, z0)(0)
def getUsedTiles(x : Int, y : Int) : Array[Int] = {
used(x)(y);
}
def calculateUsedTiles() {
for (x <- 0 to 8) {
for (y <- 0 to 8) {
used(x)(y) = calculateUsedTiles(x, y);
// Log.d(TAG, "used[" + x + "][" + y + "] = "
// + toPuzzleString(used[x][y]));
}}}
//Pour chaque tile (case), cette fonction donne la liste (Array[Int])
//des chiffres interdits a cette case
def calculateUsedTiles(x : Int, y : Int) : Array[Int] = {
var c = Array.fill[Int](9)(0)
var t = 0
// horizontal
for (i <- 0 to 8) {
if (i == y) {}
else t = getTile(x, i)
if(t!= 0)
c(t - 1)=t
}
// vertical
for (i <- 0 to 8) {
if (i == x) {}
else t = getTile(i, y)
if (t != 0)
c(t - 1) = t
}
// same cell block
var startx = (x / 3) * 3
var starty = (y / 3) * 3
for (i <- startx to startx + 2) {
for (j <- starty to starty + 2) {
if (i == x && j == y) {}
else t = getTile(i, j)
if(t!= 0)
c(t - 1)=t
}
}
// compress
var nused = 0
for (t <- c) {
if (t != 0)
nused = nused + 1
}
var c1 = Array.fill(nused)(0)
nused = 0
for (t <- c){
if (t != 0)
{c1(nused) = t
nused = nused + 1}
}
c1
}
val easyPuzzle = new String(
"360000000004230800000004200" +
"070460003820000014500013020" +
"001900000007048300000000045" )
val mediumPuzzle = new String(
"650000070000506000014000005" +
"007009000002314700000700800" +
"500000630000201000030000097" )
val hardPuzzle = new String(
"009000000080605020501078000" +
"000000700706040102004000000" +
"000720903090301080000000600" )
def getPuzzle(diff : Int) : Array[Int] = {
var puz = new String
// TODO: Continue last game
if (diff == 2)
puz = hardPuzzle
else if (diff == 1)
puz = mediumPuzzle
else if (diff == 0)
puz = easyPuzzle
return fromPuzzleString(puz) }
def toPuzzleString(puz : Array[Int]) : String = {
var buf = new StringBuilder()
for (element <- puz) {
buf.append(element)
}
buf.toString() }
def fromPuzzleString(string : String) : Array[Int] = {
var puz = Array.fill[Int](81)(0)
for (i <- 0 to 80) {
puz(i) = string.charAt(i) - '0';
}
return puz;
}
def getTile(x : Int, y : Int) : Int = {
puzzle(y * 9 + x)
}
def setTile(x : Int, y : Int, value : Int) : Unit = {
puzzle(y * 9 + x) = value
}
def getTileString(x : Int, y : Int) : String ={
var v = getTile(x, y)
if (v == 0)
""
else
String.valueOf(v);
}
}
PuzzleView.scala
package com.example.sudokuscala
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Paint.FontMetrics
import android.graphics.Paint.Style
import android.graphics.Rect
import android.util.Log
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.animation.AnimationUtils
class PuzzleView(context : Context) extends View(context) {
val TAG ="Sudoku"
var game = new Game
this.game = context.asInstanceOf[Game]
setFocusable(true)
setFocusableInTouchMode(true)
var width : Float = 0 //width of one tile
var height : Float = 0//height of one tile
var selX : Float = 0//X index of selection
var selY : Float = 0//Y index of selection
var selRect = new Rect()
override def onSizeChanged( w : Int, h : Int , oldw : Int, oldh : Int) : Unit = {
width = w /9
height = h/ 9
getRect(selX.toInt,selY.toInt, selRect)
Log.d(TAG, "onSizeChanged : width " + width +", height " + height)
super.onSizeChanged(w, h, oldw, oldh)
}
def getRect(x : Int, y : Int, rect : Rect) : Unit = {
rect.set( (x*width).toInt, (y*height).toInt, (x*width+width).toInt, (y*height+height).toInt);
}
override def onDraw(canvas : Canvas) : Unit ={
//Draw the background
val background = new Paint()
background.setColor(getResources().getColor(R.color.puzzle_background))
canvas.drawRect(0, 0, getWidth(), getHeight(), background)
//Draw the board
//Define colors for the grid lines
val dark = new Paint()
dark.setColor(getResources().getColor(R.color.puzzle_dark))
val hilite = new Paint()
hilite.setColor(getResources().getColor(R.color.puzzle_hilite))
val light = new Paint()
light.setColor(getResources().getColor(R.color.puzzle_light))
//Draw the minor grid lines
for (i <- 0 to 8){
canvas.drawLine(0, i*height, getWidth(), i * height, light)
canvas.drawLine(0, i*height + 1, getWidth(), i * height + 1, hilite)
canvas.drawLine(i*width, 0, i*width, getHeight(), light)
canvas.drawLine(i*width + 1, 0, i*width + 1, getHeight(), hilite)
}
//Draw the major grid lines
for (i <- 0 to 8){
if (i%3 != 0) {}
else {
canvas.drawLine(0, i*height, getWidth(), i * height, dark)
canvas.drawLine(0, i*height + 1, getWidth(), i * height + 1, dark)
canvas.drawLine(0, i*height - 1, getWidth(), i * height - 1, dark)
canvas.drawLine(i*width, 0, i*width, getHeight(), dark)
canvas.drawLine(i*width + 1, 0, i*width + 1, getHeight(), dark)
canvas.drawLine(i*width - 1, 0, i*width - 1, getHeight(), dark)
}
}
// Define color and style for numbers
val foreground = new Paint(Paint.ANTI_ALIAS_FLAG)
foreground.setColor(getResources().getColor(R.color.puzzle_foreground))
foreground.setStyle(Style.FILL)
foreground.setTextSize(height * 0.75f)
foreground.setTextScaleX(width / height)
foreground.setTextAlign(Paint.Align.CENTER)
// Draw the number in the center of the tile
val fm = foreground.getFontMetrics()
// Centering in X: use alignment (and X at midpoint)
var x = width / 2
// Centering in Y: measure ascent/descent first
var y = height / 2 - (fm.ascent + fm.descent) / 2
for (i <- 0 to 8) {
for (j <- 0 to 8) {
canvas.drawText(this.game.getTileString(i, j), i * width + x, j * height + y, foreground)
}
}
//Draw the selection
Log.d(TAG, "selRect=" + selRect)
val selected = new Paint()
selected.setColor(getResources().getColor(R.color.puzzle_selected))
canvas.drawRect(selRect, selected)
// Draw the hints...
// Pick a hint color based on #moves left
/*val hint = new Paint();
val c = Array(getResources().getColor(R.color.puzzle_hint_0), getResources().getColor(R.color.puzzle_hint_1), getResources().getColor(R.color.puzzle_hint_2))
val r = new Rect();
for (i <- 0 to 8) {
for (j <- 0 to 8) {
val movesleft = 9 - game.getUsedTiles(i, j).length
if (movesleft < c.length) {
getRect(i, j, r)
hint.setColor(c(movesleft))
canvas.drawRect(r, hint)
}
}
}*/
}
override def onKeyDown(keyCode : Int, event : KeyEvent) : Boolean ={
Log.d(TAG, "onKeyDown=" + keyCode +", events =" + event)
keyCode match {
/*case KeyEvent.KEYCODE_DPAD_UP =>
select(selX, selY-1)
case KeyEvent.KEYCODE_DPAD_DOWN =>
select(selX, selY+1)
case KeyEvent.KEYCODE_DPAD_LEFT =>
select(selX-1, selY)
case KeyEvent.KEYCODE_DPAD_RIGHT =>
select(selX+1, selY)*/
case KeyEvent.KEYCODE_0 =>
case KeyEvent.KEYCODE_SPACE => setSelectedTile(0)
case KeyEvent.KEYCODE_1 => setSelectedTile(1)
case KeyEvent.KEYCODE_2 => setSelectedTile(2)
case KeyEvent.KEYCODE_3 => setSelectedTile(3)
case KeyEvent.KEYCODE_4 => setSelectedTile(4)
case KeyEvent.KEYCODE_5 => setSelectedTile(5)
case KeyEvent.KEYCODE_6 => setSelectedTile(6)
case KeyEvent.KEYCODE_7 => setSelectedTile(7)
case KeyEvent.KEYCODE_8 => setSelectedTile(8)
case KeyEvent.KEYCODE_9 => setSelectedTile(9)
//case KeyEvent.KEYCODE_ENTER =>
//case KeyEvent.KEYCODE_DPAD_CENTER => game.showKeypadOrError(selX, selY)
//default => return super.onKeyDown(keyCode,event);
}
return true;
}
def select(x: Int, y : Int) : Unit = {
invalidate(selRect)
selX = Math.min(Math.max(x,0),8)
selY = Math.min(Math.max(y,0),8)
getRect(selX.toInt, selY.toInt, selRect)
invalidate(selRect)
}
override def onTouchEvent(event : MotionEvent) : Boolean = {
if (event.getAction() != MotionEvent.ACTION_DOWN)
return super.onTouchEvent(event)
select( (event.getX()/width).toInt, (event.getY() / height).toInt)
game.showKeypadOrError(selX.toInt, selY.toInt)
Log.d(TAG, "onTouchEvent : x " + selX + ", y " + selY)
return true;
}
def setSelectedTile(tile : Int) : Unit ={
if (game.setTileIfValid(selX.toInt, selY.toInt, tile)) {
invalidate() //may change hints
} else
//Number is not valid for this tile
Log.d(TAG, "setSelectedTile: invalid: " + tile);
startAnimation(AnimationUtils.loadAnimation(game,R.anim.shake));
}
}
如果你能帮助我,那就太好了!提前致谢 !