android 多行编辑部分不可编辑的文本,如填空

mpgws1up  于 2023-01-11  发布在  Android
关注(0)|答案(4)|浏览(230)

我需要有一个视图,其中包含文本视图和编辑文本。
示例:

Yay! you made it to ______ We should hang out! feel ____ to follow me.

上面的"______"可以是任何长度,它应该感觉像一个段落的结尾。上面给出的文本的其余部分是不可更改的。就像填空一样。

drkbr07n

drkbr07n1#

从我的Angular 来看,一个填空小部件应该完成以下任务:
1.只允许更改文本的特定标识部分。文本的其余部分被锁定。
1.不允许光标移动到锁定文本中。
1.像EditText一样从一条线流到另一条线。
1.用可变的空格位置概括。
下面是基于EditText的此类小部件的实现。(BlanksSpan)扩展自StyleSpan。空白范围由五个下划线标识("_____")。光标移动在OnSelectionChanged()和各种EditText回调中控制。通过TextWatcher监控文本的变更,并在此处对显示的文本进行调整。
下面是正在使用的小部件的视频:

    • 填充空白编辑文本. java**
public class FillInBlanksEditText extends android.support.v7.widget.AppCompatEditText  
    implements View.OnFocusChangeListener, TextWatcher {  
    private int mLastSelStart;  
    private int mLastSelEnd;  
    private BlanksSpan mSpans[];  
    private Editable mUndoChange;  
    private BlanksSpan mWatcherSpan;  

    public FillInBlanksEditText(Context context) {  
        super(context);  
        init();  
    }  

    public FillInBlanksEditText(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        init();  
    }  

    public FillInBlanksEditText(Context context, AttributeSet attrs, int defStyleAttr) {  
        super(context, attrs, defStyleAttr);  
        init();  
    }  

    private void init() {  
        mSpans = setSpans();  
        setOnFocusChangeListener(this);  
    }  

    @Override  
  public void onRestoreInstanceState(Parcelable state) {  
        mSpans = null;  
        super.onRestoreInstanceState(state);  
        Editable e = getEditableText();  
        mSpans = e.getSpans(0, e.length(), BlanksSpan.class);  
    }  

    @Override  
  public void onFocusChange(View v, boolean hasFocus) {  
        if (hasFocus) {  
            addTextChangedListener(this);  
            if (findInSpan(getSelectionStart(), getSelectionEnd()) != null) {  
                mLastSelStart = getSelectionStart();  
                mLastSelEnd = getSelectionEnd();  
            } else if (findInSpan(mLastSelStart, mLastSelEnd) == null) {  
                setSelection(getEditableText().getSpanStart(mSpans[0]));  
            }  
        } else {  
            removeTextChangedListener(this);  
        }  
    }  

    @Override  
  protected void onSelectionChanged(int selStart, int selEnd) {  
        if (!isFocused() || mSpans == null ||  
            (getSelectionStart() == mLastSelStart && getSelectionEnd() == mLastSelEnd)) {  
            return;  
        }  

        // The selection must be completely within a Blankspan.  
  final BlanksSpan span = findInSpan(selStart, selEnd);  
        if (span == null) {  
            // Current selection is not within a Blankspan. Restore selection to prior location.  
  moveCursor(mLastSelStart);  
        } else if (selStart > getEditableText().getSpanStart(span) + span.getDataLength()) {  
            // Acceptable location for selection (within a Blankspan).  
 // Make sure that the cursor is at the end of the entered data.  mLastSelStart = getEditableText().getSpanStart(span) + span.getDataLength();  
            mLastSelEnd = mLastSelStart;  
            moveCursor(mLastSelStart);  

        } else {  
            // Just capture the placement.  
  mLastSelStart = selStart;  
            mLastSelEnd = selEnd;  
        }  
        super.onSelectionChanged(mLastSelStart, mLastSelEnd);  
    }  

    // Safely move the cursor without directly invoking setSelection from onSelectionChange.  
  private void moveCursor(final int selStart) {  
        post(new Runnable() {  
            @Override  
  public void run() {  
                setSelection(selStart);  
            }  
        });  
        // Stop cursor form jumping on move.  
  getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {  
            @Override  
  public boolean onPreDraw() {  
                getViewTreeObserver().removeOnPreDrawListener(this);  
                return false;  
            }  
        });  
    }  

    @Nullable  
  private BlanksSpan findInSpan(int selStart, int selEnd) {  
        for (BlanksSpan span : mSpans) {  
            if (selStart >= getEditableText().getSpanStart(span) &&  
                selEnd <= getEditableText().getSpanEnd(span)) {  
                return span;  
            }  
        }  
        return null;  
    }  

    // Set up a Blankspan to cover each occurrence of BLANKS_TOKEN.  
  private BlanksSpan[] setSpans() {  
        Editable e = getEditableText();  
        String s = e.toString();  
        int offset = 0;  
        int blanksOffset;  

        while ((blanksOffset = s.substring(offset).indexOf(BLANKS_TOKEN)) != -1) {  
            offset += blanksOffset;  
            e.setSpan(new BlanksSpan(Typeface.BOLD), offset, offset + BLANKS_TOKEN.length(),  
                      Spanned.SPAN_INCLUSIVE_INCLUSIVE);  
            offset += BLANKS_TOKEN.length();  
        }  
        return e.getSpans(0, e.length(), BlanksSpan.class);  
    }  

    // Check change to make sure that it is acceptable to us.  
  @Override  
  public void beforeTextChanged(CharSequence s, int start, int count, int after) {  
        mWatcherSpan = findInSpan(start, start + count);  
        if (mWatcherSpan == null) {  
            // Change outside of a Blankspan. Just put things back the way they were.  
 // Do this in afterTextChaanged.  mUndoChange = Editable.Factory.getInstance().newEditable(s);  
        } else {  
            // Change is OK. Track data length.  
  mWatcherSpan.adjustDataLength(count, after);  
        }  
    }  

    @Override  
  public void onTextChanged(CharSequence s, int start, int before, int count) {  
        // Do nothing...  
  }  

    @Override  
  public void afterTextChanged(Editable s) {  
        if (mUndoChange == null) {  
            // The change is legal. Modify the contents of the span to the format we want.  
  CharSequence newContents = mWatcherSpan.getFormattedContent(s);  
            if (newContents != null) {  
                removeTextChangedListener(this);  
                int selection = getSelectionStart();  
                s.replace(s.getSpanStart(mWatcherSpan), s.getSpanEnd(mWatcherSpan), newContents);  
                setSelection(selection);  
                addTextChangedListener(this);  
            }  
        } else {  
            // Illegal change - put things back the way they were.  
  removeTextChangedListener(this);  
            setText(mUndoChange);  
            mUndoChange = null;  
            addTextChangedListener(this);  
        }  
    }  

    @SuppressWarnings("WeakerAccess")  
    public static class BlanksSpan extends StyleSpan {  
        private int mDataLength;  

        public BlanksSpan(int style) {  
            super(style);  
        }  

        @SuppressWarnings("unused")  
        public BlanksSpan(@NonNull Parcel src) {  
            super(src);  
        }  

        public void adjustDataLength(int count, int after) {  
            mDataLength += after - count;  
        }  

        @Nullable  
  public CharSequence getFormattedContent(Editable e) {  
            if (mDataLength == 0) {  
                return BLANKS_TOKEN;  
            }  
            int spanStart = e.getSpanStart(this);  
            return (e.getSpanEnd(this) - spanStart > mDataLength)  
                ? e.subSequence(spanStart, spanStart + mDataLength)  
                : null;  
        }  

        public int getDataLength() {  
            return mDataLength;  
        }  

    }  

    @SuppressWarnings({"FieldCanBeLocal", "unused"})  
    private static final String TAG = "FillInBlanksEditText";  
    private static final String BLANKS_TOKEN = "_____";  

}
    • 活动_主要. java**

