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