PageRenderTime 60ms CodeModel.GetById 10ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 1ms

/CircleIME/src/com/marvin/circleime/CandidateView.java

http://eyes-free.googlecode.com/
Java | 333 lines | 239 code | 52 blank | 42 comment | 42 complexity | 00aac28f3dacaceb7ca823f69898fc92 MD5 | raw file
  1/*
  2 * Copyright (C) 2008-2009 Google Inc.
  3 * 
  4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5 * use this file except in compliance with the License. You may obtain a copy of
  6 * 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, WITHOUT
 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 13 * License for the specific language governing permissions and limitations under
 14 * the License.
 15 */
 16
 17package com.marvin.circleime;
 18
 19import android.content.Context;
 20import android.content.res.Resources;
 21import android.graphics.Canvas;
 22import android.graphics.Paint;
 23import android.graphics.Rect;
 24import android.graphics.drawable.Drawable;
 25import android.view.GestureDetector;
 26import android.view.MotionEvent;
 27import android.view.View;
 28
 29import java.util.ArrayList;
 30import java.util.List;
 31
 32public class CandidateView extends View {
 33
 34    private static final int OUT_OF_BOUNDS = -1;
 35
 36    private SoftKeyboard mService;
 37
 38    private List<String> mSuggestions;
 39
 40    private int mSelectedIndex;
 41
 42    private int mTouchX = OUT_OF_BOUNDS;
 43
 44    private Drawable mSelectionHighlight;
 45
 46    private boolean mTypedWordValid;
 47
 48    private Rect mBgPadding;
 49
 50    private static final int MAX_SUGGESTIONS = 32;
 51
 52    private static final int SCROLL_PIXELS = 20;
 53
 54    private int[] mWordWidth = new int[MAX_SUGGESTIONS];
 55
 56    private int[] mWordX = new int[MAX_SUGGESTIONS];
 57
 58    private static final int X_GAP = 10;
 59
 60    private static final List<String> EMPTY_LIST = new ArrayList<String>();
 61
 62    private int mColorNormal;
 63
 64    private int mColorRecommended;
 65
 66    private int mColorOther;
 67
 68    private int mVerticalPadding;
 69
 70    private Paint mPaint;
 71
 72    private boolean mScrolled;
 73
 74    private int mTargetScrollX;
 75
 76    private int mTotalWidth;
 77
 78    private GestureDetector mGestureDetector;
 79
 80    /**
 81     * Construct a CandidateView for showing suggested words for completion.
 82     * 
 83     * @param context
 84     * @param attrs
 85     */
 86    public CandidateView(Context context) {
 87        super(context);
 88        mSelectionHighlight = context.getResources().getDrawable(
 89                android.R.drawable.list_selector_background);
 90        mSelectionHighlight.setState(new int[] {
 91                android.R.attr.state_enabled, android.R.attr.state_focused,
 92                android.R.attr.state_window_focused, android.R.attr.state_pressed
 93        });
 94
 95        Resources r = context.getResources();
 96
 97        setBackgroundColor(r.getColor(R.color.candidate_background));
 98
 99        mColorNormal = r.getColor(R.color.candidate_normal);
100        mColorRecommended = r.getColor(R.color.candidate_recommended);
101        mColorOther = r.getColor(R.color.candidate_other);
102        mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding);
103
104        mPaint = new Paint();
105        mPaint.setColor(mColorNormal);
106        mPaint.setAntiAlias(true);
107        mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height));
108        mPaint.setStrokeWidth(0);
109
110        mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
111            @Override
112            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
113                mScrolled = true;
114                int sx = getScrollX();
115                sx += distanceX;
116                if (sx < 0) {
117                    sx = 0;
118                }
119                if (sx + getWidth() > mTotalWidth) {
120                    sx -= distanceX;
121                }
122                mTargetScrollX = sx;
123                scrollTo(sx, getScrollY());
124                invalidate();
125                return true;
126            }
127        });
128        setHorizontalFadingEdgeEnabled(true);
129        setWillNotDraw(false);
130        setHorizontalScrollBarEnabled(false);
131        setVerticalScrollBarEnabled(false);
132    }
133
134    /**
135     * A connection back to the service to communicate with the text field
136     * 
137     * @param listener
138     */
139    public void setService(SoftKeyboard listener) {
140        mService = listener;
141    }
142
143    @Override
144    public int computeHorizontalScrollRange() {
145        return mTotalWidth;
146    }
147
148    @Override
149    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
150        int measuredWidth = resolveSize(50, widthMeasureSpec);
151
152        // Get the desired height of the icon menu view (last row of items does
153        // not have a divider below)
154        Rect padding = new Rect();
155        mSelectionHighlight.getPadding(padding);
156        final int desiredHeight = ((int) mPaint.getTextSize()) + mVerticalPadding + padding.top
157                + padding.bottom;
158
159        // Maximum possible width and desired height
160        setMeasuredDimension(measuredWidth, resolveSize(desiredHeight, heightMeasureSpec));
161    }
162
163    /**
164     * If the canvas is null, then only touch calculations are performed to pick
165     * the target candidate.
166     */
167    @Override
168    protected void onDraw(Canvas canvas) {
169        if (canvas != null) {
170            super.onDraw(canvas);
171        }
172        mTotalWidth = 0;
173        if (mSuggestions == null)
174            return;
175
176        if (mBgPadding == null) {
177            mBgPadding = new Rect(0, 0, 0, 0);
178            if (getBackground() != null) {
179                getBackground().getPadding(mBgPadding);
180            }
181        }
182        int x = 0;
183        final int count = mSuggestions.size();
184        final int height = getHeight();
185        final Rect bgPadding = mBgPadding;
186        final Paint paint = mPaint;
187        final int touchX = mTouchX;
188        final int scrollX = getScrollX();
189        final boolean scrolled = mScrolled;
190        final boolean typedWordValid = mTypedWordValid;
191        final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent());
192
193        for (int i = 0; i < count; i++) {
194            String suggestion = mSuggestions.get(i);
195            float textWidth = paint.measureText(suggestion);
196            final int wordWidth = (int) textWidth + X_GAP * 2;
197
198            mWordX[i] = x;
199            mWordWidth[i] = wordWidth;
200            paint.setColor(mColorNormal);
201            if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled) {
202                if (canvas != null) {
203                    canvas.translate(x, 0);
204                    mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height);
205                    mSelectionHighlight.draw(canvas);
206                    canvas.translate(-x, 0);
207                }
208                mSelectedIndex = i;
209            }
210
211            if (canvas != null) {
212                if ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid)) {
213                    paint.setFakeBoldText(true);
214                    paint.setColor(mColorRecommended);
215                } else if (i != 0) {
216                    paint.setColor(mColorOther);
217                }
218                canvas.drawText(suggestion, x + X_GAP, y, paint);
219                paint.setColor(mColorOther);
220                canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top, x + wordWidth + 0.5f,
221                        height + 1, paint);
222                paint.setFakeBoldText(false);
223            }
224            x += wordWidth;
225        }
226        mTotalWidth = x;
227        if (mTargetScrollX != getScrollX()) {
228            scrollToTarget();
229        }
230    }
231
232    private void scrollToTarget() {
233        int sx = getScrollX();
234        if (mTargetScrollX > sx) {
235            sx += SCROLL_PIXELS;
236            if (sx >= mTargetScrollX) {
237                sx = mTargetScrollX;
238                requestLayout();
239            }
240        } else {
241            sx -= SCROLL_PIXELS;
242            if (sx <= mTargetScrollX) {
243                sx = mTargetScrollX;
244                requestLayout();
245            }
246        }
247        scrollTo(sx, getScrollY());
248        invalidate();
249    }
250
251    public void setSuggestions(List<String> suggestions, boolean completions, boolean typedWordValid) {
252        clear();
253        if (suggestions != null) {
254            mSuggestions = new ArrayList<String>(suggestions);
255        }
256        mTypedWordValid = typedWordValid;
257        scrollTo(0, 0);
258        mTargetScrollX = 0;
259        // Compute the total width
260        onDraw(null);
261        invalidate();
262        requestLayout();
263    }
264
265    public void clear() {
266        mSuggestions = EMPTY_LIST;
267        mTouchX = OUT_OF_BOUNDS;
268        mSelectedIndex = -1;
269        invalidate();
270    }
271
272    @Override
273    public boolean onTouchEvent(MotionEvent me) {
274
275        if (mGestureDetector.onTouchEvent(me)) {
276            return true;
277        }
278
279        int action = me.getAction();
280        int x = (int) me.getX();
281        int y = (int) me.getY();
282        mTouchX = x;
283
284        switch (action) {
285            case MotionEvent.ACTION_DOWN:
286                mScrolled = false;
287                invalidate();
288                break;
289            case MotionEvent.ACTION_MOVE:
290                if (y <= 0) {
291                    // Fling up!?
292                    if (mSelectedIndex >= 0) {
293                        mService.pickSuggestionManually(mSelectedIndex);
294                        mSelectedIndex = -1;
295                    }
296                }
297                invalidate();
298                break;
299            case MotionEvent.ACTION_UP:
300                if (!mScrolled) {
301                    if (mSelectedIndex >= 0) {
302                        mService.pickSuggestionManually(mSelectedIndex);
303                    }
304                }
305                mSelectedIndex = -1;
306                removeHighlight();
307                requestLayout();
308                break;
309        }
310        return true;
311    }
312
313    /**
314     * For flick through from keyboard, call this method with the x coordinate
315     * of the flick gesture.
316     * 
317     * @param x
318     */
319    public void takeSuggestionAt(float x) {
320        mTouchX = (int) x;
321        // To detect candidate
322        onDraw(null);
323        if (mSelectedIndex >= 0) {
324            mService.pickSuggestionManually(mSelectedIndex);
325        }
326        invalidate();
327    }
328
329    private void removeHighlight() {
330        mTouchX = OUT_OF_BOUNDS;
331        invalidate();
332    }
333}