1
0
Fork 0

* Candidates list no longer covers the application in use

* Changed the candidates list styles to match the application look and feel
This commit is contained in:
Dimo Karaivanov 2022-10-28 15:10:11 +03:00
parent 528ebb123b
commit 0126e3db64
12 changed files with 313 additions and 326 deletions

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<size android:width="1dip" />
<solid android:color="@color/candidate_separator" />
</shape>

View file

@ -1,13 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/main_suggestions_list"
android:layout_width="fill_parent"
android:layout_height="@dimen/candidate_list_height"
android:orientation="horizontal"
android:scrollbars="none"
android:background="@color/candidate_background" />
<LinearLayout
android:id="@+id/softkey_view"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:layout_marginRight="0dp"
android:background="@drawable/bggradient"
android:baselineAligned="true"
android:gravity="bottom"
android:orientation="horizontal">
<Button
@ -59,3 +69,4 @@
android:textSize="24sp" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/suggestion_list_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/suggestion_list_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/candidate_padding_horizontal"
android:paddingVertical="@dimen/candidate_padding_vertical"
android:text=""
android:textColor="@color/candidate_color"
android:textSize="@dimen/candidate_font_size" />
</LinearLayout>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="candidate_normal">#FF000000</color>
<color name="candidate_recommended">#ffc66ac3</color>
<color name="candidate_other">#ff68f0e9</color>
<color name="candidate_background">#e19185df</color>
<color name="button_text">#FFFFFFFF</color>
<color name="button_text">#EFEBE9</color>
<color name="candidate_background">#333333</color>
<color name="candidate_color">#CCCCCC</color>
<color name="candidate_selected">#555555</color>
<color name="candidate_separator">#888888</color>
</resources>

View file

@ -1,26 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, 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.
*/
-->
<resources>
<dimen name="candidate_list_height">25sp</dimen>
<dimen name="candidate_font_size">17sp</dimen>
<dimen name="candidate_padding_horizontal">6sp</dimen>
<dimen name="candidate_padding_vertical">1sp</dimen>
<dimen name="candidate_font_height">16sp</dimen>
<dimen name="candidate_vertical_padding">6sp</dimen>
<dimen name="soft_key_height">36dp</dimen>
<dimen name="soft_key_icon_size">24sp</dimen>
</resources>

View file

