Android UI, Intents, Threads - Interactive Computing Lab: Main

Download Report

Transcript Android UI, Intents, Threads - Interactive Computing Lab: Main

Understanding Android UI and
Intents by Implementing Sudoku
Uichin Lee
KAIST KSE
Feb. 16, 2011
Hello, Android Chapter 3
Objective:
• Review Android Sudoku Example to understand
Android User Interfaces and Intents
3x3 box contains numbers
1 through 9
Getting Started
• UI Design methods:
– Procedural design: written in Java code (like Swing,
e.g., JButton, JFrame)
– Declarative design: XML description of UI objects
• Preferred method
• Step 1: Creating the Opening Screen
Project name: Sudoku
Build target: Android 2.2
Application name: Sudoku
Package name: org.example.sudoku
Create Activity: Sudoku
Min SDK Version: 8
Part 4
Android – User Interfaces
Using XML Layouts
Victor Matos
Cleveland State University
Notes are based on:
The Busy Coder's Guide to Android Development
by Mark L. Murphy
Copyright © 2008-2009 CommonsWare, LLC.
ISBN: 978-0-9816780-0-9
&
Android Developers
http://developer.android.com/index.html
4. Android – UI - User Interfaces
The View Class
•
The View class represents the basic building block for user interface
components.
•
A View occupies a rectangular area on the screen and is responsible
for drawing and event handling.
•
View is the base class for widgets, which are used to create
interactive UI components (buttons, text fields, etc.).
•
The ViewGroup subclass (of View) is the base class for layouts,
which are invisible containers that can hold other Views (or other
ViewGroups) and define their layout properties.
5
4. Android – UI - User Interfaces
Using Views
All of the views in a window are arranged in a single tree.
You can add views either from code or by specifying a tree of views in
one or more XML layout files.
Once you have created a tree of views, there are typically a few types of
common operations you may wish to perform:
1.
2.
3.
4.
Set properties: for example setting the text of a TextView. Properties
that are known at build time can be set in the XML layout files.
Set focus: The framework will handled moving focus in response to user
input. To force focus to a specific view, call requestFocus().
Set up listeners: Views allow clients to set listeners that will be notified
when something interesting happens to the view. For example, a
Button exposes a listener to notify clients when the button is clicked.
Set visibility: You can hide or show views using setVisibility(int).
6
4. Android – UI - User Interfaces
A brief sample of UI components
Layouts
Linear Layout
Relative Layout
Table Layout
A LinearLayout is a
GroupView that will lay
child View elements
vertically or horizontally.
A RelativeLayout is a ViewGroup
that allows you to layout child
elements in positions relative to
the parent or siblings elements.
A TableLayout is a
ViewGroup that will lay
child View elements into
rows and columns.
7
4. Android – UI - User Interfaces
A brief sample of UI components
Widgets
GalleryView
TabWidget
Spinner
DatePicker
Form Controls
A DatePicker is a widget
that allows the user to
select a month, day and
year.
Includes a variety of typical
form widgets, like:
image buttons,
text fields,
checkboxes and
radio buttons.
8
4. Android – UI - User Interfaces
A brief sample of UI components
WebView
MapView
AutoCompleteTextView
ListView
It is a version of the EditText
widget that will provide
auto-complete suggestions
as the user types. The
suggestions are extracted
from a collection of strings.
A ListView is a View that
shows items in a vertically
scrolling list. The items are
acquired from a ListAdapter.
9
4. Android – UI - User Interfaces
What is an XML Layout?
An XML-based layout is a specification of the various UI
components (widgets) and the relationships to each other – and to
their containers – all written in XML format.
Android considers XMLbased layouts to be
resources, and as such
layout files are stored in the
res/layout directory inside
your Android project.
10
4. Android – UI - User Interfaces
What is an XML Layout?
ASIDE
You could create Layout XML files using UI tools
such as:
•
Eclipse ADT UI Designer (getting better
but still…)
•
DroidDraw (to be phased out soon???)
•
Asset Studio (probably the best option,
not available yet)
Eclipse ADT UI Designer
11
4. Android – UI - User Interfaces
What is an XML Layout?
Each XML file contains a tree of elements specifying a layout of
widgets and containers that make up one View (shown later).
The attributes of the XML elements are properties, describing how
a widget should look or how a container should behave.
Example:
If a Button element has an attribute value of
android:textStyle = "bold"
that means that the text appearing on the face of the button
should be rendered in a boldface font style.
12
4. Android – UI - User Interfaces
An example
The application places a button to occupy the screen.
When clicked the button’s text shows current time.
import
import
import
import
import
import
java.util.Date;
android.app.Activity;
android.os.Bundle;
android.view.View;
android.view.View.OnClickListener;
android.widget.Button;
public class AndDemo extends Activity {
Button btn;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
btn = (Button) findViewById(R.id.myButton);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateTime();
}
});
}// onCreate
//
private void updateTime() {
btn.setText(new Date().toString());
}
}
13
4. Android – UI - User Interfaces
An example
This is the XML-Layout definition
<?xml version="1.0" encoding="utf-8"?>
<Button
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/myButton"
android:text=""
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
The root element needs to declare the Android XML namespace:
xmlns:android="http://schemas.android.com/apk/res/android"
All other elements will be children of the root and will inherit that namespace declaration.
Because we want to reference this button from our Java code, we need to give
it an identifier via the android:id attribute.
14
4. Android – UI - User Interfaces
An example cont.
<?xml version="1.0" encoding="utf-8"?>
<Button
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/myButton"
android:text=""
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
The remaining attributes are properties of this Button instance:
•
android:text indicates the initial text to be displayed on the button face (in this case,
an empty string)
•
android:layout_width and android:layout_height tell Android to have the button's
width and height fill the "parent“ container, in this case the entire screen.
15
4. Android – UI - User Interfaces
Look for your SDK folder, usually:
C:/your_sdk_path/android_sdk_windows/tools
UI Hierarchy
UI
Tree
The utility HierarchyViewer displays the UI structure of the current screen
shown on the emulator or device.
( Execute app on emulator, execute HierarchyViewer, click on Emulator >
Refresh Screenshot )
Cf) if you encounter an error (can’t find adb.exe), you should include SDK tools in your system path
16
4. Android – UI - User Interfaces
UI Hierarchy
Example: Display UI Hierarchy
UI
Tree
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
4. Android – UI - User Interfaces
Attaching Layouts to Java Code
PLUMBING. You must ‘connect’ the XML elements with equivalent objects
in your Java activity. This allows you to manipulate the UI with code.
XLM Layout
<xml….
...
...
</xml>
JAVA code
public class ….
{
...
...
}
18
4. Android – UI - User Interfaces
Attaching Layouts to Java Code
Assume the UI in res/layout/main.xml has been created. This layout could
be called by an application using the statement
setContentView(R.layout.main);
Individual widgets, such as myButton could be accessed by the application
using the statement findViewByID(...) as in
Button btn = (Button) findViewById(R.id.myButton);
Where R is a class automatically generated to keep track of resources
available to the application. In particular R.id... is the collection of widgets
defined in the XML layout.
19
4. Android – UI - User Interfaces
Attaching Layouts to Java Code
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package uichin.kaist;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
}
}
Where R is a class automatically generated to keep track of resources
available to the application. In particular R.id... is the collection of widgets
defined in the XML layout.
20
4. Android – UI - User Interfaces
Attaching Layouts to Java Code
Attaching Listeners to the Widgets
The button of our example could now be used, for instance a listener
for the click event could be written as:
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateTime();
}
});
private void updateTime() {
btn.setText(new Date().toString());
}
21
Sudoku Overview
(5) Draw Game
About.java
Game.java
PuzzleView.java
(1) Main
Layout XML
Touch
(4) New Game
Sudoku.java
(2) About
Keypad.java
Pref.java
Sudoku.java
(3) Menu Key
Preference:
Menu
(6) Key Pad
Step 1: Main Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/re
s/android"
android:background="@color/background"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:padding="30dip"
android:orientation="horizontal" >
<LinearLayout
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_gravity="center" >
<TextView
android:text="@string/main_title"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="25dip"
android:textSize="24.5sp" />
<Button
android:id="@+id/continue_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/continue_label" />
<Button
android:id="@+id/exit_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/exit_label" />
</LinearLayout>
</LinearLayout>
<resources>
<string name="app_name">Sudoku</string>
<string name="main_title">Android Sudoku</string>
<string name="continue_label">Continue</string>
<string name="new_game_label">New Game</string>
<string name="about_label">About</string>
<string name="exit_label">Exit</string>
Step 1: Main Layout
Use
“GraphicalLayout”
Step 1: Main Layout
• Emulator: Ctrl + F11 (rotate screen)
• Add a new layout for the landscape mode (use “-land”
suffix)
Step 1: Main Layout
• Set up click listeners for all the buttons
import
import
import
import
import
android.app.Activity;
android.os.Bundle;
android.content.Intent;
android.view.View;
android.view.View.OnClickListener;
public class Soduku extends Activity implements OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.about);
// Set up click listeners for all the buttons
View continueButton = findViewById(R.id.continue_button);
continueButton.setOnClickListener(this);
View newButton = findViewById(R.id.new_button);
newButton.setOnClickListener(this);
View aboutButton = findViewById(R.id.about_button);
aboutButton.setOnClickListener(this);
View exitButton = findViewById(R.id.exit_button);
exitButton.setOnClickListener(this);
}
}
Step 1: Main Layout
• onClick handler
public void onClick(View v) {
Continue
switch (v.getId()) {
case R.id.continue_button:
Intent intent = new Intent(Sudoku.this, Game.class);
intent.putExtra(Game.KEY_DIFFICULTY, Game.DIFFICULTY_CONTINUE);
startActivity(intent);
break;
case R.id.about_button:
Intent i = new Intent(this, About.class);
startActivity(i);
break;
// More buttons go here (if any) ...
case R.id.new_button:
openNewGameDialog();
break;
case R.id.exit_button:
finish();
break;
}
}
Shut down activity
and return control
(4) New Game
(2) About
12-2
Android
Intents
Part 2
Inter-Process Communication Using Bundles
Victor Matos
Cleveland State University
Notes are based on:
Android Developers
http://developer.android.com/index.html
12. Android – Intents – Part 2
Intents
Android Intents
An activity usually presents a single visual user interface from which a number of
actions could be performed.
Moving from one activity to another is accomplished by having the current
activity start the next one through so called intents.
Activity-1
startActivityForResult
…
onActivityResult()
…
Intent
{action + data}
Activity-2
requestCode
requestResult
[ optional data ]
onResult()
…
…
29
12. Android – Intents – Part 2
Intents
Android Bundles
Most programming languages support the notion of IPC (Inter-Process
Communication) method-calling with arguments flowing birectionally from the
caller to the invoked method.
In android the calling activity issues an invocation to another activity using an
Intent object.
Notably in Android, the caller does not stop waiting for
the called activity to return results. Instead a listeningmethod [onActivityResult(...) ] should be used.
30
IPC Inter-Process Communication
12. Android – Intents – Part 2
Intents
Android Bundles
Normally the IPC expressions actual parameter list, and
formal parameter list are used to designated the signature of
particpating arguments, and the currently supplied data.
Instead of using the traditional formal / actual parameter lists,
Android relies on the concept of Intents to establish Inter-processcommunication.
Intents optionally carry a named actual list or bundle for data
exchange.
31
12. Android – Intents – Part 2
Intents
Android Bundles
The Android Bundle container is a simple mechanism used to pass data between
activities.
A Bundle is a type-safe collection of <name, value> pairs.
There is a set of putXXX and getXXX methods to store and retrieve (single and
array) values of primitive data types from/to the bundles. For example
Bundle myBundle = new Bundle();
myBundle.putDouble (“var1”, 3.1415);
...
Double v1 = myBundle.getDouble(“var1”);
32
12. Android – Intents – Part 2
Intents
Android Intents & Bundles
Activity1: Sender
Activity2: Receiver
Intent myIntentA1A2 = new Intent (Activity1.this, Activity2.class);
Bundle myBundle1 = new Bundle();
myBundle1.putInt (“val1”, 123);
myIntentA1A2.putExtras(myBundle1);
startActivityForResult(myIntentA1A2, 1122);
INTENT
Sender class / Receiver class
requestCode (1122)
resultCode
Extras: { val1 = 123 }
33
12. Android – Intents – Part 2
Intents
Android Intents & Bundles
Activity1: Sender
Activity2: Receiver
Intent myLocalIntent2 = getIntent();
Bundle myBundle = myLocalIntent.getExtras();
INTENT
int val1 = myBundle.getInt(“val1");
Sender class / Receiver class
requestCode (1122)
resultCode
Extras: { val1 = 123 }
34
12. Android – Intents – Part 2
Intents
Android Intents & Bundles
Activity1: Sender
Activity2: Receiver
myBundle.putString("val1", 456 );
myLocalIntent.putExtras(myBundle);
INTENT
setResult(Activity.RESULT_OK, myLocalIntent);
Sender class / Receiver class
requestCode (1122)
resultCode (OK)
Extras: { val1 = 456 }
35
12. Android – Intents – Part 2
Intents
Android Bundles
Available at: http://developer.android.com/reference/android/os/Bundle.html
Example of Public Methods
void
Object
boolean
void
void
void
void
void
int
clear()
Removes all elements from the mapping of this Bundle.
clone()
Clones the current Bundle.
containsKey(String key)
Returns true if the given key is contained in the mapping of this Bundle.
putIntArray(String key, int[] value)
Inserts an int array value into the mapping of this Bundle, replacing any
existing value for the given key.
putString(String key, String value)
Inserts a String value into the mapping of this Bundle, replacing any existing
value for the given key.
putStringArray(String key, String[] value)
Inserts a String array value into the mapping of this Bundle, replacing any
existing value for the given key.
putStringArrayList(String key, ArrayList<String> value)
Inserts an ArrayList value into the mapping of this Bundle, replacing any
existing value for the given key.
remove(String key)
Removes any entry with the given key from the mapping of this Bundle.
size()
Returns the number of mappings contained in this Bundle.
36
Sudoku
(5) Draw Game
About.java
Game.java
PuzzleView.java
(1) Main
Layout XML
Touch
(4) New Game
Sudoku.java
(2) About
Keypad.java
Pref.java
Sudoku.java
(3) Menu Key
Preference:
Menu
(6) Key Pad
Step 2: About Window
• Draw a window with some info about Sudoku
– Define a new Activity, and start it
– Use the AlertDialog class, and show it
– Subclass Android’s Dialog class, and show that
• How to add a new Activity class in Eclipse?
1. (create a new class) File > New > Class:
• Superclass: android.app.Activity
2. (activity registration) AndroidManitest.xml > Application > Application Nodes
• Or manually type in <activity…> </activity> in the XML file
3. Auto-filling Override functions: Right click “About.java” > Source >
Override/Implement methods > Select “onCreate”
Register a new
activity here
<activity android:name=".About“>
</activity>
Step 2: About Window
• Adding a new layout? (about.xml)
– New > Other > Android > Android XML File
• This will automatically add R.layout.about (in R.java)
– ADT 9.0: Layout selection does not work (not sure why)
about.xml
“Layout”
ScrollView
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip">
<TextView
android:id="@+id/about_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_text" />
</ScrollView>
Step 2: About Window
package org.example.sudoku;
import android.app.Activity;
import android.os.Bundle;
public class About extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set “about.xml” layout as a default view
setContentView(R.layout.about);
}
}
Step 2: About Window
• Applying a theme
– Very similar to Cascading Style Sheets (CSS) used for
web pages
– Default styles, and user-defined styles
• Using a default theme: AndroidMenifest.xml
– “@android:” prefix means that it’s a reference to a
resource defined by Android
<activity android:name=".About“
android:label="@string/about_title“
android:theme="@android:style/Theme.Dialog">
</activity>
Sudoku
(5) Draw Game
About.java
Game.java
PuzzleView.java
(1) Main
Layout XML
Touch
(4) New Game
Sudoku.java
(2) About
Keypad.java
Pref.java
Sudoku.java
(3) Menu Key
Preference:
Menu
(6) Key Pad
Step 3: Adding a Menu/Setting
• Menus in Android:
– When you press the physical Menu button
– Context menu popping up when you press and hold
your finger on the screen (or press and hold the
trackball or the D-pad center button)
• Implementing “physical menu”
1. Define string labels that will be used in menu
(res/values/strings.xml)
2. Define a menu using XML (res/menu/menu.xml)
•
New > Other > Android > Android XML File (select “menu”)
Step 3: Adding a Menu/Setting
• Bring up a menu: Sudoku.java
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.settings:
startActivity(new Intent(this, Prefs.class));
return true;
// More items go here (if any) ...
}
return false;
}
Read menu
definition from
xml file
Called when a
user selects any
menu item
Step 3: Adding a Menu/Setting
• Bring up a setting: Pref.java
1. Create a new layout (choose “Preference”): stored in
“res/xml” by default
2. Create a class “Pref.java” (extends
“PreferenceActivity”)
•
Register Activity in the AndroidMenifest.xml
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk
/res/android">
<CheckBoxPreference
android:key="music"
android:title="@string/music_title"
android:summary="@string/music_summary"
android:defaultValue="true" />
<CheckBoxPreference
android:key="hints"
android:title="@string/hints_title"
android:summary="@string/hints_summary"
android:defaultValue="true" />
</PreferenceScreen>
setting.xml (layout)
import
import
import
import
android.content.Context;
android.os.Bundle;
android.preference.PreferenceActivity;
android.preference.PreferenceManager;
public class Prefs extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
}
}
Pref.java
Sudoku
(5) Draw Game
About.java
(1) Main
Layout XML
(4) New
Game
Game.java
PuzzleView.java
Touch
Sudoku.java
(2) About
Keypad.java
Pref.java
Sudoku.java
(3) Menu Key
Preference:
Menu
(6) Key Pad
Step 4: New Game Menu
• Pop up a dialog box, asking users to select among three
difficulty levels (AlertDialog)
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.util.Log;
Sudoku.java
<resources>
<array name="difficulty">
<item>@string/easy_label</item>
public class Sudoku extends Activity implements OnClickListener
{
<item>@string/medium_label</item>
private void openNewGameDialog() {
<item>@string/hard_label</item>
new AlertDialog.Builder(this)
</array>
.setTitle(R.string.new_game_title)
</resources>
.setItems(R.array.difficulty,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialoginterface, int i) {
startGame(i);
}
})
.show();
}
private void startGame(int i) {
Log.d(TAG, "clicked on " + i);
Intent intent = new Intent(Sudoku.this, Game.class);
intent.putExtra(Game.KEY_DIFFICULTY, i);
startActivity(intent);
}
}
Debugging
• Debugging with Log Messages
– Log class provides several static methods to print messages of various levels
•
•
•
•
•
•
Log.e(): errors
Log.w(): warnings
Log.i(): information
Log.d(): debugging
Log.v(): verbose
Log.wtf(): what a terrible failure
– Eclipse: Window > Show View > Other > Android > LogCat
• Debugging with Eclipse debugger
– Must enable debugging: AndroidManifest.xml
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.example.sudoku"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name“
android:debuggable=“true”>
Sudoku
(5) Draw Game
About.java
Game.java
PuzzleView.java
(1) Main
Layout XML
Touch
(4) New Game
Sudoku.java
(2) About
Keypad.java
Pref.java
Sudoku.java
(3) Menu Key
Preference:
Menu
(6) Key Pad
Step 5: Starting a Game
• Procedure:
1. Start a new activity (Game)
• Game.java
2. Game initializes its view using PuzzleView
• PuzzleView.java
protected void onCreate(savedInstanceState) {
int diff = getIntent().getIntExtra(KEY_DIFFICULTY,
DIFFICULTY_EASY);
puzzleView = new PuzzleView(this);
setContentView(puzzleView);
Activity-1
(Sudoku)
Intent
{action + data}
startActivity()
Intent intent = new Intent(Sudoku.this, Game.class);
intent.putExtra(Game.KEY_DIFFICULTY, i);
startActivity(intent);
Activity-2
(Game)
PuzzleView
Step 5: Starting a Game – PuzzleView
• Android 2D graphic package:
– Color
• E.g., color = Color.argb(127, 255, 0, 255) or color =
getResources().getColor(R.color.mycolor)
– Paint
• Holds style, color, and other info needed to draw any graphics such
as bitmaps, text, and geometric shapes
– Canvas: a surface on which you draw!
• Display is taken up by an Activity, which hosts a View, which in turn
hosts Canvas
• Drawing on the canvas by overriding the View.onDraw() method
– Path
– Drawable: bitmap, Shape, etc.
Step 5: Starting a Game – PuzzleView
import
import
import
import
import
import
android.content.Context;
android.graphics.Canvas;
android.graphics.Paint;
android.graphics.Rect;
android.graphics.Paint.FontMetrics;
android.graphics.Paint.Style;
import android.os.Bundle;
import android.os.Parcelable;
import
import
import
import
import
android.util.Log;
android.view.KeyEvent;
android.view.MotionEvent;
android.view.View;
android.view.animation.AnimationUtils;
public class PuzzleView extends View {
private final Game game;
public PuzzleView(Context context) {
super(context);
this.game = (Game) context;
setFocusable(true);
setFocusableInTouchMode(true);
}
private
private
private
private
private
float width;
// width of one tile
float height;
// height of one tile
static final String SELX = "selX";
static final String SELY = "selY";
final Rect selRect = new Rect();
@Override
protected void onSizeChanged(int w, int h, int
oldw, int oldh) {
width = w / 9f;
height = h / 9f;
getRect(selX, selY, selRect);
super.onSizeChanged(w, h, oldw, oldh);
}
private void getRect(int x, int y, Rect rect) {
rect.set((int) (x * width), (int) (y *
height), (int) (x * width + width), (int) (y *
height + height));
}
onSizeChanged() is called after
the view is created
Step 5: Starting a Game - PuzzleView
• Drawing the board
– onDraw() method is called anytime the view needs to
be updated
@Override
protected void onDraw(Canvas canvas) {
// Draw the background...
Paint background = new Paint();
background.setColor(getResources().getColor(
R.color.puzzle_background));
canvas.drawRect(0, 0, getWidth(), getHeight(), background);
Step 5: Starting a Game - PuzzleView
• Drawing the grid lines for the board
// Define colors for the grid lines
Paint dark = new Paint();
dark.setColor(getResources().getColor(R.color.puzzle_dark));
Paint hilite = new Paint();
hilite.setColor(getResources().getColor(R.color.puzzle_hilite));
Paint light = new Paint();
light.setColor(getResources().getColor(R.color.puzzle_light));
// Draw the minor grid lines
for (int i = 0; i < 9; i++) {
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 (int i = 0; i < 9; i++) {
if (i % 3 != 0)
continue;
canvas.drawLine(0, i * height, getWidth(), i * height, dark);
canvas.drawLine(0, i * height + 1, getWidth(), i * height + 1, hilite);
canvas.drawLine(i * width, 0, i * width, getHeight(), dark);
canvas.drawLine(i * width + 1, 0, i * width + 1, getHeight(), hilite);
}
Step 5: Starting a Game - PuzzleView
• Drawing the numbers
// Define color and style for numbers
Paint 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
FontMetrics fm = foreground.getFontMetrics();
// Centering in X: use alignment (and X at midpoint)
float x = width / 2;
// Centering in Y: measure ascent/descent first
float y = height / 2 - (fm.ascent + fm.descent) / 2;
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
canvas.drawText(this.game.getTileString(i, j), i
* width + x, j * height + y, foreground);
}
}
Sudoku
(5) Draw Game
About.java
Game.java
PuzzleView.java
(1) Main
Layout XML
Touch
(4) New Game
Sudoku.java
(2) About
Keypad.java
Pref.java
Sudoku.java
(3) Menu Key
Preference:
Menu
(6) Key Pad
Step 6: Handling Input
• Procedure
1.
2.
Create a new layout for keypad (buttons!)
Create a new class, Keypad that extends Dialog
public class Keypad extends Dialog {
private final View keys[] = new View[9];
private View keypad;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setTitle(R.string.keypad_title);
setContentView(R.layout.keypad);
findViews(); // keys, keypad Views
private final int useds[];
private final PuzzleView puzzleView;
for (int element : useds) {
if (element != 0)
public Keypad(Context context, int
useds[], PuzzleView puzzleView) {
super(context);
this.useds = useds;
this.puzzleView = puzzleView;
}
keys[element-1].setVisibility(View.INVISIBLE);
}
// for each key, keypad view,
// set up OnClickListener()
setListeners();
}
Cf. As of J2SE 5.0, Java allows you to iterate through a collection (array):
http://www.java-tips.org/java-se-tips/java.lang/the-enhanced-for-loop.html
Step 6: Handling Input
• PuzzleView:
– Keyboard: if a key is down, onKeyDown()is invoked
– Touch: if a user touches the screen, onTouchEvent()is invoked
– Then, display keypad: game.showKeypadOrError(selX, selY)
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
select(selX - 1, selY);
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
select(selX + 1, selY);
break;
case KeyEvent.KEYCODE_0:
case KeyEvent.KEYCODE_SPACE:
setSelectedTile(0); break;
case KeyEvent.KEYCODE_1:
setSelectedTile(1); break;
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_DPAD_CENTER:
game.showKeypadOrError(selX, selY);
break;
default:
return super.onKeyDown(keyCode, event);
}
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() !=
MotionEvent.ACTION_DOWN)
return super.onTouchEvent(event);
select((int) (event.getX() / width),
(int) (event.getY() / height));
game.showKeypadOrError(selX, selY);
return true;
}
Step 6: Handling Input
• Showing keypad and returning the result
/** Open the keypad if there are any valid moves */
Game.java
protected void showKeypadOrError(int x, int y) {
int tiles[] = getUsedTiles(x, y);
if (tiles.length == 9) {
Toast toast = Toast.makeText(this,
R.string.no_moves_label, Toast.LENGTH_SHORT);
strings.xml
toast.setGravity(Gravity.CENTER, 0, 0);
<string
toast.show();
name="no_moves_label">
No moves</string>
}
else {
Log.d(TAG, "showKeypad: used=" + toPuzzleString(tiles));
Dialog v = new Keypad(this, tiles, puzzleView);
v.show();
}
}
private void returnResult(int tile) {
puzzleView.setSelectedTile(tile);
dismiss();
}
Keypad.java