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 KeyboardView(final Context context, final AttributeSet attrs)117 public KeyboardView(final Context context, final AttributeSet attrs) { 118 this(context, attrs, R.attr.keyboardViewStyle); 119 } 120 KeyboardView(final Context context, final AttributeSet attrs, final int defStyle)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 blendAlpha(final Paint paint, final int alpha)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 */ setKeyboard(final Keyboard keyboard)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 */ getKeyboard()178 public Keyboard getKeyboard() { 179 return mKeyboard; 180 } 181 getVerticalCorrection()182 protected float getVerticalCorrection() { 183 return mVerticalCorrection; 184 } 185 updateKeyDrawParams(final int keyHeight)186 protected void updateKeyDrawParams(final int keyHeight) { 187 mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes); 188 } 189 190 @Override onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)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 onDraw(final Canvas canvas)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 maybeAllocateOffscreenBuffer()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 freeOffscreenBuffer()236 private void freeOffscreenBuffer() { 237 if (mOffscreenBuffer != null) { 238 mOffscreenBuffer.recycle(); 239 mOffscreenBuffer = null; 240 } 241 } 242 onDrawKeyboard(final Canvas canvas)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 onDrawKey(final Key key, final Canvas canvas, final Paint paint)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. onDrawKeyBackground(final Key key, final Canvas canvas)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. onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, final KeyDrawParams params)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. drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint, final KeyDrawParams params)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 drawIcon(final Canvas canvas, final Drawable icon, final int x, final int y, final int width, final int height)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 drawHorizontalLine(final Canvas canvas, final float y, final float w, final int color, final Paint paint)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 drawVerticalLine(final Canvas canvas, final float x, final float h, final int color, final Paint paint)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 drawRectangle(final Canvas canvas, final float x, final float y, final float w, final float h, final int color, final Paint paint)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 newLabelPaint(final Key key)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 */ invalidateAllKeys()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 */ invalidateKey(final Key key)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 onDetachedFromWindow()603 protected void onDetachedFromWindow() { 604 super.onDetachedFromWindow(); 605 freeOffscreenBuffer(); 606 } 607 } 608