Figure 6.9 shows class CannonView
’s constructor. When a View
is inflated, its constructor is called with a Context
and an AttributeSet
as arguments. The Context
is the Activity
that displays the CannonGameFragment
containing the CannonView
, and the AttributeSet (package android.util
) contains the CannonView
attribute values that are set in the layout’s XML document. These arguments are passed to the superclass constructor (line 96) to ensure that the custom View
is properly configured with the values of any standard View
attributes specified in the XML. Line 97 stores a reference to the MainActivity
so we can use it at the end of a game to display an AlertDialog
from the Activity
’s GUI thread.
93 // public constructor
94 public CannonView(Context context, AttributeSet attrs)
95 {
96 super(context, attrs); // call superclass constructor
97 activity = (Activity) context; // store reference to MainActivity
98
99 // register SurfaceHolder.Callback listener
100 getHolder().addCallback(this);
101
102 // initialize Lines and Point representing game items
103 blocker = new Line(); // create the blocker as a Line
104 target = new Line(); // create the target as a Line
105 cannonball = new Point(); // create the cannonball as a Point
106
107 // initialize hitStates as a boolean array
108 hitStates = new boolean[TARGET_PIECES];
109
110 // initialize SoundPool to play the app's three sound effects
111 soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
112
113 // create Map of sounds and pre-load sounds
114 soundMap = new SparseIntArray(3); // create new SparseIntArray
115 soundMap.put(TARGET_SOUND_ID,
116 soundPool.load(context, R.raw.target_hit, 1));
117 soundMap.put(CANNON_SOUND_ID,
118 soundPool.load(context, R.raw.cannon_fire, 1 ));
119 soundMap.put(BLOCKER_SOUND_ID,
120 soundPool.load(context, R.raw.blocker_hit, 1 ));
121
122 // construct Paints for drawing text, cannonball, cannon,
123 // blocker and target; these are configured in method onSizeChanged
124 textPaint = new Paint();
125 cannonPaint = new Paint();
126 cannonballPaint = new Paint();
127 blockerPaint = new Paint();
128 targetPaint = new Paint();
129 backgroundPaint = new Paint();
130 } // end CannonView constructor
131
Line 100 registers this
(i.e., the CannonView
) as the object that implements SurfaceHolder.Callback
to receive the method calls that indicate when the SurfaceView
is created, updated and destroyed. Inherited SurfaceView
method getHolder returns the SurfaceHolder
object for managing the SurfaceView
, and SurfaceHolder
method addCallback stores the object that implements interface SurfaceHolder.Callback
.
Lines 103–105 create the blocker
and target
as Line
s and the cannonball
as a Point
. Next, we create boolean
array hitStates
to keep track of which of the target’s seven pieces have been hit (and thus should not be drawn).
Lines 111–120 configure the sounds that we use in the app. First, we create the SoundPool
that’s used to load and play the app’s sound effects. The constructor’s first argument represents the maximum number of simultaneous sound streams that can play at once. We play only one sound at a time, so we pass 1
. The second argument specifies which audio stream will be used to play the sounds. There are seven sound streams identified by constants in class AudioManager
, but the documentation for class SoundPool
recommends using the stream for playing music (AudioManager.STREAM_MUSIC
) for sound in games. The last argument represents the sound quality, but the documentation indicates that this value is not currently used and 0
should be specified as the default value.
Line 114 creates a SparseIntArray
(soundMap
), which maps integer keys to integer values. SparseIntArray
is similar to—but more efficient than—a HashMap<Integer, Integer>
for small numbers of key–value pairs. In this case, we map the sound keys (defined in lines Fig. 6.8, 79–81) to the loaded sounds’ IDs, which are represented by the return values of the SoundPool
’s load method (called in Fig. 6.9, lines 116, 118 and 120). Each sound ID can be used to play a sound (and later to return its resources to the system). SoundPool
method load
receives three arguments—the application’s Context
, a resource ID representing the sound file to load and the sound’s priority. According to the documentation for this method, the last argument is not currently used and should be specified as 1
.
Lines 124–129 create the Paint
objects that are used when drawing the game’s elements. We configure these in method onSizeChanged
(Section 6.8.4), because some of the Paint
settings depend on scaling the game elements based on the device’s screen size.