gpt4 book ai didi

android - 串行(类似 TTY)、与(富) TextView 的同步用户交互

转载 作者:行者123 更新时间:2023-11-30 00:50:17 29 4
gpt4 key购买 nike

我想使用类似 TextView 的东西进行类似串行/TTY/文本终端的同步用户交互。我的意思是我需要以下操作:

  • 在末尾添加一些新文本(使用各种 Span 设置样式)
  • 同步等待用户在最后输入一些进一步的文本(使用一些 Span 样式),通过换行符/虚拟键盘上的一些“提交”式按钮等完成。文本的任何其他部分都不应是可选择的、可编辑的等。

当然,第二个操作在 UI 线程上没有任何意义,所以按照我的想象,我会有一个单独的线程,其中包含表单的同步代码

// NOT on the UI thread here
textui.addText("What's your name? ",
new StyleSpan(Typeface.BOLD),
new ForegroundColorSpan(Color.RED));
String name = textui.getLine(
new BackgroundColorSpan(Color.GREEN),
new StyleSpan(Typeface.ITALIC));
textui.addText("Hello, ");
textui.addText(name,
new StyleSpan(Typeface.BOLD));
textui.addText("!");

注意 getLine 应该等待用户输入,用户不能编辑 What's your name? 部分,当用户输入时,他的输入应为绿色斜体。这是一些模型:

  1. 添加第一行后:

    1

  2. 用户开始输入

    2

  3. 用户已提交他的输入

    3

我试过的一件事是使用 EditTextInputFilter 来拒绝用户编辑,直到调用 getLine 并禁止编辑任何以前的输出,但这仍然允许用户四处移动光标并选择输出,就好像它是输入一样。

编辑添加:由于我的问题“不清楚”,我得到了接近的投票,所以让我尝试用另一种方式解释它。想象一下,您有一个使用 System.out.printlnSystem.console().readLine 的控制台程序,并且您想要在 Android 控件(可能包围通过其他 Android 控件),添加样式。

最佳答案

更新:添加了允许/禁止编辑以及指定字段是单行还是多行的标志。将自定义 EditText 移动到单独的类文件中。修复了光标移动问题。

以下是一种可能适合您的方法。我知道您正在寻找一种 TTY 功能。我不确定同步部分是关于什么的,但这里是。

当不允许编辑时,方法是使用自定义 EditText 来消除用户在字段中四处移动的能力,并从硬件键盘检测回车键(字段终止)和禁止删除键。这全部由 TtyEditText.java 中定义的自定义 EditText 完成。

屏幕键盘的控制略有不同,并使用 TextWatcher 来处理事情。

示例应用简单地循环显示三个提示,并使用您在问题中提到的 Spans 类型将用户输入的信息显示回屏幕。

这是一个不允许编辑的快速视频。

Demo video

使用向左和向右箭头键在字段中四处移动有一个怪癖。右箭头键工作正常,但左箭头键似乎想要在字段之间移动而不是返回字段内。在找到解决方案之前,我已明确禁用这两个功能,即使这是一个问题。

根据 this page :

Users can also navigate your app using the arrow keys on a keyboard (the behavior is the same as when navigating with a D-pad or trackball). The system provides a best-guess as to which view should be given focus in a given direction based on the layout of the views on screen. Sometimes, however, the system might guess wrong.

那么,Android 是不是猜错了?这已通过使已完成的字段不可聚焦来解决。已更正代码以处理光标键。

MainActivity.java

public class MainActivity extends AppCompatActivity
implements TextWatcher, TtyEditText.FieldAction {
private String mPrompts[] = new String[]{
"What is your name? ",
"What is your quest? ",
"What is the air-speed velocity of an unladen swallow? "
};
private int mPromptIndex = -1;
private LinearLayout mLinearLayout;
private TtyEditText mCurrentField;

// true-current field can be edited (backspace/selection); false-disallow field editing
private final boolean mAllowEditing = true;
// true input is single line EditText else multi-line
private final boolean mSingleLine = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
final ScrollView scrollView;

super.onCreate(savedInstanceState);
scrollView = createViewGroup(); // Simple LinearLayout within ScrollView
mLinearLayout = (LinearLayout) scrollView.getChildAt(0);
setContentView(scrollView);

// Get the ball rolling with first prompt.
mCurrentField = generatePrompt(mLinearLayout);
}

// Create the next prompt field and place it at the bottom of the layout.
private TtyEditText generatePrompt(LinearLayout layout) {
final TtyEditText editText;
final SpannableString s;

if (++mPromptIndex >= mPrompts.length) {
mPromptIndex = 0;
}

editText = createTtyEditText(mAllowEditing, mPrompts[mPromptIndex].length());
s = styleText(mPrompts[mPromptIndex], new StyleSpan(Typeface.BOLD),
new ForegroundColorSpan(Color.RED));
editText.setText(s);
layout.addView(editText);

editText.setSingleLine(mSingleLine);
if (!mSingleLine) { // multi-line gets a "done" key.
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
}

editText.addTextChangedListener(MainActivity.this);

editText.requestFocus();

InputMethodManager imm =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);

return editText;
}

// Echo the user's input to the bottom of the layout.
private void echoResponse(LinearLayout layout, String enteredText) {
final TextView textView = new TextView(this);
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
SpannableString prefix;
SpannableString input;

prefix = styleText("You entered: ", new StyleSpan(Typeface.NORMAL),
new ForegroundColorSpan(Color.BLACK));
input = styleText(enteredText, new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(Color.BLACK));
textView.setLayoutParams(params);
textView.setTextSize(DEFAULT_TEXT_SIZE);
textView.setText(TextUtils.concat(prefix, input));
layout.addView(textView);
}

