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(); KeyboardView(final Context context, final AttributeSet attrs)119 public KeyboardView(final Context context, final AttributeSet attrs) { 120 this(context, attrs, R.attr.keyboardViewStyle); 121 } 122 KeyboardView(final Context context, final AttributeSet attrs, final int defStyle)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 getKeyVisualAttribute()162 public KeyVisualAttributes getKeyVisualAttribute() { 163 return mKeyVisualAttributes; 164 } 165 blendAlpha(final Paint paint, final int alpha)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 setHardwareAcceleratedDrawingEnabled(final boolean enabled)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 */ setKeyboard(final Keyboard keyboard)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 */ getKeyboard()199 public Keyboard getKeyboard() { 200 return mKeyboard; 201 } 202 getVerticalCorrection()203 protected float getVerticalCorrection() { 204 return mVerticalCorrection; 205 } 206 updateKeyDrawParams(final int keyHeight)207 protected void updateKeyDrawParams(final int keyHeight) { 208 mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes); 209 } 210 211 @Override onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)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 onDraw(final Canvas canvas)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 maybeAllocateOffscreenBuffer()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 freeOffscreenBuffer()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 onDrawKeyboard(final Canvas canvas)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 onDrawKey(final Key key, final Canvas canvas, final Paint paint)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. onDrawKeyBackground(final Key key, final Canvas canvas, final Drawable 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. onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, final KeyDrawParams params)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. drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint, final KeyDrawParams params)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 drawIcon(final Canvas canvas, final Drawable icon, final int x, final int y, final int width, final int height)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 newLabelPaint(final Key key)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 */ invalidateAllKeys()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 */ invalidateKey(final Key key)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 onDetachedFromWindow()570 protected void onDetachedFromWindow() { 571 super.onDetachedFromWindow(); 572 freeOffscreenBuffer(); 573 } 574 deallocateMemory()575 public void deallocateMemory() { 576 freeOffscreenBuffer(); 577 } 578 } 579