KeyboardView.java revision db776143d940b28abb1b0aa1dcab62a945571990
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 17package com.android.inputmethod.keyboard; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Bitmap; 22import android.graphics.Canvas; 23import android.graphics.Color; 24import android.graphics.Paint; 25import android.graphics.Paint.Align; 26import android.graphics.PorterDuff; 27import android.graphics.Rect; 28import android.graphics.Region; 29import android.graphics.Typeface; 30import android.graphics.drawable.Drawable; 31import android.graphics.drawable.NinePatchDrawable; 32import android.text.TextUtils; 33import android.util.AttributeSet; 34import android.view.View; 35 36import com.android.inputmethod.keyboard.internal.KeyDrawParams; 37import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; 38import com.android.inputmethod.latin.Constants; 39import com.android.inputmethod.latin.R; 40import com.android.inputmethod.latin.utils.TypefaceUtils; 41 42import 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 */ 78public 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 Rect padding = mKeyBackgroundPadding; 344 final int bgWidth = key.getDrawWidth() + padding.left + padding.right; 345 final int bgHeight = key.getHeight() + padding.top + padding.bottom; 346 final int bgX = -padding.left; 347 final int bgY = -padding.top; 348 final Rect bounds = background.getBounds(); 349 if (bgWidth != bounds.right || bgHeight != bounds.bottom) { 350 background.setBounds(0, 0, bgWidth, bgHeight); 351 } 352 canvas.translate(bgX, bgY); 353 background.draw(canvas); 354 canvas.translate(-bgX, -bgY); 355 } 356 357 // Draw key top visuals. 358 protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, 359 final KeyDrawParams params) { 360 final int keyWidth = key.getDrawWidth(); 361 final int keyHeight = key.getHeight(); 362 final float centerX = keyWidth * 0.5f; 363 final float centerY = keyHeight * 0.5f; 364 365 // Draw key label. 366 final Drawable icon = key.getIcon(mKeyboard.mIconsSet, params.mAnimAlpha); 367 float labelX = centerX; 368 float labelBaseline = centerY; 369 final String label = key.getLabel(); 370 if (label != null) { 371 paint.setTypeface(key.selectTypeface(params)); 372 paint.setTextSize(key.selectTextSize(params)); 373 final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); 374 final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); 375 376 // Vertical label text alignment. 377 labelBaseline = centerY + labelCharHeight / 2.0f; 378 379 // Horizontal label text alignment 380 if (key.isAlignLabelOffCenter()) { 381 // The label is placed off center of the key. Used mainly on "phone number" layout. 382 labelX = centerX + params.mLabelOffCenterRatio * labelCharWidth; 383 paint.setTextAlign(Align.LEFT); 384 } else { 385 labelX = centerX; 386 paint.setTextAlign(Align.CENTER); 387 } 388 if (key.needsAutoXScale()) { 389 final float ratio = Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / 390 TypefaceUtils.getStringWidth(label, paint)); 391 if (key.needsAutoScale()) { 392 final float autoSize = paint.getTextSize() * ratio; 393 paint.setTextSize(autoSize); 394 } else { 395 paint.setTextScaleX(ratio); 396 } 397 } 398 399 if (key.isEnabled()) { 400 paint.setColor(key.selectTextColor(params)); 401 // Set a drop shadow for the text if the shadow radius is positive value. 402 if (mKeyTextShadowRadius > 0.0f) { 403 paint.setShadowLayer(mKeyTextShadowRadius, 0.0f, 0.0f, params.mTextShadowColor); 404 } else { 405 paint.clearShadowLayer(); 406 } 407 } else { 408 // Make label invisible 409 paint.setColor(Color.TRANSPARENT); 410 paint.clearShadowLayer(); 411 } 412 blendAlpha(paint, params.mAnimAlpha); 413 canvas.drawText(label, 0, label.length(), labelX, labelBaseline, paint); 414 // Turn off drop shadow and reset x-scale. 415 paint.clearShadowLayer(); 416 paint.setTextScaleX(1.0f); 417 } 418 419 // Draw hint label. 420 final String hintLabel = key.getHintLabel(); 421 if (hintLabel != null) { 422 paint.setTextSize(key.selectHintTextSize(params)); 423 paint.setColor(key.selectHintTextColor(params)); 424 // TODO: Should add a way to specify type face for hint letters 425 paint.setTypeface(Typeface.DEFAULT_BOLD); 426 blendAlpha(paint, params.mAnimAlpha); 427 final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); 428 final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); 429 final float hintX, hintBaseline; 430 if (key.hasHintLabel()) { 431 // The hint label is placed just right of the key label. Used mainly on 432 // "phone number" layout. 433 hintX = labelX + params.mHintLabelOffCenterRatio * labelCharWidth; 434 if (key.isAlignHintLabelToBottom(mDefaultKeyLabelFlags)) { 435 hintBaseline = labelBaseline; 436 } else { 437 hintBaseline = centerY + labelCharHeight / 2.0f; 438 } 439 paint.setTextAlign(Align.LEFT); 440 } else if (key.hasShiftedLetterHint()) { 441 // The hint label is placed at top-right corner of the key. Used mainly on tablet. 442 hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f; 443 paint.getFontMetrics(mFontMetrics); 444 hintBaseline = -mFontMetrics.top; 445 paint.setTextAlign(Align.CENTER); 446 } else { // key.hasHintLetter() 447 // The hint letter is placed at top-right corner of the key. Used mainly on phone. 448 final float hintDigitWidth = TypefaceUtils.getReferenceDigitWidth(paint); 449 final float hintLabelWidth = TypefaceUtils.getStringWidth(hintLabel, paint); 450 hintX = keyWidth - mKeyHintLetterPadding 451 - Math.max(hintDigitWidth, hintLabelWidth) / 2.0f; 452 hintBaseline = -paint.ascent(); 453 paint.setTextAlign(Align.CENTER); 454 } 455 final float adjustmentY = params.mHintLabelVerticalAdjustment * labelCharHeight; 456 canvas.drawText( 457 hintLabel, 0, hintLabel.length(), hintX, hintBaseline + adjustmentY, paint); 458 } 459 460 // Draw key icon. 461 if (label == null && icon != null) { 462 final int iconWidth; 463 if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) { 464 iconWidth = (int)(keyWidth * mSpacebarIconWidthRatio); 465 } else { 466 iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth); 467 } 468 final int iconHeight = icon.getIntrinsicHeight(); 469 final int iconY; 470 if (key.isAlignIconToBottom()) { 471 iconY = keyHeight - iconHeight; 472 } else { 473 iconY = (keyHeight - iconHeight) / 2; // Align vertically center. 474 } 475 final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center. 476 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 477 } 478 479 if (key.hasPopupHint() && key.getMoreKeys() != null) { 480 drawKeyPopupHint(key, canvas, paint, params); 481 } 482 } 483 484 // Draw popup hint "..." at the bottom right corner of the key. 485 protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint, 486 final KeyDrawParams params) { 487 if (TextUtils.isEmpty(mKeyPopupHintLetter)) { 488 return; 489 } 490 final int keyWidth = key.getDrawWidth(); 491 final int keyHeight = key.getHeight(); 492 493 paint.setTypeface(params.mTypeface); 494 paint.setTextSize(params.mHintLetterSize); 495 paint.setColor(params.mHintLabelColor); 496 paint.setTextAlign(Align.CENTER); 497 final float hintX = keyWidth - mKeyHintLetterPadding 498 - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f; 499 final float hintY = keyHeight - mKeyPopupHintLetterPadding; 500 canvas.drawText(mKeyPopupHintLetter, hintX, hintY, paint); 501 } 502 503 protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x, 504 final int y, final int width, final int height) { 505 canvas.translate(x, y); 506 icon.setBounds(0, 0, width, height); 507 icon.draw(canvas); 508 canvas.translate(-x, -y); 509 } 510 511 public Paint newLabelPaint(final Key key) { 512 final Paint paint = new Paint(); 513 paint.setAntiAlias(true); 514 if (key == null) { 515 paint.setTypeface(mKeyDrawParams.mTypeface); 516 paint.setTextSize(mKeyDrawParams.mLabelSize); 517 } else { 518 paint.setColor(key.selectTextColor(mKeyDrawParams)); 519 paint.setTypeface(key.selectTypeface(mKeyDrawParams)); 520 paint.setTextSize(key.selectTextSize(mKeyDrawParams)); 521 } 522 return paint; 523 } 524 525 /** 526 * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient 527 * because the keyboard renders the keys to an off-screen buffer and an invalidate() only 528 * draws the cached buffer. 529 * @see #invalidateKey(Key) 530 */ 531 public void invalidateAllKeys() { 532 mInvalidatedKeys.clear(); 533 mInvalidateAllKeys = true; 534 invalidate(); 535 } 536 537 /** 538 * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only 539 * one key is changing it's content. Any changes that affect the position or size of the key 540 * may not be honored. 541 * @param key key in the attached {@link Keyboard}. 542 * @see #invalidateAllKeys 543 */ 544 public void invalidateKey(final Key key) { 545 if (mInvalidateAllKeys) return; 546 if (key == null) return; 547 mInvalidatedKeys.add(key); 548 final int x = key.getX() + getPaddingLeft(); 549 final int y = key.getY() + getPaddingTop(); 550 invalidate(x, y, x + key.getWidth(), y + key.getHeight()); 551 } 552 553 @Override 554 protected void onDetachedFromWindow() { 555 super.onDetachedFromWindow(); 556 freeOffscreenBuffer(); 557 } 558 559 public void deallocateMemory() { 560 freeOffscreenBuffer(); 561 } 562} 563