起因:最近工作中遇到一个需求,使用手机进行填空题考试。
分析:因为涉及到判分,需要答案与文字一一对应,刚开始在网上找一些方案在TextView修改文字样式然后处理事件,感觉样式不是太好控制操作繁琐,然后又注意到学习强国的填空题真的很符合需求,所以就拿来模仿了。1
截图如下:左边我,右边学习强国
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_marginLeft="1.5dp"
android:layout_marginTop="2dp"
android:layout_marginRight="1.5dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textColor="#D9000000"
android:textSize="16sp"
android:textStyle="bold"
android:visibility="gone" />
<EditText
android:id="@+id/et_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_edit_selecter"
android:cursorVisible="false"
android:gravity="center"
android:maxLength="1"
android:textColor="#FF16AFF3"
android:visibility="gone" />
</LinearLayout>
效果就是这个样子的。
总体思路:RecyclerView 实现数据的装载,使用TextView和Edittext展示内容与填空处,监听焦点,软件盘退格,并设置焦点,根据屏幕宽度设置一行展示的文字个数。
布局文件:布局文件就不多说了,根据自己需求写,但是要同时包含TextView、EditText
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_marginLeft="1.5dp"
android:layout_marginTop="2dp"
android:layout_marginRight="1.5dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textColor="#D9000000"
android:textSize="16sp"
android:textStyle="bold"
android:visibility="gone" />
<EditText
android:id="@+id/et_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_edit_selecter"
android:cursorVisible="false"
android:gravity="center"
android:maxLength="1"
android:textColor="#FF16AFF3"
android:visibility="gone" />
</LinearLayout>
计算一行展示的文字个数
这块注释写的很清楚了,需要去除内外边距计算 ,其实最后得出的numInLine还可以进行减一操作,为了安全起见,但是也可以不用(int类型除法本来就会丢失精度的)。
//获取屏幕宽度
int screenWidth = getScreenWidth();
//去除padding宽度
int contentWidth = screenWidth - DensityUtils.dp2px(context, Constant.EXAM_PADDING_DP);
//计算出一行字数
int numInLine = contentWidth / DensityUtils.dp2px(context, Constant.EXAM_LETTER_DP);
//设置一行个数
rv_list.setLayoutManager(new GridLayoutManager(context, numInLine));
/**
* 获取屏幕宽度
*/
public int getScreenWidth() {
DisplayMetrics dm2 = getResources().getDisplayMetrics();
return dm2.widthPixels;
}
数据源
数据源很简单,每个对象代表一个字,标明是否是填空处
public class BlankTextBean {
/**
* 文字
*/
private String text;
/**
* 是否是填空
*/
private boolean blank;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public boolean isBlank() {
return blank;
}
public void setBlank(boolean blank) {
this.blank = blank;
}
}
适配器(用心去感受。。。)
这块主要需求为:
1.无论点击哪个填空处,焦点总是在第一个没有字的格子上。
- 点击退格键,如果当前框有字则删除,否则像钱移动一个格子。
3.填空时,如果填了字,并且后面还有格子焦点自动向后移动一个格子。
PS:在适当时机调用clearList();注意保存并设置临时填空数据;
class TextAdapter extends BaseQuickAdapter<BlankTextBean, BaseViewHolder> {
private List<EditText> editTextList = new ArrayList<>();
private boolean isDelete = false;
public TextAdapter(@Nullable List<BlankTextBean> data) {
super(R.layout.item_blank_text, data);
}
public void clearList() {
editTextList.clear();
}
/**
* 获取首个焦点位置
*
* @return
*/
private int getFirstFocusPosition() {
for (int i = 0; i < editTextList.size(); i++) {
if (!editTextList.get(i).getText().toString().isEmpty()) {
if (i == (editTextList.size() - 1)) {
return i;
}
} else {
return i;
}
}
return 0;
}
/**
* 获取点击的edittext在集合的位置
*
* @param editText
* @return
*/
private int getEdittextPosition(EditText editText) {
for (int i = 0; i < editTextList.size(); i++) {
if (editText.hashCode() == editTextList.get(i).hashCode()) {
return i;
}
}
return 0;
}
@Override
protected void convert(BaseViewHolder helper, BlankTextBean item) {
TextView tv_text = helper.getView(R.id.tv_text);
EditText et_text = helper.getView(R.id.et_text);
tv_text.setText(item.getText());
if (item.isBlank()) {
editTextList.add(et_text);
//设置答案文字
if (ExamBlankView.this.questionBean != null && ExamBlankView.this.questionBean.getUserAnswer() != null) {
if (editTextList.size() <= ExamBlankView.this.questionBean.getUserAnswer().length()) {
editTextList.get(editTextList.size() - 1).setText(String.valueOf(ExamBlankView.this.questionBean.getUserAnswer().charAt(editTextList.size() - 1)));
}
}
tv_text.setVisibility(View.GONE);
et_text.setVisibility(View.VISIBLE);
} else {
tv_text.setVisibility(View.VISIBLE);
et_text.setVisibility(View.GONE);
}
/**
* 设置获取焦点事件,使得焦点在第一个没字的edittext上,如果都有字停留在最后一个格子处
*/
et_text.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus && !isDelete) {
editTextList.get(getFirstFocusPosition()).requestFocus();
}
});
/**
* 焦点的转移
*/
et_text.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
int edittextPosition = getEdittextPosition(et_text);//当前点击的edittext在几何中的位置
if (s.toString().length() >= 1 && (edittextPosition + 1) < editTextList.size()) {//增加
editTextList.get(edittextPosition + 1).requestFocus();
} else if (s.toString().length() < 1 && (edittextPosition - 1) >= 0) {//删除
isDelete = true;
editTextList.get(edittextPosition - 1).requestFocus();
isDelete = false;
}
}
});
/**
* 退格键删除空时的操作
*/
et_text.setOnKeyListener((v, keyCode, event) -> {
int edittextPosition = getEdittextPosition(et_text);
if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
if (et_text.getText().toString().isEmpty()) {//当前文字为空
isDelete = true;
if (edittextPosition - 1 >= 0) {
editTextList.get(edittextPosition - 1).requestFocus();
} else {
editTextList.get(edittextPosition).requestFocus();
}
isDelete = false;
return true;
} else if (!et_text.getText().toString().isEmpty()) {//当前文字不为空
isDelete = true;
et_text.setText("");
if (edittextPosition - 1 >= 0) {
editTextList.get(edittextPosition - 1).requestFocus();
} else {
editTextList.get(edittextPosition).requestFocus();
}
isDelete = false;
return true;
}
return false;
}
return false;
});
}
}
完!如代码不全可以联系我