1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.keyboard; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.graphics.Bitmap; 22 import android.graphics.Canvas; 23 import android.graphics.Color; 24 import android.graphics.Paint; 25 import android.graphics.Paint.Align; 26 import android.graphics.PorterDuff; 27 import android.graphics.Rect; 28 import android.graphics.Region; 29 import android.graphics.Typeface; 30 import android.graphics.drawable.Drawable; 31 import android.graphics.drawable.NinePatchDrawable; 32 import android.text.TextUtils; 33 import android.util.AttributeSet; 34 import android.view.View; 35 36 import com.android.inputmethod.keyboard.internal.KeyDrawParams; 37 import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; 38 import com.android.inputmethod.latin.Constants; 39 import com.android.inputmethod.latin.R; 40 import com.android.inputmethod.latin.utils.TypefaceUtils; 41 42 import java.util.HashSet; 43 44 /** 45 * A view that renders a virtual {@link Keyboard}. 46 * 47 * @attr ref R.styleable#KeyboardView_keyBackground 48 * @attr ref R.styleable#KeyboardView_functionalKeyBackground 49 * @attr ref R.styleable#KeyboardView_spacebarBackground 50 * @attr ref R.styleable#KeyboardView_spacebarIconWidthRatio 51 * @attr ref R.styleable#Keyboard_Key_keyLabelFlags 52 * @attr ref R.styleable#KeyboardView_keyHintLetterPadding 53 * @attr ref R.styleable#KeyboardView_keyPopupHintLetter 54 * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding 55 * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding 56 * @attr ref R.styleable#KeyboardView_keyTextShadowRadius 57 * @attr ref R.styleable#KeyboardView_verticalCorrection 58 * @attr ref R.styleable#Keyboard_Key_keyTypeface 59 * @attr ref R.styleable#Keyboard_Key_keyLetterSize 60 * @attr ref R.styleable#Keyboard_Key_keyLabelSize 61 * @attr ref R.styleable#Keyboard_Key_keyLargeLetterRatio 62 * @attr ref R.styleable#Keyboard_Key_keyLargeLabelRatio 63 * @attr ref R.styleable#Keyboard_Key_keyHintLetterRatio 64 * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintRatio 65 * @attr ref R.styleable#Keyboard_Key_keyHintLabelRatio 66 * @attr ref R.styleable#Keyboard_Key_keyLabelOffCenterRatio 67 * @attr ref R.styleable#Keyboard_Key_keyHintLabelOffCenterRatio 68 * @attr ref R.styleable#Keyboard_Key_keyPreviewTextRatio 69 * @attr ref R.styleable#Keyboard_Key_keyTextColor 70 * @attr ref R.styleable#Keyboard_Key_keyTextColorDisabled 71 * @attr ref R.styleable#Keyboard_Key_keyTextShadowColor 72 * @attr ref R.styleable#Keyboard_Key_keyHintLetterColor 73 * @attr ref R.styleable#Keyboard_Key_keyHintLabelColor 74 * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintInactivatedColor 75 * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintActivatedColor 76 * @attr ref R.styleable#Keyboard_Key_keyPreviewTextColor 77 */ 78 public class KeyboardView extends View { 79 // XML attributes 80 private final KeyVisualAttributes mKeyVisualAttributes; 81 // Default keyLabelFlags from {@link KeyboardTheme}. 82 // Currently only "alignHintLabelToBottom" is supported. 83 private final int mDefaultKeyLabelFlags; 84 private final float mKeyHintLetterPadding; 85 private final String mKeyPopupHintLetter; 86 private final float mKeyPopupHintLetterPadding; 87 private final float mKeyShiftedLetterHintPadding; 88 private final float mKeyTextShadowRadius; 89 private final float mVerticalCorrection; 90 private final Drawable mKeyBackground; 91 private final Drawable mFunctionalKeyBackground; 92 private final Drawable mSpacebarBackground; 93 private final float mSpacebarIconWidthRatio; 94 private final Rect mKeyBackgroundPadding = new Rect(); 95 private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; 96 97 // The maximum key label width in the proportion to the key width. 98 private static final float MAX_LABEL_RATIO = 0.90f; 99 100 // Main keyboard 101 private Keyboard mKeyboard; 102 protected final KeyDrawParams mKeyDrawParams = new KeyDrawParams(); 103 104 // Drawing 105 /** True if all keys should be drawn */ 106 private boolean mInvalidateAllKeys; 107 /** The keys that should be drawn */ 108 private final HashSet<Key> mInvalidatedKeys = new HashSet<>(); 109 /** The working rectangle variable */ 110 private final Rect mWorkingRect = new Rect(); 111 /** The keyboard bitmap buffer for faster updates */ 112 /** The clip region to draw keys */ 113 private final Region mClipRegion = new Region(); 114 private Bitmap mOffscreenBuffer; 115 /** The canvas for the above mutable keyboard bitmap */ 116 private final Canvas mOffscreenCanvas = new Canvas(); 117 private final Paint mPaint = new Paint(); 118 private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); 119 public KeyboardView(final Context context, final AttributeSet attrs) { 120 this(context, attrs, R.attr.keyboardViewStyle); 121 } 122 123 public KeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { 124 super(context, attrs, defStyle); 125 126 final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs, 127 R.styleable.KeyboardView, defStyle, R.style.KeyboardView); 128 mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground); 129 mKeyBackground.getPadding(mKeyBackgroundPadding); 130 final Drawable functionalKeyBackground = keyboardViewAttr.getDrawable( 131 R.styleable.KeyboardView_functionalKeyBackground); 132 mFunctionalKeyBackground = (functionalKeyBackground != null) ? functionalKeyBackground 133 : mKeyBackground; 134 final Drawable spacebarBackground = keyboardViewAttr.getDrawable( 135 R.styleable.KeyboardView_spacebarBackground); 136 mSpacebarBackground = (spacebarBackground != null) ? spacebarBackground : mKeyBackground; 137 mSpacebarIconWidthRatio = keyboardViewAttr.getFloat( 138 R.styleable.KeyboardView_spacebarIconWidthRatio, 1.0f); 139 mKeyHintLetterPadding = keyboardViewAttr.getDimension( 140 R.styleable.KeyboardView_keyHintLetterPadding, 0.0f); 141 mKeyPopupHintLetter = keyboardViewAttr.getString( 142 R.styleable.KeyboardView_keyPopupHintLetter); 143 mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension( 144 R.styleable.KeyboardView_keyPopupHintLetterPadding, 0.0f); 145 mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension( 146 R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0.0f); 147 mKeyTextShadowRadius = keyboardViewAttr.getFloat( 148 R.styleable.KeyboardView_keyTextShadowRadius, KET_TEXT_SHADOW_RADIUS_DISABLED); 149 mVerticalCorrection = keyboardViewAttr.getDimension( 150 R.styleable.KeyboardView_verticalCorrection, 0.0f); 151 keyboardViewAttr.recycle(); 152 153 final TypedArray keyAttr = context.obtainStyledAttributes(attrs, 154 R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView); 155 mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0); 156 mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); 157 keyAttr.recycle(); 158 159 mPaint.setAntiAlias(true); 160 } 161 162 public KeyVisualAttributes getKeyVisualAttribute() { 163 return mKeyVisualAttributes; 164 } 165 166 private static void blendAlpha(final Paint paint, final int alpha) { 167 final int color = paint.getColor(); 168 paint.setARGB((paint.getAlpha() * alpha) / Constants.Color.ALPHA_OPAQUE, 169 Color.red(color), Color.green(color), Color.blue(color)); 170 } 171 172 public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { 173 if (!enabled) return; 174 // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off? 175 setLayerType(LAYER_TYPE_HARDWARE, null); 176 } 177 178 /** 179 * Attaches a keyboard to this view. The keyboard can be switched at any time and the 180 * view will re-layout itself to accommodate the keyboard. 181 * @see Keyboard 182 * @see #getKeyboard() 183 * @param keyboard the keyboard to display in this view 184 */ 185 public void setKeyboard(final Keyboard keyboard) { 186 mKeyboard = keyboard; 187 final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; 188 mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes); 189 mKeyDrawParams.updateParams(keyHeight, keyboard.mKeyVisualAttributes); 190 invalidateAllKeys(); 191 requestLayout(); 192 } 193 194 /** 195 * Returns the current keyboard being displayed by this view. 196 * @return the currently attached keyboard 197 * @see #setKeyboard(Keyboard) 198 */ 199 public Keyboard getKeyboard() { 200 return mKeyboard; 201 } 202 203 protected float getVerticalCorrection() { 204 return mVerticalCorrection; 205 } 206 207 protected void updateKeyDrawParams(final int keyHeight) { 208 mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes); 209 } 210 211 @Override 212 protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 213 if (mKeyboard == null) { 214 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 215 return; 216 } 217 // The main keyboard expands to the entire this {@link KeyboardView}. 218 final int width = mKeyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight(); 219 final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); 220 setMeasuredDimension(width, height); 221 } 222 223 @Override 224 protected void onDraw(final Canvas canvas) { 225 super.onDraw(canvas); 226 if (canvas.isHardwareAccelerated()) { 227 onDrawKeyboard(canvas); 228 return; 229 } 230 231 final boolean bufferNeedsUpdates = mInvalidateAllKeys || !mInvalidatedKeys.isEmpty(); 232 if (bufferNeedsUpdates || mOffscreenBuffer == null) { 233 if (maybeAllocateOffscreenBuffer()) { 234 mInvalidateAllKeys = true; 235 // TODO: Stop using the offscreen canvas even when in software rendering 236 mOffscreenCanvas.setBitmap(mOffscreenBuffer); 237 } 238 onDrawKeyboard(mOffscreenCanvas); 239 } 240 canvas.drawBitmap(mOffscreenBuffer, 0.0f, 0.0f, null); 241 } 242 243 private boolean maybeAllocateOffscreenBuffer() { 244 final int width = getWidth(); 245 final int height = getHeight(); 246 if (width == 0 || height == 0) { 247 return false; 248 } 249 if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == width 250 && mOffscreenBuffer.getHeight() == height) { 251 return false; 252 } 253 freeOffscreenBuffer(); 254 mOffscreenBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 255 return true; 256 } 257 258 private void freeOffscreenBuffer() { 259 mOffscreenCanvas.setBitmap(null); 260 mOffscreenCanvas.setMatrix(null); 261 if (mOffscreenBuffer != null) { 262 mOffscreenBuffer.recycle(); 263 mOffscreenBuffer = null; 264 } 265 } 266 267 private void onDrawKeyboard(final Canvas canvas) { 268 if (mKeyboard == null) return; 269 270 final int width = getWidth(); 271 final int height = getHeight(); 272 final Paint paint = mPaint; 273 274 // Calculate clip region and set. 275 final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty(); 276 final boolean isHardwareAccelerated = canvas.isHardwareAccelerated(); 277 // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. 278 if (drawAllKeys || isHardwareAccelerated) { 279 mClipRegion.set(0, 0, width, height); 280 } else { 281 mClipRegion.setEmpty(); 282 for (final Key key : mInvalidatedKeys) { 283 if (mKeyboard.hasKey(key)) { 284 final int x = key.getX() + getPaddingLeft(); 285 final int y = key.getY() + getPaddingTop(); 286 mWorkingRect.set(x, y, x + key.getWidth(), y + key.getHeight()); 287 mClipRegion.union(mWorkingRect); 288 } 289 } 290 } 291 if (!isHardwareAccelerated) { 292 canvas.clipRegion(mClipRegion, Region.Op.REPLACE); 293 // Draw keyboard background. 294 canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); 295 final Drawable background = getBackground(); 296 if (background != null) { 297 background.draw(canvas); 298 } 299 } 300 301 // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. 302 if (drawAllKeys || isHardwareAccelerated) { 303 // Draw all keys. 304 for (final Key key : mKeyboard.getSortedKeys()) { 305 onDrawKey(key, canvas, paint); 306 } 307 } else { 308 // Draw invalidated keys. 309 for (final Key key : mInvalidatedKeys) { 310 if (mKeyboard.hasKey(key)) { 311 onDrawKey(key, canvas, paint); 312 } 313 } 314 } 315 316 mInvalidatedKeys.clear(); 317 mInvalidateAllKeys = false; 318 } 319 320 private void onDrawKey(final Key key, final Canvas canvas, final Paint paint) { 321 final int keyDrawX = key.getDrawX() + getPaddingLeft(); 322 final int keyDrawY = key.getY() + getPaddingTop(); 323 canvas.translate(keyDrawX, keyDrawY); 324 325 final int keyHeight = mKeyboard.mMostCommonKeyHeight - mKeyboard.mVerticalGap; 326 final KeyVisualAttributes attr = key.getVisualAttributes(); 327 final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams(keyHeight, attr); 328 params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE; 329 330 if (!key.isSpacer()) { 331 final Drawable background = key.selectBackgroundDrawable( 332 mKeyBackground, mFunctionalKeyBackground, mSpacebarBackground); 333 onDrawKeyBackground(key, canvas, background); 334 } 335 onDrawKeyTopVisuals(key, canvas, paint, params); 336 337 canvas.translate(-keyDrawX, -keyDrawY); 338 } 339 340 // Draw key background. 341 protected void onDrawKeyBackground(final Key key, final Canvas canvas, 342 final Drawable background) { 343 final int keyWidth = key.getDrawWidth(); 344 final int keyHeight = key.getHeight(); 345 final int bgWidth, bgHeight, bgX, bgY; 346 if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags) 347 // HACK: To disable expanding normal/functional key background. 348 && !key.hasCustomActionLabel()) { 349 final int intrinsicWidth = background.getIntrinsicWidth(); 350 final int intrinsicHeight = background.getIntrinsicHeight(); 351 final float minScale = Math.min( 352 keyWidth / (float)intrinsicWidth, keyHeight / (float)intrinsicHeight); 353 bgWidth = (int)(intrinsicWidth * minScale); 354 bgHeight = (int)(intrinsicHeight * minScale); 355 bgX = (keyWidth - bgWidth) / 2; 356 bgY = (keyHeight - bgHeight) / 2; 357 } else { 358 final Rect padding = mKeyBackgroundPadding; 359 bgWidth = keyWidth + padding.left + padding.right; 360 bgHeight = keyHeight + padding.top + padding.bottom; 361 bgX = -padding.left; 362 bgY = -padding.top; 363 } 364 final Rect bounds = background.getBounds(); 365 if (bgWidth != bounds.right || bgHeight != bounds.bottom) { 366 background.setBounds(0, 0, bgWidth, bgHeight); 367 } 368 canvas.translate(bgX, bgY); 369 background.draw(canvas); 370 canvas.translate(-bgX, -bgY); 371 } 372 373 // Draw key top visuals. 374 protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, 375 final KeyDrawParams params) { 376 final int keyWidth = key.getDrawWidth(); 377 final int keyHeight = key.getHeight(); 378 final float centerX = keyWidth * 0.5f; 379 final float centerY = keyHeight * 0.5f; 380 381 // Draw key label. 382 final Drawable icon = key.getIcon(mKeyboard.mIconsSet, params.mAnimAlpha); 383 float labelX = centerX; 384 float labelBaseline = centerY; 385 final String label = key.getLabel(); 386 if (label != null) { 387 paint.setTypeface(key.selectTypeface(params)); 388 paint.setTextSize(key.selectTextSize(params)); 389 final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); 390 final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); 391 392 // Vertical label text alignment. 393 labelBaseline = centerY + labelCharHeight / 2.0f; 394 395 // Horizontal label text alignment 396 if (key.isAlignLabelOffCenter()) { 397 // The label is placed off center of the key. Used mainly on "phone number" layout. 398 labelX = centerX + params.mLabelOffCenterRatio * labelCharWidth; 399 paint.setTextAlign(Align.LEFT); 400 } else { 401 labelX = centerX; 402 paint.setTextAlign(Align.CENTER); 403 } 404 if (key.needsAutoXScale()) { 405 final float ratio = Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / 406 TypefaceUtils.getStringWidth(label, paint)); 407 if (key.needsAutoScale()) { 408 final float autoSize = paint.getTextSize() * ratio; 409 paint.setTextSize(autoSize); 410 } else { 411 paint.setTextScaleX(ratio); 412 } 413 } 414 415 if (key.isEnabled()) { 416 paint.setColor(key.selectTextColor(params)); 417 // Set a drop shadow for the text if the shadow radius is positive value. 418 if (mKeyTextShadowRadius > 0.0f) { 419 paint.setShadowLayer(mKeyTextShadowRadius, 0.0f, 0.0f, params.mTextShadowColor); 420 } else { 421 paint.clearShadowLayer(); 422 } 423 } else { 424 // Make label invisible 425 paint.setColor(Color.TRANSPARENT); 426 paint.clearShadowLayer(); 427 } 428 blendAlpha(paint, params.mAnimAlpha); 429 canvas.drawText(label, 0, label.length(), labelX, labelBaseline, paint); 430 // Turn off drop shadow and reset x-scale. 431 paint.clearShadowLayer(); 432 paint.setTextScaleX(1.0f); 433 } 434 435 // Draw hint label. 436 final String hintLabel = key.getHintLabel(); 437 if (hintLabel != null) { 438 paint.setTextSize(key.selectHintTextSize(params)); 439 paint.setColor(key.selectHintTextColor(params)); 440 // TODO: Should add a way to specify type face for hint letters 441 paint.setTypeface(Typeface.DEFAULT_BOLD); 442 blendAlpha(paint, params.mAnimAlpha); 443 final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); 444 final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); 445 final float hintX, hintBaseline; 446 if (key.hasHintLabel()) { 447 // The hint label is placed just right of the key label. Used mainly on 448 // "phone number" layout. 449 hintX = labelX + params.mHintLabelOffCenterRatio * labelCharWidth; 450 if (key.isAlignHintLabelToBottom(mDefaultKeyLabelFlags)) { 451 hintBaseline = labelBaseline; 452 } else { 453 hintBaseline = centerY + labelCharHeight / 2.0f; 454 } 455 paint.setTextAlign(Align.LEFT); 456 } else if (key.hasShiftedLetterHint()) { 457 // The hint label is placed at top-right corner of the key. Used mainly on tablet. 458 hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f; 459 paint.getFontMetrics(mFontMetrics); 460 hintBaseline = -mFontMetrics.top; 461 paint.setTextAlign(Align.CENTER); 462 } else { // key.hasHintLetter() 463 // The hint letter is placed at top-right corner of the key. Used mainly on phone. 464 final float hintDigitWidth = TypefaceUtils.getReferenceDigitWidth(paint); 465 final float hintLabelWidth = TypefaceUtils.getStringWidth(hintLabel, paint); 466 hintX = keyWidth - mKeyHintLetterPadding 467 - Math.max(hintDigitWidth, hintLabelWidth) / 2.0f; 468 hintBaseline = -paint.ascent(); 469 paint.setTextAlign(Align.CENTER); 470 } 471 final float adjustmentY = params.mHintLabelVerticalAdjustment * labelCharHeight; 472 canvas.drawText( 473 hintLabel, 0, hintLabel.length(), hintX, hintBaseline + adjustmentY, paint); 474 } 475 476 // Draw key icon. 477 if (label == null && icon != null) { 478 final int iconWidth; 479 if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) { 480 iconWidth = (int)(keyWidth * mSpacebarIconWidthRatio); 481 } else { 482 iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth); 483 } 484 final int iconHeight = icon.getIntrinsicHeight(); 485 final int iconY; 486 if (key.isAlignIconToBottom()) { 487 iconY = keyHeight - iconHeight; 488 } else { 489 iconY = (keyHeight - iconHeight) / 2; // Align vertically center. 490 } 491 final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center. 492 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 493 } 494 495 if (key.hasPopupHint() && key.getMoreKeys() != null) { 496 drawKeyPopupHint(key, canvas, paint, params); 497 } 498 } 499 500 // Draw popup hint "..." at the bottom right corner of the key. 501 protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint, 502 final KeyDrawParams params) { 503 if (TextUtils.isEmpty(mKeyPopupHintLetter)) { 504 return; 505 } 506 final int keyWidth = key.getDrawWidth(); 507 final int keyHeight = key.getHeight(); 508 509 paint.setTypeface(params.mTypeface); 510 paint.setTextSize(params.mHintLetterSize); 511 paint.setColor(params.mHintLabelColor); 512 paint.setTextAlign(Align.CENTER); 513 final float hintX = keyWidth - mKeyHintLetterPadding 514 - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f; 515 final float hintY = keyHeight - mKeyPopupHintLetterPadding; 516 canvas.drawText(mKeyPopupHintLetter, hintX, hintY, paint); 517 } 518 519 protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x, 520 final int y, final int width, final int height) { 521 canvas.translate(x, y); 522 icon.setBounds(0, 0, width, height); 523 icon.draw(canvas); 524 canvas.translate(-x, -y); 525 } 526 527 public Paint newLabelPaint(final Key key) { 528 final Paint paint = new Paint(); 529 paint.setAntiAlias(true); 530 if (key == null) { 531 paint.setTypeface(mKeyDrawParams.mTypeface); 532 paint.setTextSize(mKeyDrawParams.mLabelSize); 533 } else { 534 paint.setColor(key.selectTextColor(mKeyDrawParams)); 535 paint.setTypeface(key.selectTypeface(mKeyDrawParams)); 536 paint.setTextSize(key.selectTextSize(mKeyDrawParams)); 537 } 538 return paint; 539 } 540 541 /** 542 * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient 543 * because the keyboard renders the keys to an off-screen buffer and an invalidate() only 544 * draws the cached buffer. 545 * @see #invalidateKey(Key) 546 */ 547 public void invalidateAllKeys() { 548 mInvalidatedKeys.clear(); 549 mInvalidateAllKeys = true; 550 invalidate(); 551 } 552 553 /** 554 * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only 555 * one key is changing it's content. Any changes that affect the position or size of the key 556 * may not be honored. 557 * @param key key in the attached {@link Keyboard}. 558 * @see #invalidateAllKeys 559 */ 560 public void invalidateKey(final Key key) { 561 if (mInvalidateAllKeys) return; 562 if (key == null) return; 563 mInvalidatedKeys.add(key); 564 final int x = key.getX() + getPaddingLeft(); 565 final int y = key.getY() + getPaddingTop(); 566 invalidate(x, y, x + key.getWidth(), y + key.getHeight()); 567 } 568 569 @Override 570 protected void onDetachedFromWindow() { 571 super.onDetachedFromWindow(); 572 freeOffscreenBuffer(); 573 } 574 575 public void deallocateMemory() { 576 freeOffscreenBuffer(); 577 } 578 } 579