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