// Invoked from our EditText subclass when the enter key is seen on single line EditText and
// from this Activity on multiline EditText.
@Override
public void onEnter(TtyEditText editText) {
final String s = editText.getText().toString();
final String entry = s.substring(editText.getPromptLength(), s.length());

editText.removeTextChangedListener(this);
editText.setFocusable(false);
editText.setFocusableInTouchMode(false);
editText.setEnabled(false);
echoResponse(mLinearLayout, entry);
mCurrentField = generatePrompt(mLinearLayout);
}

@Override
public void afterTextChanged(Editable s) {
// Do nothing
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (mAllowEditing) {
if (start < mPrompts[mPromptIndex].length()) {
mCurrentField.removeTextChangedListener(this); // prevent infinite looping
mCurrentField.setText(s);
mCurrentField.addTextChangedListener(this);
}
} else if (after < count) { // Trying to delete, so don't allow it.
mCurrentField.removeTextChangedListener(this); // prevent infinite looping
mCurrentField.setText(s);
mCurrentField.addTextChangedListener(this);
}
}

// This method only does anything if the input field is multi-line.
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mSingleLine) {
return;
}
// Multi-line fields get a newline character inserted. Look for the newline, strip it
// and that will complete field entry.
int newlinePos = TextUtils.indexOf(s, "\n");
if (newlinePos != -1) {
mCurrentField.removeTextChangedListener(this); // prevent infinite looping
s = TextUtils.concat(s.subSequence(0, newlinePos),
s.subSequence(newlinePos + 1, s.length()));
mCurrentField.setText(s);
onEnter(mCurrentField);
}
}

// Add text to a TextView (could be an EditText) and apply spans for styling.
private SpannableString styleText(String text, StyleSpan style,
ForegroundColorSpan foregroundColor) {
final SpannableString s = new SpannableString(text);

s.setSpan(style, 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(foregroundColor, 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new ForegroundColorSpan(Color.GREEN), s.length(), s.length(),
Spanned.SPAN_INCLUSIVE_INCLUSIVE);
s.setSpan(new StyleSpan(Typeface.ITALIC), s.length(), s.length(),
Spanned.SPAN_INCLUSIVE_INCLUSIVE);
return s;
}

// Create an empty LinearLayout within ScrollView.
private ScrollView createViewGroup() {
final ScrollView scroll = new ScrollView(this);
final LinearLayout linearLayout = new LinearLayout(this);
final FrameLayout.LayoutParams params =
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
final int margins = convertDpToPixels(16f, this);

params.setMargins(margins, margins, margins, margins);
scroll.setLayoutParams(params);

linearLayout.setLayoutParams(new FrameLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
linearLayout.setOrientation(LinearLayout.VERTICAL);
/* Bottom padding is set here due to a very strange interaction between the
EditText, the soft keyboard, and the ScrollView. With zero padding, when
input field abuts the top of the soft keyboard and the user anter a space,
things get a little wacky with how things are handled. User input freezes and
there are bad calls made to this app. The bottom padding fixes it, though.
*/
linearLayout.setPadding(0, 0, 0, convertDpToPixels(16f, this));

scroll.addView(linearLayout);

return scroll;
}

// Create an instance of our subclass of EditText.
private TtyEditText createTtyEditText(final boolean allowEditing, final int promptLength) {
final TtyEditText editText;
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);

editText = new TtyEditText(this);
editText.setEditParams(allowEditing, promptLength);
editText.setLayoutParams(params);
editText.setTextSize(DEFAULT_TEXT_SIZE);
editText.setBackgroundResource(android.R.color.transparent);
editText.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
| InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
editText.setPadding(0, 0, 0, 0);
editText.setFocusable(true);
editText.setFocusableInTouchMode(true);

return editText;
}

private static int convertDpToPixels(float dp, Context context) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
context.getResources().getDisplayMetrics());
}

private static final float DEFAULT_TEXT_SIZE = 18f;
}

TtyEditText.java

public class TtyEditText extends AppCompatEditText {
boolean mAllowEditing = true;
int mPromptLength = 0;

// Interface to activity for the enter key. This can be expanded to accommodate other keys.
FieldAction mFieldAction;

public TtyEditText(Context context) {
this(context, null);
}

public TtyEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public TtyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mFieldAction = (FieldAction) context;
}

// Handle the "enter" and "delete" keys from hardware keyboards.
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {

case KeyEvent.KEYCODE_ENTER:
mFieldAction.onEnter(this);
return true;
}
super.onKeyDown(keyCode, event);
return false;
}

// Don't let the user move around in the field. If they try, put them back to the end.
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
//on selection move cursor to end of text
if (!mAllowEditing) { // No backspace/no cursor movement except to add to end
setSelection(this.length());
} else if (selStart < mPromptLength && this.getText().length() > 0) { // can't move into prompt area
setSelection(mPromptLength);
} else {
super.onSelectionChanged(selStart, selEnd);
}
}

public void setEditParams(boolean allowEditing, int promptLength) {
mAllowEditing = allowEditing;
mPromptLength = promptLength;
}

public int getPromptLength() {
return mPromptLength;
}

interface FieldAction {
public void onEnter(TtyEditText ttyEditText);
}
}

关于android - 串行(类似 TTY)、与(富) TextView 的同步用户交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41156642/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com