很久不寫博客了,應爲一直真的都很忙,沒時間寫,正好今天有時間寫一下O(∩_∩)O哈哈~。
起因:最近工作中遇到一個需求,使用手機進行填空題考試。
分析:因爲涉及到判分,需要答案與文字一一對應,剛開始在網上找一些方案在TextView修改文字樣式然後處理事件,感覺樣式不是太好控制操作繁瑣,然後又注意到學習強國的填空題真的很符合需求,所以就拿來模仿了。
截圖如下:左邊我,右邊學習強國
效果就是這個樣子的。
總體思路: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.無論點擊哪個填空處,焦點總是在第一個沒有字的格子上。
2. 點擊退格鍵,如果當前框有字則刪除,否則像錢移動一個格子。
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;
});
}
}