示例布局。

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.fillintheblanks.FillInBlanksEditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:background="@android:color/transparent"
        android:inputType="textMultiLine"
        android:padding="16dp"
        android:text="Yay! You made it to _____. We should hang out! Feel _____ to follow me."
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.fillintheblanks.FillInBlanksEditText
        android:id="@+id/editText2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:background="@android:color/transparent"
        android:inputType="textMultiLine"
        android:padding="16dp"
        android:text="_____ says that it is time to _____. Are you _____?"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/editText" />

</android.support.constraint.ConstraintLayout>

需要注意的几点:
1.在提取模式下,如果在BlanksSpan之外进行触摸,光标位置会跳来跳去。事情仍然可以工作,但有点不正常。
1.空白字段的长度是固定的,但可以通过一些额外的工作使其长度可变。
1.控件中的动作模式需要根据需要做一些工作。

laik7k3q

laik7k3q2#

多行编辑部分不可编辑的文本,如填空
您可以使用**TextWatcher()**满足此要求
试试这个,他是这个周围的小工作
主要活动

public class MainActivity extends AppCompatActivity {

    EditText myEditText;
    String startText = "I'm The First Part";
    String lastText = "I'm The Last Part";

    SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final SpannableStringBuilder firstStringBuilder = new SpannableStringBuilder(startText);
        final SpannableStringBuilder lastStringBuilder = new SpannableStringBuilder(lastText);

