我需要一个包含textview和edittext的视图。
示例:
Yay! you made it to ______ We should hang out! feel ____ to follow me.
“ _____”上方可以是任意长度,最后应该像一段。上面给出的其余文本是不可更改的。就像填补空白一样。
答案 0 :(得分:3)
从我的角度来看,填写空白的小部件应执行以下操作:
EditText
那样一行一行地流动。这是基于EditText
的此类小部件的实现。使用从BlanksSpan
扩展的跨度(StyleSpan
)设置可编辑跨度。文本中的五个下划线(______)标识了空白范围。光标移动在OnSelectionChanged()
和各种EditText
回调中进行控制。文本的更改由TextWatcher
监视,并在那里显示的文本进行了调整。
以下是正在使用的小部件的视频:
FillInBlanksEditText.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 = "_____";
}
activity_main.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>
一些注意事项:
BlanksSpan
之外进行触摸,则光标位置会跳来跳去。事情仍然有效,但行为异常。答案 1 :(得分:2)
多行edittext,其中的部分不可编辑,例如填入空白
您可以使用 TextWatcher()
满足此要求
尝试这个,他是解决这个问题的小手笔
MainActivity
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());
}
}
});
}
}
layout.activity_main
<?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>
的输出视频
使用上述代码,您无法编辑editext
的第一部分和最后一部分
注意
您还可以使用 TextDrawable
这里有一些链接
您也可以为此创建自定义EditText
您也可以使用InputFilter
答案 2 :(得分:0)
解决方案之一
尝试使用flexbox-layout-https://github.com/google/flexbox-layout。
第二种解决方案
使用textWatcher
解决方案三