KeyboardView.java revision c13c1adfa72227b0006add5f13f555fbb9c9eb4e
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.util.AttributeSet; 32import android.view.View; 33 34import com.android.inputmethod.keyboard.internal.KeyDrawParams; 35import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; 36import com.android.inputmethod.latin.Constants; 37import com.android.inputmethod.latin.LatinImeLogger; 38import com.android.inputmethod.latin.R; 39import com.android.inputmethod.latin.define.ProductionFlag; 40import com.android.inputmethod.latin.utils.CollectionUtils; 41import com.android.inputmethod.latin.utils.TypefaceUtils; 42import com.android.inputmethod.research.ResearchLogger; 43 44import java.util.HashSet; 45 46/** 47 * A view that renders a virtual {@link Keyboard}. 48 * 49 * @attr ref R.styleable#KeyboardView_keyBackground 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 Rect mKeyBackgroundPadding = new Rect(); 85 86 // HORIZONTAL ELLIPSIS "...", character for popup hint. 87 private static final String POPUP_HINT_CHAR = "\u2026"; 88 89 // Margin between the label and the icon on a key that has both of them. 90 // Specified by the fraction of the key width. 91 // TODO: Use resource parameter for this value. 92 private static final float LABEL_ICON_MARGIN = 0.05f; 93 94 // The maximum key label width in the proportion to the key width. 95 private static final float MAX_LABEL_RATIO = 0.90f; 96 97 // Main keyboard 98 private Keyboard mKeyboard; 99 protected final KeyDrawParams mKeyDrawParams = new KeyDrawParams(); 100 101 // Drawing 102 /** True if all keys should be drawn */ 103 private boolean mInvalidateAllKeys; 104 /** The keys that should be drawn */ 105 private final HashSet<Key> mInvalidatedKeys = CollectionUtils.newHashSet(); 106 /** The working rectangle variable */ 107 private final Rect mWorkingRect = new Rect(); 108 /** The keyboard bitmap buffer for faster updates */ 109 /** The clip region to draw keys */ 110 private final Region mClipRegion = new Region(); 111 private Bitmap mOffscreenBuffer; 112 /** The canvas for the above mutable keyboard bitmap */ 113 private final Canvas mOffscreenCanvas = new Canvas(); 114 private final Paint mPaint = new Paint(); 115 private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); 116 public KeyboardView(final Context context, final AttributeSet attrs) { 117 this(context, attrs, R.attr.keyboardViewStyle); 118 } 119 120 public KeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { 121 super(context, attrs, defStyle); 122 123 final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs, 124 R.styleable.KeyboardView, defStyle, R.style.KeyboardView); 125 mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground); 126 mKeyBackground.getPadding(mKeyBackgroundPadding); 127 mKeyLabelHorizontalPadding = keyboardViewAttr.getDimensionPixelOffset( 128 R.styleable.KeyboardView_keyLabelHorizontalPadding, 0); 129 mKeyHintLetterPadding = keyboardViewAttr.getDimension( 130 R.styleable.KeyboardView_keyHintLetterPadding, 0.0f); 131 mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension( 132 R.styleable.KeyboardView_keyPopupHintLetterPadding, 0.0f); 133 mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension( 134 R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0.0f); 135 mKeyTextShadowRadius = keyboardViewAttr.getFloat( 136 R.styleable.KeyboardView_keyTextShadowRadius, 0.0f); 137 mVerticalCorrection = keyboardViewAttr.getDimension( 138 R.styleable.KeyboardView_verticalCorrection, 0.0f); 139 keyboardViewAttr.recycle(); 140 141 final TypedArray keyAttr = context.obtainStyledAttributes(attrs, 142 R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView); 143 mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); 144 keyAttr.recycle(); 145 146 mPaint.setAntiAlias(true); 147 } 148 149 public KeyVisualAttributes getKeyVisualAttribute() { 150 return mKeyVisualAttributes; 151 } 152 153 private static void blendAlpha(final Paint paint, final int alpha) { 154 final int color = paint.getColor(); 155 paint.setARGB((paint.getAlpha() * alpha) / Constants.Color.ALPHA_OPAQUE, 156 Color.red(color), Color.green(color), Color.blue(color)); 157 } 158 159 public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { 160 if (!enabled) return; 161 // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off? 162 setLayerType(LAYER_TYPE_HARDWARE, null); 163 } 164 165 /** 166 * Attaches a keyboard to this view. The keyboard can be switched at any time and the 167 * view will re-layout itself to accommodate the keyboard. 168 * @see Keyboard 169 * @see #getKeyboard() 170 * @param keyboard the keyboard to display in this view 171 */ 172 public void setKeyboard(final Keyboard keyboard) { 173 mKeyboard = keyboard; 174 LatinImeLogger.onSetKeyboard(keyboard); 175 final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; 176 mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes); 177 mKeyDrawParams.updateParams(keyHeight, keyboard.mKeyVisualAttributes); 178 invalidateAllKeys(); 179 requestLayout(); 180 } 181 182 /** 183 * Returns the current keyboard being displayed by this view. 184 * @return the currently attached keyboard 185 * @see #setKeyboard(Keyboard) 186 */ 187 public Keyboard getKeyboard() { 188 return mKeyboard; 189 } 190 191 protected float getVerticalCorrection() { 192 return mVerticalCorrection; 193 } 194 195 protected void updateKeyDrawParams(final int keyHeight) { 196 mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes); 197 } 198 199 @Override 200 protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 201 if (mKeyboard == null) { 202 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 203 return; 204 } 205 // The main keyboard expands to the entire this {@link KeyboardView}. 206 final int width = mKeyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight(); 207 final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); 208 setMeasuredDimension(width, height); 209 } 210 211 @Override 212 protected void onDraw(final Canvas canvas) { 213 super.onDraw(canvas); 214 if (canvas.isHardwareAccelerated()) { 215 onDrawKeyboard(canvas); 216 return; 217 } 218 219 final boolean bufferNeedsUpdates = mInvalidateAllKeys || !mInvalidatedKeys.isEmpty(); 220 if (bufferNeedsUpdates || mOffscreenBuffer == null) { 221 if (maybeAllocateOffscreenBuffer()) { 222 mInvalidateAllKeys = true; 223 // TODO: Stop using the offscreen canvas even when in software rendering 224 mOffscreenCanvas.setBitmap(mOffscreenBuffer); 225 } 226 onDrawKeyboard(mOffscreenCanvas); 227 } 228 canvas.drawBitmap(mOffscreenBuffer, 0.0f, 0.0f, null); 229 } 230 231 private boolean maybeAllocateOffscreenBuffer() { 232 final int width = getWidth(); 233 final int height = getHeight(); 234 if (width == 0 || height == 0) { 235 return false; 236 } 237 if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == width 238 && mOffscreenBuffer.getHeight() == height) { 239 return false; 240 } 241 freeOffscreenBuffer(); 242 mOffscreenBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 243 return true; 244 } 245 246 private void freeOffscreenBuffer() { 247 mOffscreenCanvas.setBitmap(null); 248 mOffscreenCanvas.setMatrix(null); 249 if (mOffscreenBuffer != null) { 250 mOffscreenBuffer.recycle(); 251 mOffscreenBuffer = null; 252 } 253 } 254 255 private void onDrawKeyboard(final Canvas canvas) { 256 if (mKeyboard == null) return; 257 258 final int width = getWidth(); 259 final int height = getHeight(); 260 final Paint paint = mPaint; 261 262 // Calculate clip region and set. 263 final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty(); 264 final boolean isHardwareAccelerated = canvas.isHardwareAccelerated(); 265 // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. 266 if (drawAllKeys || isHardwareAccelerated) { 267 mClipRegion.set(0, 0, width, height); 268 } else { 269 mClipRegion.setEmpty(); 270 for (final Key key : mInvalidatedKeys) { 271 if (mKeyboard.hasKey(key)) { 272 final int x = key.getX() + getPaddingLeft(); 273 final int y = key.getY() + getPaddingTop(); 274 mWorkingRect.set(x, y, x + key.getWidth(), y + key.getHeight()); 275 mClipRegion.union(mWorkingRect); 276 } 277 } 278 } 279 if (!isHardwareAccelerated) { 280 canvas.clipRegion(mClipRegion, Region.Op.REPLACE); 281 // Draw keyboard background. 282 canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); 283 final Drawable background = getBackground(); 284 if (background != null) { 285 background.draw(canvas); 286 } 287 } 288 289 // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. 290 if (drawAllKeys || isHardwareAccelerated) { 291 // Draw all keys. 292 for (final Key key : mKeyboard.getSortedKeys()) { 293 onDrawKey(key, canvas, paint); 294 } 295 } else { 296 // Draw invalidated keys. 297 for (final Key key : mInvalidatedKeys) { 298 if (mKeyboard.hasKey(key)) { 299 onDrawKey(key, canvas, paint); 300 } 301 } 302 } 303 304 // Research Logging (Development Only Diagnostics) indicator. 305 // TODO: Reimplement using a keyboard background image specific to the ResearchLogger, 306 // and remove this call. 307 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { 308 ResearchLogger.getInstance().paintIndicator(this, paint, canvas, width, height); 309 } 310 311 mInvalidatedKeys.clear(); 312 mInvalidateAllKeys = false; 313 } 314 315 private void onDrawKey(final Key key, final Canvas canvas, final Paint paint) { 316 final int keyDrawX = key.getDrawX() + getPaddingLeft(); 317 final int keyDrawY = key.getY() + getPaddingTop(); 318 canvas.translate(keyDrawX, keyDrawY); 319 320 final int keyHeight = mKeyboard.mMostCommonKeyHeight - mKeyboard.mVerticalGap; 321 final KeyVisualAttributes attr = key.getVisualAttributes(); 322 final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams(keyHeight, attr); 323 params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE; 324 325 if (!key.isSpacer()) { 326 onDrawKeyBackground(key, canvas, mKeyBackground); 327 } 328 onDrawKeyTopVisuals(key, canvas, paint, params); 329 330 canvas.translate(-keyDrawX, -keyDrawY); 331 } 332 333 // Draw key background. 334 protected void onDrawKeyBackground(final Key key, final Canvas canvas, 335 final Drawable background) { 336 final Rect padding = mKeyBackgroundPadding; 337 final int bgWidth = key.getDrawWidth() + padding.left + padding.right; 338 final int bgHeight = key.getHeight() + padding.top + padding.bottom; 339 final int bgX = -padding.left; 340 final int bgY = -padding.top; 341 final int[] drawableState = key.getCurrentDrawableState(); 342 background.setState(drawableState); 343 final Rect bounds = background.getBounds(); 344 if (bgWidth != bounds.right || bgHeight != bounds.bottom) { 345 background.setBounds(0, 0, bgWidth, bgHeight); 346 } 347 canvas.translate(bgX, bgY); 348 background.draw(canvas); 349 if (LatinImeLogger.sVISUALDEBUG) { 350 drawRectangle(canvas, 0.0f, 0.0f, bgWidth, bgHeight, 0x80c00000, new Paint()); 351 } 352 canvas.translate(-bgX, -bgY); 353 } 354 355 // Draw key top visuals. 356 protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, 357 final KeyDrawParams params) { 358 final int keyWidth = key.getDrawWidth(); 359 final int keyHeight = key.getHeight(); 360 final float centerX = keyWidth * 0.5f; 361 final float centerY = keyHeight * 0.5f; 362 363 if (LatinImeLogger.sVISUALDEBUG) { 364 drawRectangle(canvas, 0.0f, 0.0f, keyWidth, keyHeight, 0x800000c0, new Paint()); 365 } 366 367 // Draw key label. 368 final Drawable icon = key.getIcon(mKeyboard.mIconsSet, params.mAnimAlpha); 369 float positionX = centerX; 370 final String label = key.getLabel(); 371 if (label != null) { 372 paint.setTypeface(key.selectTypeface(params)); 373 paint.setTextSize(key.selectTextSize(params)); 374 final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); 375 final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); 376 377 // Vertical label text alignment. 378 final float baseline = centerY + labelCharHeight / 2.0f; 379 380 // Horizontal label text alignment 381 float labelWidth = 0.0f; 382 if (key.isAlignLeft()) { 383 positionX = mKeyLabelHorizontalPadding; 384 paint.setTextAlign(Align.LEFT); 385 } else if (key.isAlignRight()) { 386 positionX = keyWidth - mKeyLabelHorizontalPadding; 387 paint.setTextAlign(Align.RIGHT); 388 } else if (key.isAlignLeftOfCenter()) { 389 // TODO: Parameterise this? 390 positionX = centerX - labelCharWidth * 7.0f / 4.0f; 391 paint.setTextAlign(Align.LEFT); 392 } else if (key.hasLabelWithIconLeft() && icon != null) { 393 labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth() 394 + LABEL_ICON_MARGIN * keyWidth; 395 positionX = centerX + labelWidth / 2.0f; 396 paint.setTextAlign(Align.RIGHT); 397 } else if (key.hasLabelWithIconRight() && icon != null) { 398 labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth() 399 + LABEL_ICON_MARGIN * keyWidth; 400 positionX = centerX - labelWidth / 2.0f; 401 paint.setTextAlign(Align.LEFT); 402 } else { 403 positionX = centerX; 404 paint.setTextAlign(Align.CENTER); 405 } 406 if (key.needsAutoXScale()) { 407 final float ratio = Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / 408 TypefaceUtils.getStringWidth(label, paint)); 409 if (key.needsAutoScale()) { 410 final float autoSize = paint.getTextSize() * ratio; 411 paint.setTextSize(autoSize); 412 } else { 413 paint.setTextScaleX(ratio); 414 } 415 } 416 417 paint.setColor(key.selectTextColor(params)); 418 if (key.isEnabled()) { 419 // Set a drop shadow for the text 420 paint.setShadowLayer(mKeyTextShadowRadius, 0.0f, 0.0f, params.mTextShadowColor); 421 } else { 422 // Make label invisible 423 paint.setColor(Color.TRANSPARENT); 424 } 425 blendAlpha(paint, params.mAnimAlpha); 426 canvas.drawText(label, 0, label.length(), positionX, baseline, paint); 427 // Turn off drop shadow and reset x-scale. 428 paint.setShadowLayer(0.0f, 0.0f, 0.0f, Color.TRANSPARENT); 429 paint.setTextScaleX(1.0f); 430 431 if (icon != null) { 432 final int iconWidth = icon.getIntrinsicWidth(); 433 final int iconHeight = icon.getIntrinsicHeight(); 434 final int iconY = (keyHeight - iconHeight) / 2; 435 if (key.hasLabelWithIconLeft()) { 436 final int iconX = (int)(centerX - labelWidth / 2.0f); 437 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 438 } else if (key.hasLabelWithIconRight()) { 439 final int iconX = (int)(centerX + labelWidth / 2.0f - iconWidth); 440 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 441 } 442 } 443 444 if (LatinImeLogger.sVISUALDEBUG) { 445 final Paint line = new Paint(); 446 drawHorizontalLine(canvas, baseline, keyWidth, 0xc0008000, line); 447 drawVerticalLine(canvas, positionX, keyHeight, 0xc0800080, line); 448 } 449 } 450 451 // Draw hint label. 452 final String hintLabel = key.getHintLabel(); 453 if (hintLabel != null) { 454 paint.setTextSize(key.selectHintTextSize(params)); 455 paint.setColor(key.selectHintTextColor(params)); 456 // TODO: Should add a way to specify type face for hint letters 457 paint.setTypeface(Typeface.DEFAULT_BOLD); 458 blendAlpha(paint, params.mAnimAlpha); 459 final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); 460 final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); 461 final KeyVisualAttributes visualAttr = key.getVisualAttributes(); 462 final float adjustmentY = (visualAttr == null) ? 0.0f 463 : visualAttr.mHintLabelVerticalAdjustment * labelCharHeight; 464 final float hintX, hintY; 465 if (key.hasHintLabel()) { 466 // The hint label is placed just right of the key label. Used mainly on 467 // "phone number" layout. 468 // TODO: Generalize the following calculations. 469 hintX = positionX + labelCharWidth * 2.0f; 470 hintY = centerY + labelCharHeight / 2.0f; 471 paint.setTextAlign(Align.LEFT); 472 } else if (key.hasShiftedLetterHint()) { 473 // The hint label is placed at top-right corner of the key. Used mainly on tablet. 474 hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f; 475 paint.getFontMetrics(mFontMetrics); 476 hintY = -mFontMetrics.top; 477 paint.setTextAlign(Align.CENTER); 478 } else { // key.hasHintLetter() 479 // The hint letter is placed at top-right corner of the key. Used mainly on phone. 480 final float hintDigitWidth = TypefaceUtils.getReferenceDigitWidth(paint); 481 final float hintLabelWidth = TypefaceUtils.getStringWidth(hintLabel, paint); 482 hintX = keyWidth - mKeyHintLetterPadding 483 - Math.max(hintDigitWidth, hintLabelWidth) / 2.0f; 484 hintY = -paint.ascent(); 485 paint.setTextAlign(Align.CENTER); 486 } 487 canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY + adjustmentY, paint); 488 489 if (LatinImeLogger.sVISUALDEBUG) { 490 final Paint line = new Paint(); 491 drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); 492 drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); 493 } 494 } 495 496 // Draw key icon. 497 if (label == null && icon != null) { 498 final int iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth); 499 final int iconHeight = icon.getIntrinsicHeight(); 500 final int iconX, alignX; 501 final int iconY = (keyHeight - iconHeight) / 2; 502 if (key.isAlignLeft()) { 503 iconX = mKeyLabelHorizontalPadding; 504 alignX = iconX; 505 } else if (key.isAlignRight()) { 506 iconX = keyWidth - mKeyLabelHorizontalPadding - iconWidth; 507 alignX = iconX + iconWidth; 508 } else { // Align center 509 iconX = (keyWidth - iconWidth) / 2; 510 alignX = iconX + iconWidth / 2; 511 } 512 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 513 514 if (LatinImeLogger.sVISUALDEBUG) { 515 final Paint line = new Paint(); 516 drawVerticalLine(canvas, alignX, keyHeight, 0xc0800080, line); 517 drawRectangle(canvas, iconX, iconY, iconWidth, iconHeight, 0x80c00000, line); 518 } 519 } 520 521 if (key.hasPopupHint() && key.getMoreKeys() != null) { 522 drawKeyPopupHint(key, canvas, paint, params); 523 } 524 } 525 526 // Draw popup hint "..." at the bottom right corner of the key. 527 protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint, 528 final KeyDrawParams params) { 529 final int keyWidth = key.getDrawWidth(); 530 final int keyHeight = key.getHeight(); 531 532 paint.setTypeface(params.mTypeface); 533 paint.setTextSize(params.mHintLetterSize); 534 paint.setColor(params.mHintLabelColor); 535 paint.setTextAlign(Align.CENTER); 536 final float hintX = keyWidth - mKeyHintLetterPadding 537 - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f; 538 final float hintY = keyHeight - mKeyPopupHintLetterPadding; 539 canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); 540 541 if (LatinImeLogger.sVISUALDEBUG) { 542 final Paint line = new Paint(); 543 drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); 544 drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); 545 } 546 } 547 548 protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x, 549 final int y, final int width, final int height) { 550 canvas.translate(x, y); 551 icon.setBounds(0, 0, width, height); 552 icon.draw(canvas); 553 canvas.translate(-x, -y); 554 } 555 556 private static void drawHorizontalLine(final Canvas canvas, final float y, final float w, 557 final int color, final Paint paint) { 558 paint.setStyle(Paint.Style.STROKE); 559 paint.setStrokeWidth(1.0f); 560 paint.setColor(color); 561 canvas.drawLine(0.0f, y, w, y, paint); 562 } 563 564 private static void drawVerticalLine(final Canvas canvas, final float x, final float h, 565 final int color, final Paint paint) { 566 paint.setStyle(Paint.Style.STROKE); 567 paint.setStrokeWidth(1.0f); 568 paint.setColor(color); 569 canvas.drawLine(x, 0.0f, x, h, paint); 570 } 571 572 private static void drawRectangle(final Canvas canvas, final float x, final float y, 573 final float w, final float h, final int color, final Paint paint) { 574 paint.setStyle(Paint.Style.STROKE); 575 paint.setStrokeWidth(1.0f); 576 paint.setColor(color); 577 canvas.translate(x, y); 578 canvas.drawRect(0.0f, 0.0f, w, h, paint); 579 canvas.translate(-x, -y); 580 } 581 582 public Paint newLabelPaint(final Key key) { 583 final Paint paint = new Paint(); 584 paint.setAntiAlias(true); 585 if (key == null) { 586 paint.setTypeface(mKeyDrawParams.mTypeface); 587 paint.setTextSize(mKeyDrawParams.mLabelSize); 588 } else { 589 paint.setColor(key.selectTextColor(mKeyDrawParams)); 590 paint.setTypeface(key.selectTypeface(mKeyDrawParams)); 591 paint.setTextSize(key.selectTextSize(mKeyDrawParams)); 592 } 593 return paint; 594 } 595 596 /** 597 * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient 598 * because the keyboard renders the keys to an off-screen buffer and an invalidate() only 599 * draws the cached buffer. 600 * @see #invalidateKey(Key) 601 */ 602 public void invalidateAllKeys() { 603 mInvalidatedKeys.clear(); 604 mInvalidateAllKeys = true; 605 invalidate(); 606 } 607 608 /** 609 * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only 610 * one key is changing it's content. Any changes that affect the position or size of the key 611 * may not be honored. 612 * @param key key in the attached {@link Keyboard}. 613 * @see #invalidateAllKeys 614 */ 615 public void invalidateKey(final Key key) { 616 if (mInvalidateAllKeys) return; 617 if (key == null) return; 618 mInvalidatedKeys.add(key); 619 final int x = key.getX() + getPaddingLeft(); 620 final int y = key.getY() + getPaddingTop(); 621 invalidate(x, y, x + key.getWidth(), y + key.getHeight()); 622 } 623 624 @Override 625 protected void onDetachedFromWindow() { 626 super.onDetachedFromWindow(); 627 freeOffscreenBuffer(); 628 } 629 630 public void deallocateMemory() { 631 freeOffscreenBuffer(); 632 } 633} 634