From 179ada958b0bb46c6b9c8eb8b220d84dd3db855a Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 29 Sep 2010 14:30:01 +0900 Subject: [PATCH] Refactor CandidateView touch event handling This change also fixes tha the touch slop value is applyed only for initial movement of scrolling suggestion bar. Bug: 3004920 Change-Id: I62afdedc210156e41e8c84c48cade442f9d5a1aa --- java/res/values/dimens.xml | 1 + .../inputmethod/latin/CandidateView.java | 186 +++++++----------- 2 files changed, 77 insertions(+), 110 deletions(-) diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index 502157dcf..f83fc36a6 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -33,4 +33,5 @@ 91.8dip -54dip + 0.3in diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 0f5b43009..9bbd30e24 100755 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -16,17 +16,13 @@ package com.android.inputmethod.latin; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Paint.Align; import android.graphics.Rect; import android.graphics.Typeface; -import android.graphics.Paint.Align; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; @@ -40,75 +36,82 @@ import android.view.ViewGroup.LayoutParams; import android.widget.PopupWindow; import android.widget.TextView; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class CandidateView extends View { private static final int OUT_OF_BOUNDS = -1; - private static final List EMPTY_LIST = new ArrayList(); private LatinIME mService; - private List mSuggestions = EMPTY_LIST; + private final ArrayList mSuggestions = new ArrayList(); private boolean mShowingCompletions; private CharSequence mSelectedString; private int mSelectedIndex; private int mTouchX = OUT_OF_BOUNDS; - private Drawable mSelectionHighlight; + private final Drawable mSelectionHighlight; private boolean mTypedWordValid; private boolean mHaveMinimalSuggestion; private Rect mBgPadding; - private TextView mPreviewText; - private PopupWindow mPreviewPopup; + private final TextView mPreviewText; + private final PopupWindow mPreviewPopup; + private final int mDelayAfterPreview; private int mCurrentWordIndex; private Drawable mDivider; private static final int MAX_SUGGESTIONS = 32; private static final int SCROLL_PIXELS = 20; - private static final int MSG_REMOVE_PREVIEW = 1; - private static final int MSG_REMOVE_THROUGH_PREVIEW = 2; - - private int[] mWordWidth = new int[MAX_SUGGESTIONS]; - private int[] mWordX = new int[MAX_SUGGESTIONS]; + private final int[] mWordWidth = new int[MAX_SUGGESTIONS]; + private final int[] mWordX = new int[MAX_SUGGESTIONS]; private int mPopupPreviewX; private int mPopupPreviewY; private static final int X_GAP = 10; - private int mColorNormal; - private int mColorRecommended; - private int mColorOther; - private Paint mPaint; - private int mDescent; + private final int mColorNormal; + private final int mColorRecommended; + private final int mColorOther; + private final Paint mPaint; + private final int mDescent; private boolean mScrolled; private boolean mShowingAddToDictionary; private CharSequence mAddToDictionaryHint; private int mTargetScrollX; - private int mMinTouchableWidth; + private final int mMinTouchableWidth; private int mTotalWidth; - private GestureDetector mGestureDetector; + private final GestureDetector mGestureDetector; + + private final UIHandler mHandler = new UIHandler(); + + private class UIHandler extends Handler { + private static final int MSG_DISMISS_PREVIEW = 1; - Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_REMOVE_PREVIEW: - mPreviewText.setVisibility(GONE); - break; - case MSG_REMOVE_THROUGH_PREVIEW: - mPreviewText.setVisibility(GONE); - if (mTouchX != OUT_OF_BOUNDS) { - removeHighlight(); - } + case MSG_DISMISS_PREVIEW: + mPreviewPopup.dismiss(); break; } } - }; + + public void dismissPreview(long delay) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay); + } + + public void cancelDismissPreview() { + removeMessages(MSG_DISMISS_PREVIEW); + } + } /** * Construct a CandidateView for showing suggested words for completion. @@ -129,6 +132,8 @@ public class CandidateView extends View { mPreviewPopup.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); mPreviewPopup.setContentView(mPreviewText); mPreviewPopup.setBackgroundDrawable(null); + mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation); + mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview); mColorNormal = res.getColor(R.color.candidate_normal); mColorRecommended = res.getColor(R.color.candidate_recommended); mColorOther = res.getColor(R.color.candidate_other); @@ -142,13 +147,10 @@ public class CandidateView extends View { mPaint.setStrokeWidth(0); mPaint.setTextAlign(Align.CENTER); mDescent = (int) mPaint.descent(); - // 50 pixels for a 160dpi device would mean about 0.3 inch - mMinTouchableWidth = (int) (getResources().getDisplayMetrics().density * 50); + mMinTouchableWidth = (int)res.getDimension(R.dimen.candidate_min_touchable_width); // Slightly reluctant to scroll to be able to easily choose the suggestion - // 50 pixels for a 160dpi device would mean about 0.3 inch - final int touchSlop = (int) (getResources().getDisplayMetrics().density * 50); - final int touchSlopSquare = touchSlop * touchSlop; + final int touchSlopSquare = mMinTouchableWidth * mMinTouchableWidth; mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent me) { @@ -158,15 +160,25 @@ public class CandidateView extends View { } } } - + + @Override + public boolean onDown(MotionEvent e) { + mScrolled = false; + return false; + } + @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - final int deltaX = (int) (e2.getX() - e1.getX()); - final int deltaY = (int) (e2.getY() - e1.getY()); - final int distance = (deltaX * deltaX) + (deltaY * deltaY); - if (distance < touchSlopSquare) { - return false; + if (!mScrolled) { + // This is applied only when we recognize that scrolling is starting. + final int deltaX = (int) (e2.getX() - e1.getX()); + final int deltaY = (int) (e2.getY() - e1.getY()); + final int distance = (deltaX * deltaX) + (deltaY * deltaY); + if (distance < touchSlopSquare) { + return true; + } + mScrolled = true; } final int width = getWidth(); @@ -215,7 +227,6 @@ public class CandidateView extends View { super.onDraw(canvas); } mTotalWidth = 0; - if (mSuggestions == null) return; final int height = getHeight(); if (mBgPadding == null) { @@ -226,8 +237,8 @@ public class CandidateView extends View { mDivider.setBounds(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } - int x = 0; - final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS); + + final int count = mSuggestions.size(); final Rect bgPadding = mBgPadding; final Paint paint = mPaint; final int touchX = mTouchX; @@ -238,25 +249,26 @@ public class CandidateView extends View { boolean existsAutoCompletion = false; + int x = 0; for (int i = 0; i < count; i++) { CharSequence suggestion = mSuggestions.get(i); if (suggestion == null) continue; + final int wordLength = suggestion.length(); + paint.setColor(mColorNormal); if (mHaveMinimalSuggestion && ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid))) { paint.setTypeface(Typeface.DEFAULT_BOLD); paint.setColor(mColorRecommended); existsAutoCompletion = true; - } else if (i != 0 || (suggestion.length() == 1 && count > 1)) { + } else if (i != 0 || (wordLength == 1 && count > 1)) { // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1 and // there are multiple suggestions, such as the default punctuation list. paint.setColor(mColorOther); } - final int wordWidth; - if (mWordWidth[i] != 0) { - wordWidth = mWordWidth[i]; - } else { - float textWidth = paint.measureText(suggestion, 0, suggestion.length()); + int wordWidth; + if ((wordWidth = mWordWidth[i]) == 0) { + float textWidth = paint.measureText(suggestion, 0, wordLength); wordWidth = Math.max(mMinTouchableWidth, (int) textWidth + X_GAP * 2); mWordWidth[i] = wordWidth; } @@ -277,7 +289,7 @@ public class CandidateView extends View { } if (canvas != null) { - canvas.drawText(suggestion, 0, suggestion.length(), x + wordWidth / 2, y, paint); + canvas.drawText(suggestion, 0, wordLength, x + wordWidth / 2, y, paint); paint.setColor(mColorOther); canvas.translate(x + wordWidth, 0); // Draw a divider unless it's after the hint @@ -324,7 +336,12 @@ public class CandidateView extends View { boolean typedWordValid, boolean haveMinimalSuggestion) { clear(); if (suggestions != null) { - mSuggestions = new ArrayList(suggestions); + int insertCount = Math.min(suggestions.size(), MAX_SUGGESTIONS); + for (CharSequence suggestion : suggestions) { + mSuggestions.add(suggestion); + if (--insertCount == 0) + break; + } } mShowingCompletions = completions; mTypedWordValid = typedWordValid; @@ -355,50 +372,6 @@ public class CandidateView extends View { return true; } - public void scrollPrev() { - int i = 0; - final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS); - int firstItem = 0; // Actually just before the first item, if at the boundary - while (i < count) { - if (mWordX[i] < getScrollX() - && mWordX[i] + mWordWidth[i] >= getScrollX() - 1) { - firstItem = i; - break; - } - i++; - } - int leftEdge = mWordX[firstItem] + mWordWidth[firstItem] - getWidth(); - if (leftEdge < 0) leftEdge = 0; - updateScrollPosition(leftEdge); - } - - public void scrollNext() { - int i = 0; - int scrollX = getScrollX(); - int targetX = scrollX; - final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS); - int rightEdge = scrollX + getWidth(); - while (i < count) { - if (mWordX[i] <= rightEdge && - mWordX[i] + mWordWidth[i] >= rightEdge) { - targetX = Math.min(mWordX[i], mTotalWidth - getWidth()); - break; - } - i++; - } - updateScrollPosition(targetX); - } - - private void updateScrollPosition(int targetX) { - if (targetX != getScrollX()) { - // TODO: Animate - mTargetScrollX = targetX; - requestLayout(); - invalidate(); - mScrolled = true; - } - } - /* package */ List getSuggestions() { return mSuggestions; } @@ -406,7 +379,7 @@ public class CandidateView extends View { public void clear() { // Don't call mSuggestions.clear() because it's being used for logging // in LatinIME.pickSuggestionManually(). - mSuggestions = EMPTY_LIST; + mSuggestions.clear(); mTouchX = OUT_OF_BOUNDS; mSelectedString = null; mSelectedIndex = -1; @@ -414,9 +387,7 @@ public class CandidateView extends View { invalidate(); Arrays.fill(mWordWidth, 0); Arrays.fill(mWordX, 0); - if (mPreviewPopup.isShowing()) { - mPreviewPopup.dismiss(); - } + mPreviewPopup.dismiss(); } @Override @@ -433,7 +404,6 @@ public class CandidateView extends View { switch (action) { case MotionEvent.ACTION_DOWN: - mScrolled = false; invalidate(); break; case MotionEvent.ACTION_MOVE: @@ -453,7 +423,6 @@ public class CandidateView extends View { mSelectedIndex = -1; } } - invalidate(); break; case MotionEvent.ACTION_UP: if (!mScrolled) { @@ -473,8 +442,8 @@ public class CandidateView extends View { mSelectedString = null; mSelectedIndex = -1; removeHighlight(); - hidePreview(); requestLayout(); + mHandler.dismissPreview(mDelayAfterPreview); break; } return true; @@ -482,10 +451,7 @@ public class CandidateView extends View { private void hidePreview() { mCurrentWordIndex = OUT_OF_BOUNDS; - if (mPreviewPopup.isShowing()) { - mHandler.sendMessageDelayed(mHandler - .obtainMessage(MSG_REMOVE_PREVIEW), 60); - } + mPreviewPopup.dismiss(); } private void showPreview(int wordIndex, String altText) { @@ -496,6 +462,7 @@ public class CandidateView extends View { if (wordIndex == OUT_OF_BOUNDS) { hidePreview(); } else { + mHandler.cancelDismissPreview(); CharSequence word = altText != null? altText : mSuggestions.get(wordIndex); mPreviewText.setText(word); mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), @@ -508,7 +475,6 @@ public class CandidateView extends View { mPopupPreviewX = mWordX[wordIndex] - mPreviewText.getPaddingLeft() - getScrollX() + (mWordWidth[wordIndex] - wordWidth) / 2; mPopupPreviewY = - popupHeight; - mHandler.removeMessages(MSG_REMOVE_PREVIEW); int [] offsetInWindow = new int[2]; getLocationInWindow(offsetInWindow); if (mPreviewPopup.isShowing()) {