@ -8,13 +8,11 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import io.github.sspanak.tt9.preferences.T9Preferences;
import io.github.sspanak.tt9.ui.CandidateView;
abstract class KeyPadHandler extends InputMethodService {
protected InputConnection currentInputConnection = null;
protected CandidateView mSuggestionView;
protected T9Preferences prefs;
// editing mode
@ -77,19 +75,6 @@ abstract class KeyPadHandler extends InputMethodService {
}
/**
* Called by the framework when your view for showing candidates needs to be
* generated, like {@link #onCreateInputView}.
*/
@Override
public View onCreateCandidatesView() {
if (mSuggestionView == null) {
mSuggestionView = new CandidateView(this);
}
return mSuggestionView;
}
/**
* This is the main point where we do our initialization of the input method
* to begin operating on an application. At this point we have been bound to

View file

@ -8,7 +8,7 @@ import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.ui.UI;
class SoftKeyHandler implements View.OnTouchListener {
private static final int[] buttons = { R.id.main_left, R.id.main_right, R.id.main_mid };
private static final int[] buttons = { R.id.main_left, R.id.main_mid, R.id.main_right };
private final TraditionalT9 tt9;
private View view = null;
@ -31,6 +31,10 @@ class SoftKeyHandler implements View.OnTouchListener {
return view;
}
View getView() {
return view;
}
void show() {
if (view != null) {

View file

@ -16,6 +16,7 @@ import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.ui.SuggestionsView;
import io.github.sspanak.tt9.ui.UI;
public class TraditionalT9 extends KeyPadHandler {
@ -31,6 +32,7 @@ public class TraditionalT9 extends KeyPadHandler {
// soft key view
private SoftKeyHandler softKeyHandler = null;
private SuggestionsView mSuggestionView = null;
public static Context getMainContext() {
@ -59,6 +61,10 @@ public class TraditionalT9 extends KeyPadHandler {
softKeyHandler = new SoftKeyHandler(getLayoutInflater(), this);
}
if (mSuggestionView == null) {
mSuggestionView = new SuggestionsView(softKeyHandler.getView());
}
loadPreferences();
prefs.clearLastWord();
}

View file

@ -167,6 +167,8 @@ public class T9Preferences {
public int getSuggestionsMax() { return 20; }
public int getSuggestionsMin() { return 8; }
public int getSuggestionSelectAnimationDuration() { return 66; }
public int getSuggestionTranslateAnimationDuration() { return 0; }
/************* add word, last word *************/

View file

@ -1,231 +0,0 @@
package io.github.sspanak.tt9.ui;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import io.github.sspanak.tt9.R;
public class CandidateView extends View {
private List<String> mSuggestions = new ArrayList<>();
protected int mSelectedIndex;
private Drawable mSelectionHighlight;
private Rect mBgPadding;
private static final int MAX_SUGGESTIONS = 32;
private static final int CANDIDATE_SCROLL_STEP = 20; // px
private int[] mWordWidth = new int[MAX_SUGGESTIONS];
private int[] mWordX = new int[MAX_SUGGESTIONS];
private static final int X_GAP = 10;
private static final List<String> EMPTY_LIST = new ArrayList<>();
private int mColorNormal;
private int mColorRecommended;
private int mColorOther;
private int mVerticalPadding;
private Paint mPaint;
private int mTargetScrollX;
private int mTotalWidth;
Rect mPadding;
/**
* Construct a CandidateView for showing suggested words for completion.
*/
public CandidateView(Context context) {
super(context);
mSelectionHighlight = context.getResources().getDrawable(
android.R.drawable.list_selector_background);
mSelectionHighlight.setState(new int[] {
android.R.attr.state_enabled, android.R.attr.state_focused,
android.R.attr.state_window_focused, android.R.attr.state_pressed });
Resources r = context.getResources();
setBackgroundColor(r.getColor(R.color.candidate_background));
mColorNormal = r.getColor(R.color.candidate_normal);
mColorRecommended = r.getColor(R.color.candidate_recommended);
mColorOther = r.getColor(R.color.candidate_other);
mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding);
mPaint = new Paint();
mPaint.setColor(mColorNormal);
mPaint.setAntiAlias(true);
mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height));
mPaint.setStrokeWidth(0);
mPadding = new Rect();
setHorizontalFadingEdgeEnabled(true);
setWillNotDraw(false);
setHorizontalScrollBarEnabled(false);
setVerticalScrollBarEnabled(false);
}
@Override
public int computeHorizontalScrollRange() {
return mTotalWidth;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredWidth = resolveSize(50, widthMeasureSpec);
// Get the desired height of the icon menu view (last row of items does
// not have a divider below)
mSelectionHighlight.getPadding(mPadding);
final int desiredHeight = ((int) mPaint.getTextSize()) + mVerticalPadding + mPadding.top
+ mPadding.bottom;
// Maximum possible width and desired height
setMeasuredDimension(measuredWidth, resolveSize(desiredHeight, heightMeasureSpec));
}
/**
* If the canvas is null, then only touch calculations are performed to pick
* the target candidate.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mTotalWidth = 0;
if (mSuggestions == null)
return;
if (mBgPadding == null) {
mBgPadding = new Rect(0, 0, 0, 0);
if (getBackground() != null) {
getBackground().getPadding(mBgPadding);
}
}
int x = 0;
final int count = mSuggestions.size();
final int height = getHeight();
final Rect bgPadding = mBgPadding;
final Paint paint = mPaint;
final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent());
for (int i = 0; i < count; i++) {
String suggestion = mSuggestions.get(i);
if (suggestion.equals("\n")) {
suggestion = ""; // make it more clear it is a new line
}
float textWidth = paint.measureText(suggestion);
final int wordWidth = (int) textWidth + X_GAP * 2;
mWordX[i] = x;
mWordWidth[i] = wordWidth;
paint.setColor(mColorNormal);
// if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth &&
// !scrolled) {
if (i == mSelectedIndex) {
canvas.translate(x, 0);
mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height);
mSelectionHighlight.draw(canvas);
canvas.translate(-x, 0);
paint.setFakeBoldText(true);
paint.setColor(mColorRecommended);
} else {
paint.setColor(mColorOther);
}
canvas.drawText(suggestion, x + X_GAP, y, paint);
paint.setColor(mColorOther);
canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top, x + wordWidth + 0.5f, height + 1,
paint);
paint.setFakeBoldText(false);
x += wordWidth;
}
mTotalWidth = x;
scrollTargetIntoView();
}
private void scrollTargetIntoView() {
int currentX = getScrollX();
int scrollDistance = mTargetScrollX - currentX;
if (scrollDistance == 0) {
return;
}
int maxStepX = (Math.abs(scrollDistance) > getWidth() * 0.5) ? (int)(CANDIDATE_SCROLL_STEP * 2.25) : CANDIDATE_SCROLL_STEP;
int stepX = Integer.signum(scrollDistance) * Math.min(Math.abs(scrollDistance), maxStepX);
scrollTo(currentX + stepX, getScrollY());
invalidate();
}
public int getCurrentIndex() {
return mSelectedIndex;
}
public String getCurrentSuggestion() {
return getSuggestion(mSelectedIndex);
}
public String getSuggestion(int id) {
return mSuggestions != null && id >= 0 && id < mSuggestions.size() ? mSuggestions.get(id) : "";
}
public void setSuggestions(List<String> suggestions, int initialSel) {
clear();
if (suggestions != null) {
mSuggestions = suggestions;
mSelectedIndex = Math.max(initialSel, 0);
}
scrollTo(0, 0);
mTargetScrollX = 0;
// Compute the total width
// onDraw(null);
invalidate();
requestLayout();
}
protected void clear() {
mSuggestions = EMPTY_LIST;
mSelectedIndex = -1;
invalidate();
}
public void scrollToSuggestion(int increment) {
if (mSuggestions != null && mSuggestions.size() > 1) {
mSelectedIndex = mSelectedIndex + increment;
if (mSelectedIndex == mSuggestions.size()) {
mSelectedIndex = 0;
} else if (mSelectedIndex < 0) {
mSelectedIndex = mSuggestions.size() - 1;
}
int fullsize = getWidth();
int halfsize = fullsize / 2;
mTargetScrollX = mWordX[mSelectedIndex] + (mWordWidth[mSelectedIndex] / 2) - halfsize;
if (mTargetScrollX < 0) {
mTargetScrollX = 0;
} else if (mTargetScrollX > (mTotalWidth - fullsize)) {
mTargetScrollX = mTotalWidth - fullsize;
}
invalidate();
}
}
}

View file

@ -0,0 +1,67 @@
package io.github.sspanak.tt9.ui;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.ViewHolder> {
private final int colorHighlight;
private final int layout;
private final int textViewResourceId;
private final LayoutInflater mInflater;
private final List<String> mSuggestions;
private int selectedIndex = 0;
public SuggestionsAdapter(Context context, int layout, int textViewResourceId, int highLightColor, List<String> suggestions) {
this.colorHighlight = highLightColor;
this.layout = layout;
this.textViewResourceId = textViewResourceId;
this.mInflater = LayoutInflater.from(context);
this.mSuggestions = suggestions;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(mInflater.inflate(layout, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.suggestionItem.setText(mSuggestions.get(position));
holder.suggestionItem.setBackgroundColor(selectedIndex == position ? colorHighlight : Color.TRANSPARENT);
}
@Override
public int getItemCount() {
return mSuggestions.size();
}
public void setSelection(int index) {
selectedIndex = index;
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView suggestionItem;
ViewHolder(View itemView) {
super(itemView);
suggestionItem = itemView.findViewById(textViewResourceId);
}
}
}

View file

@ -0,0 +1,137 @@
package io.github.sspanak.tt9.ui;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.preferences.T9Preferences;
public class SuggestionsView {
private final List<String> suggestions = new ArrayList<>();
protected int selectedIndex = 0;
private final RecyclerView mView;
private SuggestionsAdapter mSuggestionsAdapter;
public SuggestionsView(View mainView) {
super();
mView = mainView.findViewById(R.id.main_suggestions_list);
mView.setLayoutManager(new LinearLayoutManager(mainView.getContext(), RecyclerView.HORIZONTAL,false));
initDataAdapter(mainView.getContext());
initSeparator(mainView.getContext());
configureAnimation();
}
private void configureAnimation() {
DefaultItemAnimator animator = new DefaultItemAnimator();
int translateDuration = T9Preferences.getInstance().getSuggestionTranslateAnimationDuration();
int selectDuration = T9Preferences.getInstance().getSuggestionSelectAnimationDuration();
animator.setMoveDuration(selectDuration);
animator.setChangeDuration(translateDuration);
animator.setAddDuration(translateDuration);
animator.setRemoveDuration(translateDuration);
mView.setItemAnimator(animator);
}
private void initDataAdapter(Context context) {
mSuggestionsAdapter = new SuggestionsAdapter(
context,
R.layout.suggestion_list_view,
R.id.suggestion_list_item,
ContextCompat.getColor(context, R.color.candidate_selected),
suggestions
);
mView.setAdapter(mSuggestionsAdapter);
}
private void initSeparator(Context context) {
// Extra XML is required instead of a ColorDrawable object, because setting the highlight color
// erases the borders defined using the ColorDrawable.
Drawable separatorDrawable = ContextCompat.getDrawable(context, R.drawable.suggestion_separator);
if (separatorDrawable == null) {
return;
}
DividerItemDecoration separator = new DividerItemDecoration(mView.getContext(), RecyclerView.HORIZONTAL);
separator.setDrawable(separatorDrawable);
mView.addItemDecoration(separator);
}
public boolean isShown() {
return suggestions.size() > 0;
}
public int getCurrentIndex() {
return selectedIndex;
}
public String getCurrentSuggestion() {
return getSuggestion(selectedIndex);
}
public String getSuggestion(int id) {
return id >= 0 && id < suggestions.size() ? suggestions.get(id) : "";
}
@SuppressLint("NotifyDataSetChanged")
public void setSuggestions(List<String> newSuggestions, int initialSel) {
suggestions.clear();
selectedIndex = 0;
if (newSuggestions != null) {
suggestions.addAll(newSuggestions);
selectedIndex = Math.max(initialSel, 0);
}
mSuggestionsAdapter.setSelection(selectedIndex);
mSuggestionsAdapter.notifyDataSetChanged();
mView.scrollToPosition(selectedIndex);
}
public void scrollToSuggestion(int increment) {
if (suggestions.size() <= 1) {
return;
}
int oldIndex = selectedIndex;
selectedIndex = selectedIndex + increment;
if (selectedIndex == suggestions.size()) {
selectedIndex = 0;
} else if (selectedIndex < 0) {
selectedIndex = suggestions.size() - 1;
}
mSuggestionsAdapter.setSelection(selectedIndex);
mSuggestionsAdapter.notifyItemChanged(oldIndex);
mSuggestionsAdapter.notifyItemChanged(selectedIndex);
mView.scrollToPosition(selectedIndex);
}
}