Add scrollable KeyboardView

Bug: 6370846
Change-Id: I8b9a619e0e6a980c8b17788ad03c62effc7f35b5
main
Tadashi G. Takaoka 2013-08-26 13:24:48 +09:00
parent 78ab1bb661
commit 2da94ad8cd
2 changed files with 277 additions and 0 deletions

View File

@ -0,0 +1,211 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ScrollView;
import android.widget.Scroller;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.latin.R;
/**
* This is an extended {@link KeyboardView} class that hosts a scroll keyboard.
* Multi-touch unsupported. No {@link PointerTracker}s. No gesture support.
*/
public final class ScrollKeyboardView extends KeyboardView implements
ScrollViewWithNotifier.ScrollListener, GestureDetector.OnGestureListener {
private static final boolean PAGINATION = false;
public interface OnKeyClickListener {
public void onKeyClick(Key key);
}
private static final OnKeyClickListener EMPTY_LISTENER = new OnKeyClickListener() {
@Override
public void onKeyClick(final Key key) {}
};
private OnKeyClickListener mListener = EMPTY_LISTENER;
private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */);
private final GestureDetector mGestureDetector;
private final Scroller mScroller;
private ScrollViewWithNotifier mScrollView;
public ScrollKeyboardView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.keyboardViewStyle);
}
public ScrollKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
mGestureDetector = new GestureDetector(context, this);
mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
mScroller = new Scroller(context);
}
public void setScrollView(final ScrollViewWithNotifier scrollView) {
mScrollView = scrollView;
scrollView.setScrollListener(this);
}
private final Runnable mScrollTask = new Runnable() {
@Override
public void run() {
final Scroller scroller = mScroller;
final ScrollView scrollView = mScrollView;
scroller.computeScrollOffset();
scrollView.scrollTo(0, scroller.getCurrY());
if (!scroller.isFinished()) {
scrollView.post(this);
}
}
};
// {@link ScrollViewWithNotified#ScrollListener} methods.
@Override
public void notifyScrollChanged(final int scrollX, final int scrollY, final int oldX,
final int oldY) {
if (PAGINATION) {
mScroller.forceFinished(true /* finished */);
mScrollView.removeCallbacks(mScrollTask);
final int currentTop = mScrollView.getScrollY();
final int pageHeight = getKeyboard().mBaseHeight;
final int lastPageNo = currentTop / pageHeight;
final int lastPageTop = lastPageNo * pageHeight;
final int nextPageNo = lastPageNo + 1;
final int nextPageTop = Math.min(nextPageNo * pageHeight, getHeight() - pageHeight);
final int scrollTo = (currentTop - lastPageTop) < (nextPageTop - currentTop)
? lastPageTop : nextPageTop;
final int deltaY = scrollTo - currentTop;
mScroller.startScroll(0, currentTop, 0, deltaY, 300);
mScrollView.post(mScrollTask);
}
}
@Override
public void notifyOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
final boolean clampedY) {
releaseCurrentKey();
}
public void setOnKeyClickListener(final OnKeyClickListener listener) {
mListener = listener;
}
/**
* {@inheritDoc}
*/
@Override
public void setKeyboard(final Keyboard keyboard) {
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
}
/**
* {@inheritDoc}
*/
@Override
public boolean onTouchEvent(final MotionEvent e) {
if (mGestureDetector.onTouchEvent(e)) {
return true;
}
final Key key = getKey(e);
if (key != null && key != mCurrentKey) {
releaseCurrentKey();
}
return true;
}
// {@link GestureDetector#OnGestureListener} methods.
private Key mCurrentKey;
private Key getKey(final MotionEvent e) {
final int index = e.getActionIndex();
final int x = (int)e.getX(index);
final int y = (int)e.getY(index);
return mKeyDetector.detectHitKey(x, y);
}
public void releaseCurrentKey() {
final Key currentKey = mCurrentKey;
if (currentKey == null) {
return;
}
currentKey.onReleased();
invalidateKey(currentKey);
mCurrentKey = null;
}
@Override
public boolean onDown(final MotionEvent e) {
final Key key = getKey(e);
releaseCurrentKey();
mCurrentKey = key;
if (key == null) {
return false;
}
// TODO: May call {@link KeyboardActionListener#onPressKey(int,int,boolean)}.
key.onPressed();
invalidateKey(key);
return false;
}
@Override
public void onShowPress(final MotionEvent e) {
// User feedback is done at {@link #onDown(MotionEvent)}.
}
@Override
public boolean onSingleTapUp(final MotionEvent e) {
final Key key = getKey(e);
releaseCurrentKey();
if (key == null) {
return false;
}
// TODO: May call {@link KeyboardActionListener#onReleaseKey(int,boolean)}.
key.onReleased();
invalidateKey(key);
mListener.onKeyClick(key);
return true;
}
@Override
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
final float distanceY) {
releaseCurrentKey();
return false;
}
@Override
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX,
final float velocityY) {
releaseCurrentKey();
return false;
}
@Override
public void onLongPress(final MotionEvent e) {
// Long press detection of {@link #mGestureDetector} is disabled and not used.
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;
/**
* This is an extended {@link ScrollView} that can notify
* {@link ScrollView#onScrollChanged(int,int,int,int} and
* {@link ScrollView#onOverScrolled(int,int,int,int)} to a content view.
*/
public class ScrollViewWithNotifier extends ScrollView {
private ScrollListener mScrollListener = EMPTY_LISTER;
public interface ScrollListener {
public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY);
public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY);
}
private static final ScrollListener EMPTY_LISTER = new ScrollListener() {
@Override
public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY) {}
@Override
public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY) {}
};
public ScrollViewWithNotifier(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onScrollChanged(final int scrollX, final int scrollY, final int oldX,
final int oldY) {
super.onScrollChanged(scrollX, scrollY, oldX, oldY);
mScrollListener.notifyScrollChanged(scrollX, scrollY, oldX, oldY);
}
@Override
protected void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
final boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
mScrollListener.notifyOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
public void setScrollListener(final ScrollListener listener) {
mScrollListener = listener;
}
}