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