<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android使用代碼實(shí)現(xiàn)一個(gè)填空題

          共 8341字,需瀏覽 17分鐘

           ·

          2021-08-18 18:12

          剛看到要做填空題這個(gè)需求的時(shí)候,第一個(gè)反應(yīng)是到百度,啊...不對(duì),谷歌上搜一下有沒(méi)有類(lèi)似的Demo,無(wú)奈搜出來(lái)的全是Android面試題,唉,算了,還是老老實(shí)實(shí)自己實(shí)現(xiàn)吧,先看下效果:


          學(xué)習(xí)一些基礎(chǔ)知識(shí)


          首先來(lái)學(xué)習(xí)一下如何對(duì)TextView的局部設(shè)置顏色和點(diǎn)擊事件,這里要用到一個(gè)很重要的類(lèi)SpannableString。
          Talk is cheap. Show me the code.
          public class SpannableStringActivity extends BaseActivity {
          @Bind(R.id.tv_content) TextView tvContent;
          @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_spannable_string); ButterKnife.bind(this);
          initData(); }
          private void initData() { String originContent = "你看我不僅能變顏色,還能點(diǎn)擊。"; SpannableString content = new SpannableString(originContent);
          // 設(shè)置顏色 ForegroundColorSpan colorSpan = new ForegroundColorSpan(Color.parseColor("#4DB6AC")); content.setSpan(colorSpan, 7, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
          // 設(shè)置點(diǎn)擊事件 MyClickableSpan myClickableSpan = new MyClickableSpan(); content.setSpan(myClickableSpan, 12, 14, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 設(shè)置此方法后,點(diǎn)擊事件才能生效 tvContent.setMovementMethod(LinkMovementMethod.getInstance());
          tvContent.setText(content); }
          class MyClickableSpan extends ClickableSpan {
          @Override public void onClick(View widget) { Toast.makeText(SpannableStringActivity.this, "我被點(diǎn)擊了", Toast.LENGTH_SHORT).show(); } }}

          看下效果:


          簡(jiǎn)單說(shuō)下,首先把要顯示的內(nèi)容轉(zhuǎn)成SpannableString對(duì)象,然后通過(guò)ForegroundColorSpan設(shè)置顏色,ClickableSpan設(shè)置點(diǎn)擊事件,SpannableString通過(guò)調(diào)用setSpan方法將顏色和點(diǎn)擊事件應(yīng)用到顯示的內(nèi)容中,setSpan方法需要傳入設(shè)置格式生效的起始坐標(biāo)(比如顏色的起始坐標(biāo)分別是7、8,那么就傳入7,8+1),最后注意一下Spanned.SPAN_EXCLUSIVE_EXCLUSIVE這個(gè)標(biāo)志,一共有四種flag可以選擇,分別是:

          • Spanned.SPAN_INCLUSIVE_INCLUSIVE:前后都包括

          • Spanned.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括

          • Spanned.SPAN_INCLUSIVE_EXCLUSIVE:前面包括,后面不包括

          • Spanned.SPAN_EXCLUSIVE_INCLUSIVE:前面不包括,后面包括


          這個(gè)flag是用來(lái)標(biāo)識(shí)在Span范圍內(nèi)的文本,前后輸入新的字符時(shí)是否也使用這個(gè)效果用的。一臉蒙圈,啥,你說(shuō)的是啥?還是看圖吧:

          我們把flag設(shè)置為:
          Spanned.SPAN_INCLUSIVE_EXCLUSIVE
          (前面包括,后面不包括)。



          是不是清晰了很多,如果圖還看不懂,慢走不送!

          實(shí)現(xiàn)


          首先初始化一些數(shù)據(jù)

          public class FillBlankView extends RelativeLayout {
          private TextView tvContent; private Context context; // 答案集合 private List<String> answerList; // 答案范圍集合 private List<AnswerRange> rangeList; // 填空題內(nèi)容 private SpannableStringBuilder content;
          public FillBlankView(Context context) { this(context, null); }
          public FillBlankView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
          public FillBlankView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; initView(); }
          private void initView() { LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.layout_fill_blank, this);
          tvContent = (TextView) findViewById(R.id.tv_content); }
          ...}

          定義一個(gè)設(shè)置數(shù)據(jù)的方法,供外部調(diào)用
          /** * 設(shè)置數(shù)據(jù) * * @param originContent   源數(shù)據(jù) * @param answerRangeList 答案范圍集合 */public void setData(String originContent, List<AnswerRange> answerRangeList) {    if (TextUtils.isEmpty(originContent) || answerRangeList == null            || answerRangeList.isEmpty()) {        return;    }
          // 獲取課文內(nèi)容 content = new SpannableStringBuilder(originContent); // 答案范圍集合 rangeList = answerRangeList;
          // 設(shè)置下劃線(xiàn)顏色 for (AnswerRange range : rangeList) { ForegroundColorSpan colorSpan = new ForegroundColorSpan(Color.parseColor("#4DB6AC")); content.setSpan(colorSpan, range.start, range.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); }
          // 答案集合 answerList = new ArrayList<>(); for (int i = 0; i < rangeList.size(); i++) { answerList.add(""); }
          // 設(shè)置填空處點(diǎn)擊事件 for (int i = 0; i < rangeList.size(); i++) { AnswerRange range = rangeList.get(i); BlankClickableSpan blankClickableSpan = new BlankClickableSpan(i); content.setSpan(blankClickableSpan, range.start, range.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); }
          // 設(shè)置此方法后,點(diǎn)擊事件才能生效 tvContent.setMovementMethod(LinkMovementMethod.getInstance()); tvContent.setText(content);}

          代碼中已經(jīng)寫(xiě)了很全的注釋?zhuān)饕窃O(shè)置填空處的顏色和點(diǎn)擊事件。

          點(diǎn)擊事件
          /** * 點(diǎn)擊事件 */class BlankClickableSpan extends ClickableSpan {
          private int position;
          public BlankClickableSpan(int position) { this.position = position; }
          @Override public void onClick(final View widget) { View view = LayoutInflater.from(context).inflate(R.layout.layout_input, null); final EditText etInput = (EditText) view.findViewById(R.id.et_answer); Button btnFillBlank = (Button) view.findViewById(R.id.btn_fill_blank);
          // 顯示原有答案 String oldAnswer = answerList.get(position); if (!TextUtils.isEmpty(oldAnswer)) { etInput.setText(oldAnswer); etInput.setSelection(oldAnswer.length()); }
          final PopupWindow popupWindow = new PopupWindow(view, LayoutParams.MATCH_PARENT, dp2px(40)); // 獲取焦點(diǎn) popupWindow.setFocusable(true); // 為了防止彈出菜單獲取焦點(diǎn)之后,點(diǎn)擊Activity的其他組件沒(méi)有響應(yīng) popupWindow.setBackgroundDrawable(new PaintDrawable()); // 設(shè)置PopupWindow在軟鍵盤(pán)的上方 popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); // 彈出PopupWindow popupWindow.showAtLocation(tvContent, Gravity.BOTTOM, 0, 0);
          btnFillBlank.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 填寫(xiě)答案 String answer = etInput.getText().toString(); fillAnswer(answer, position); popupWindow.dismiss(); } });
          // 顯示軟鍵盤(pán) InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); }
          @Override public void updateDrawState(TextPaint ds) { // 不顯示下劃線(xiàn) ds.setUnderlineText(false); }}

          點(diǎn)擊填空處彈出一個(gè)PopupWindow輸入框,輸入答案后點(diǎn)擊確定,
          調(diào)用fillAnswer方法將答案設(shè)置到填空處。

          填寫(xiě)答案


          前方高能,請(qǐng)減速慢行!
          /** * 填寫(xiě)答案 * * @param answer   當(dāng)前填空處答案 * @param position 填空位置 */private void fillAnswer(String answer, int position) {    answer = " " + answer + " ";
          // 替換答案 AnswerRange range = rangeList.get(position); content.replace(range.start, range.end, answer);
          // 更新當(dāng)前的答案范圍 AnswerRange currentRange = new AnswerRange(range.start, range.start + answer.length()); rangeList.set(position, currentRange);
          // 答案設(shè)置下劃線(xiàn) content.setSpan(new UnderlineSpan(), currentRange.start, currentRange.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
          // 將答案添加到集合中 answerList.set(position, answer.replace(" ", ""));
          // 更新內(nèi)容 tvContent.setText(content);
          for (int i = 0; i < rangeList.size(); i++) { if (i > position) { // 獲取下一個(gè)答案原來(lái)的范圍 AnswerRange oldNextRange = rangeList.get(i); int oldNextAmount = oldNextRange.end - oldNextRange.start; // 計(jì)算新舊答案字?jǐn)?shù)的差值 int difference = currentRange.end - range.end;
          // 更新下一個(gè)答案的范圍 AnswerRange nextRange = new AnswerRange(oldNextRange.start + difference, oldNextRange.start + difference + oldNextAmount); rangeList.set(i, nextRange); } }}

          首先把填空處的下劃線(xiàn)或舊答案替換成新答案,然后更新一下當(dāng)前的答案范圍,由于下劃線(xiàn)已經(jīng)被答案替換了,所以需要為答案設(shè)置一條下劃線(xiàn),最后把答案更新到集合中,這樣一個(gè)填空就完成了。

          But,當(dāng)一個(gè)填空處的答案范圍改變后,后面所有的填空處答案范圍都要跟著改變,所以還需要再更新一下后面填空處的答案范圍。首先獲取下一個(gè)答案原來(lái)的范圍,計(jì)算一下需要向前或向后移動(dòng)的距離,然后更新一下答案范圍就大功告成了。


          最后看下如何設(shè)置數(shù)據(jù)
          public class MainActivity extends AppCompatActivity {
          @BindView(R.id.fbv_content) FillBlankView fbvContent;
          @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this);
          initData(); }
          private void initData() { String content = "紛紛揚(yáng)揚(yáng)的________下了半尺多厚。天地間________的一片。我順著________工地走了四十多公里," + "只聽(tīng)見(jiàn)各種機(jī)器的吼聲,可是看不見(jiàn)人影,也看不見(jiàn)工點(diǎn)。一進(jìn)靈官峽,我就心里發(fā)慌。";
          // 答案范圍集合 List<AnswerRange> rangeList = new ArrayList<>(); rangeList.add(new AnswerRange(5, 13)); rangeList.add(new AnswerRange(23, 31)); rangeList.add(new AnswerRange(38, 46));
          fbvContent.setData(content, rangeList); }}

          源碼地址:
          https://github.com/alidili/Demos/tree/master/FillBlankQuestionDemo

          到這里就結(jié)束啦。
          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本欧美在线人 | 精品国产制服丝袜高跟 | 成人做爰免费A片在线观看视频网站 | 竹菊精品一区二区三区 | 天天操天天摸天天日天天爱 |