KeyboardView.java revision 51ee5a47b8af04a14e1756c900b42784f948fff5
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.util.AttributeSet; 33import android.view.View; 34 35import com.android.inputmethod.keyboard.internal.KeyDrawParams; 36import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; 37import com.android.inputmethod.latin.Constants; 38import com.android.inputmethod.latin.R; 39import com.android.inputmethod.latin.utils.TypefaceUtils; 40 41import java.util.HashSet; 42 43/** 44 * A view that renders a virtual {@link Keyboard}. 45 * 46 * @attr ref R.styleable#KeyboardView_keyBackground 47 * @attr ref R.styleable#KeyboardView_functionalKeyBackground 48 * @attr ref R.styleable#KeyboardView_spacebarBackground 49 * @attr ref R.styleable#KeyboardView_spacebarIconWidthRatio 50 * @attr ref R.styleable#KeyboardView_keyLabelHorizontalPadding 51 * @attr ref R.styleable#KeyboardView_keyHintLetterPadding 52 * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding 53 * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding 54 * @attr ref R.styleable#KeyboardView_keyTextShadowRadius 55 * @attr ref R.styleable#KeyboardView_verticalCorrection 56 * @attr ref R.styleable#Keyboard_Key_keyTypeface 57 * @attr ref R.styleable#Keyboard_Key_keyLetterSize 58 * @attr ref R.styleable#Keyboard_Key_keyLabelSize 59 * @attr ref R.styleable#Keyboard_Key_keyLargeLetterRatio 60 * @attr ref R.styleable#Keyboard_Key_keyLargeLabelRatio 61 * @attr ref R.styleable#Keyboard_Key_keyHintLetterRatio 62 * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintRatio 63 * @attr ref R.styleable#Keyboard_Key_keyHintLabelRatio 64 * @attr ref R.styleable#Keyboard_Key_keyPreviewTextRatio 65 * @attr ref R.styleable#Keyboard_Key_keyTextColor 66 * @attr ref R.styleable#Keyboard_Key_keyTextColorDisabled 67 * @attr ref R.styleable#Keyboard_Key_keyTextShadowColor 68 * @attr ref R.styleable#Keyboard_Key_keyHintLetterColor 69 * @attr ref R.styleable#Keyboard_Key_keyHintLabelColor 70 * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintInactivatedColor 71 * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintActivatedColor 72 * @attr ref R.styleable#Keyboard_Key_keyPreviewTextColor 73 */ 74public class KeyboardView extends View { 75 // XML attributes 76 private final KeyVisualAttributes mKeyVisualAttributes; 77 private final int mKeyLabelHorizontalPadding; 78 private final float mKeyHintLetterPadding; 79 private final float mKeyPopupHintLetterPadding; 80 private final float mKeyShiftedLetterHintPadding; 81 private final float mKeyTextShadowRadius; 82 private final float mVerticalCorrection; 83 private final Drawable mKeyBackground; 84 private final Drawable mFunctionalKeyBackground; 85 private final Drawable mSpacebarBackground; 86 private final float mSpacebarIconWidthRatio; 87 private final Rect mKeyBackgroundPadding = new Rect(); 88 private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; 89 90 // HORIZONTAL ELLIPSIS "...", character for popup hint. 91 private static final String POPUP_HINT_CHAR = "\u2026"; 92 93 // Margin between the label and the icon on a key that has both of them. 94 // Specified by the fraction of the key width. 95 // TODO: Use resource parameter for this value. 96 private static final float LABEL_ICON_MARGIN = 0.05f; 97 98 // The maximum key label width in the proportion to the key width. 99 private static final float MAX_LABEL_RATIO = 0.90f; 100 101 // Main keyboard 102 private Keyboard mKeyboard; 103 protected final KeyDrawParams mKeyDrawParams = new KeyDrawParams(); 104 105 // Drawing 106 /** True if all keys should be drawn */ 107 private boolean mInvalidateAllKeys; 108 /** The keys that should be drawn */ 109 private final HashSet<Key> mInvalidatedKeys = new HashSet<>(); 110 /** The working rectangle variable */ 111 private final Rect mWorkingRect = new Rect(); 112 /** The keyboard bitmap buffer for faster updates */ 113 /** The clip region to draw keys */ 114 private final Region mClipRegion = new Region(); 115 private Bitmap mOffscreenBuffer; 116 /** The canvas for the above mutable keyboard bitmap */ 117 private final Canvas mOffscreenCanvas = new Canvas(); 118 private final Paint mPaint = new Paint(); 119 private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); 120 public KeyboardView(final Context context, final AttributeSet attrs) { 121 this(context, attrs, R.attr.keyboardViewStyle); 122 } 123 124 public KeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { 125 super(context, attrs, defStyle); 126 127 final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs, 128 R.styleable.KeyboardView, defStyle, R.style.KeyboardView); 129 mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground); 130 mKeyBackground.getPadding(mKeyBackgroundPadding); 131 final Drawable functionalKeyBackground = keyboardViewAttr.getDrawable( 132 R.styleable.KeyboardView_functionalKeyBackground); 133 mFunctionalKeyBackground = (functionalKeyBackground != null) ? functionalKeyBackground 134 : mKeyBackground; 135 final Drawable spacebarBackground = keyboardViewAttr.getDrawable( 136 R.styleable.KeyboardView_spacebarBackground); 137 mSpacebarBackground = (spacebarBackground != null) ? spacebarBackground : mKeyBackground; 138 mSpacebarIconWidthRatio = keyboardViewAttr.getFloat( 139 R.styleable.KeyboardView_spacebarIconWidthRatio, 1.0f); 140 mKeyLabelHorizontalPadding = keyboardViewAttr.getDimensionPixelOffset( 141 R.styleable.KeyboardView_keyLabelHorizontalPadding, 0); 142 mKeyHintLetterPadding = keyboardViewAttr.getDimension( 143 R.styleable.KeyboardView_keyHintLetterPadding, 0.0f); 144 mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension( 145 R.styleable.KeyboardView_keyPopupHintLetterPadding, 0.0f); 146 mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension( 147 R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0.0f); 148 mKeyTextShadowRadius = keyboardViewAttr.getFloat( 149 R.styleable.KeyboardView_keyTextShadowRadius, KET_TEXT_SHADOW_RADIUS_DISABLED); 150 mVerticalCorrection = keyboardViewAttr.getDimension( 151 R.styleable.KeyboardView_verticalCorrection, 0.0f); 152 keyboardViewAttr.recycle(); 153 154 final TypedArray keyAttr = context.obtainStyledAttributes(attrs, 155 R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView); 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 positionX = centerX; 368 final String label = key.getLabel(); 369 if (label != null) { 370 paint.setTypeface(key.selectTypeface(params)); 371 paint.setTextSize(key.selectTextSize(params)); 372 final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); 373 final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); 374 375 // Vertical label text alignment. 376 final float baseline = centerY + labelCharHeight / 2.0f; 377 378 // Horizontal label text alignment 379 float labelWidth = 0.0f; 380 if (key.isAlignLeft()) { 381 positionX = mKeyLabelHorizontalPadding; 382 paint.setTextAlign(Align.LEFT); 383 } else if (key.isAlignRight()) { 384 positionX = keyWidth - mKeyLabelHorizontalPadding; 385 paint.setTextAlign(Align.RIGHT); 386 } else if (key.isAlignLeftOfCenter()) { 387 // TODO: Parameterise this? 388 positionX = centerX - labelCharWidth * 7.0f / 4.0f; 389 paint.setTextAlign(Align.LEFT); 390 } else if (key.hasLabelWithIconLeft() && icon != null) { 391 labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth() 392 + LABEL_ICON_MARGIN * keyWidth; 393 positionX = centerX + labelWidth / 2.0f; 394 paint.setTextAlign(Align.RIGHT); 395 } else if (key.hasLabelWithIconRight() && icon != null) { 396 labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth() 397 + LABEL_ICON_MARGIN * keyWidth; 398 positionX = centerX - labelWidth / 2.0f; 399 paint.setTextAlign(Align.LEFT); 400 } else { 401 positionX = 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(), positionX, baseline, paint); 430 // Turn off drop shadow and reset x-scale. 431 paint.clearShadowLayer(); 432 paint.setTextScaleX(1.0f); 433 434 if (icon != null) { 435 final int iconWidth = icon.getIntrinsicWidth(); 436 final int iconHeight = icon.getIntrinsicHeight(); 437 final int iconY = (keyHeight - iconHeight) / 2; 438 if (key.hasLabelWithIconLeft()) { 439 final int iconX = (int)(centerX - labelWidth / 2.0f); 440 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 441 } else if (key.hasLabelWithIconRight()) { 442 final int iconX = (int)(centerX + labelWidth / 2.0f - iconWidth); 443 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 444 } 445 } 446 } 447 448 // Draw hint label. 449 final String hintLabel = key.getHintLabel(); 450 if (hintLabel != null) { 451 paint.setTextSize(key.selectHintTextSize(params)); 452 paint.setColor(key.selectHintTextColor(params)); 453 // TODO: Should add a way to specify type face for hint letters 454 paint.setTypeface(Typeface.DEFAULT_BOLD); 455 blendAlpha(paint, params.mAnimAlpha); 456 final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); 457 final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); 458 final KeyVisualAttributes visualAttr = key.getVisualAttributes(); 459 final float adjustmentY = (visualAttr == null) ? 0.0f 460 : visualAttr.mHintLabelVerticalAdjustment * labelCharHeight; 461 final float hintX, hintY; 462 if (key.hasHintLabel()) { 463 // The hint label is placed just right of the key label. Used mainly on 464 // "phone number" layout. 465 // TODO: Generalize the following calculations. 466 hintX = positionX + labelCharWidth * 2.0f; 467 hintY = centerY + labelCharHeight / 2.0f; 468 paint.setTextAlign(Align.LEFT); 469 } else if (key.hasShiftedLetterHint()) { 470 // The hint label is placed at top-right corner of the key. Used mainly on tablet. 471 hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f; 472 paint.getFontMetrics(mFontMetrics); 473 hintY = -mFontMetrics.top; 474 paint.setTextAlign(Align.CENTER); 475 } else { // key.hasHintLetter() 476 // The hint letter is placed at top-right corner of the key. Used mainly on phone. 477 final float hintDigitWidth = TypefaceUtils.getReferenceDigitWidth(paint); 478 final float hintLabelWidth = TypefaceUtils.getStringWidth(hintLabel, paint); 479 hintX = keyWidth - mKeyHintLetterPadding 480 - Math.max(hintDigitWidth, hintLabelWidth) / 2.0f; 481 hintY = -paint.ascent(); 482 paint.setTextAlign(Align.CENTER); 483 } 484 canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY + adjustmentY, paint); 485 } 486 487 // Draw key icon. 488 if (label == null && icon != null) { 489 final int iconWidth; 490 if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) { 491 iconWidth = (int)(keyWidth * mSpacebarIconWidthRatio); 492 } else { 493 iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth); 494 } 495 final int iconHeight = icon.getIntrinsicHeight(); 496 final int iconY = key.isAlignButtom() ? keyHeight - iconHeight 497 : (keyHeight - iconHeight) / 2; 498 final int iconX; 499 if (key.isAlignLeft()) { 500 iconX = mKeyLabelHorizontalPadding; 501 } else if (key.isAlignRight()) { 502 iconX = keyWidth - mKeyLabelHorizontalPadding - iconWidth; 503 } else { // Align center 504 iconX = (keyWidth - iconWidth) / 2; 505 } 506 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 507 } 508 509 if (key.hasPopupHint() && key.getMoreKeys() != null) { 510 drawKeyPopupHint(key, canvas, paint, params); 511 } 512 } 513 514 // Draw popup hint "..." at the bottom right corner of the key. 515 protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint, 516 final KeyDrawParams params) { 517 final int keyWidth = key.getDrawWidth(); 518 final int keyHeight = key.getHeight(); 519 520 paint.setTypeface(params.mTypeface); 521 paint.setTextSize(params.mHintLetterSize); 522 paint.setColor(params.mHintLabelColor); 523 paint.setTextAlign(Align.CENTER); 524 final float hintX = keyWidth - mKeyHintLetterPadding 525 - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f; 526 final float hintY = keyHeight - mKeyPopupHintLetterPadding; 527 canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); 528 } 529 530 protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x, 531 final int y, final int width, final int height) { 532 canvas.translate(x, y); 533 icon.setBounds(0, 0, width, height); 534 icon.draw(canvas); 535 canvas.translate(-x, -y); 536 } 537 538 public Paint newLabelPaint(final Key key) { 539 final Paint paint = new Paint(); 540 paint.setAntiAlias(true); 541 if (key == null) { 542 paint.setTypeface(mKeyDrawParams.mTypeface); 543 paint.setTextSize(mKeyDrawParams.mLabelSize); 544 } else { 545 paint.setColor(key.selectTextColor(mKeyDrawParams)); 546 paint.setTypeface(key.selectTypeface(mKeyDrawParams)); 547 paint.setTextSize(key.selectTextSize(mKeyDrawParams)); 548 } 549 return paint; 550 } 551 552 /** 553 * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient 554 * because the keyboard renders the keys to an off-screen buffer and an invalidate() only 555 * draws the cached buffer. 556 * @see #invalidateKey(Key) 557 */ 558 public void invalidateAllKeys() { 559 mInvalidatedKeys.clear(); 560 mInvalidateAllKeys = true; 561 invalidate(); 562 } 563 564 /** 565 * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only 566 * one key is changing it's content. Any changes that affect the position or size of the key 567 * may not be honored. 568 * @param key key in the attached {@link Keyboard}. 569 * @see #invalidateAllKeys 570 */ 571 public void invalidateKey(final Key key) { 572 if (mInvalidateAllKeys) return; 573 if (key == null) return; 574 mInvalidatedKeys.add(key); 575 final int x = key.getX() + getPaddingLeft(); 576 final int y = key.getY() + getPaddingTop(); 577 invalidate(x, y, x + key.getWidth(), y + key.getHeight()); 578 } 579 580 @Override 581 protected void onDetachedFromWindow() { 582 super.onDetachedFromWindow(); 583 freeOffscreenBuffer(); 584 } 585 586 public void deallocateMemory() { 587 freeOffscreenBuffer(); 588 } 589} 590