        StyleSpan firstStyleSpan = new StyleSpan(android.graphics.Typeface.BOLD);

        firstStringBuilder.setSpan(firstStyleSpan, 0, firstStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
        lastStringBuilder.setSpan(firstStyleSpan, 0, lastStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold

        myEditText = findViewById(R.id.myEditText);

        spannableStringBuilder.append(firstStringBuilder);
        spannableStringBuilder.append("   ");
        spannableStringBuilder.append(lastStringBuilder);

        myEditText.setText(spannableStringBuilder);
        Selection.setSelection(myEditText.getText(), startText.length() + 1);

        myEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count,
                                          int after) {
                // TODO Auto-generated method stub

            }

            @Override
            public void afterTextChanged(Editable s) {

                if (!s.toString().startsWith(firstStringBuilder.toString())
                        || !s.toString().contains(lastText)) {
                    Log.e("StringBuilder_TAG", spannableStringBuilder.toString());

                    myEditText.setText(spannableStringBuilder);
                    Selection.setSelection(myEditText.getText(), myEditText.getText().length() - lastStringBuilder.length() - 1);
                } else {

                    spannableStringBuilder.clear();
                    spannableStringBuilder.append(s.toString());
                    Log.e("My_TAG", spannableStringBuilder.toString());

                }

            }
        });

    }

}

布局.活动_主

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/myEditText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:padding="5dp" />

</LinearLayout>

下面是上述代码https://www.youtube.com/watch?v=pfhUzLiFD6U的输出视频
使用上面的代码你可以使editext的开头和结尾部分不可编辑

    • 注**

您也可以使用**TextDrawable**,这里有一些链接

您还可以为此创建自定义EditText

您也可以使用InputFilter

r6l8ljro

r6l8ljro3#

解决方案一

尝试使用flexbox-layout -https://github.com/google/flexbox-layout

解决方案二

使用文本监视器

解决方案三

  • 使用html,css和javascript来设计一个简单的网页。
  • 使用webview加载html文件。
nbewdwxp

nbewdwxp4#

按照@Cheticamp的答案,当你在setText()方法中设置一个SpannableString时,它就起作用了。
此外,您还应覆盖setText()方法并将Spans设置为mSpans:

@Override
public void setText(CharSequence text, BufferType type) {
    mSpans = Editable.Factory.getInstance().newEditable(text).getSpans(0, text.length(), BlanksSpan.class);
    Log.d(TAG, "setText: " + mSpans.length);
    super.setText(text, type);
}

如果你不设置一个SpannableString,Editable接口将得到一个简单的没有Spans的String。所以如果你调用如下方法:

  1. getSpanStart(标签)返回-1;
  2. getSpanEnd(标签)返回-1;
  3. getSpans(start,end,Class)返回空数组

相关问题