android edittext取消焦点_Android使用代码实现一个填空题

作者 |  容华谢后 地址 |  https://www.jianshu.com/p/3c8a1c987cea
刚看到要做填空题这个需求的时候,第一个反应是到百度,啊...不对,谷歌上搜一下有没有类似的Demo,无奈搜出来的全是Android面试题,唉,算了,还是老老实实自己实现吧,先看下效果

dbd7845994f113b8c5345c56dc271b22.gif

2.学习一些基础知识

首先来学习一下如何对TextView的局部设置颜色和点击事件, 这里要用到一个很重要的类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 = "你看我不仅能变颜色,还能点击。";        SpannableString content = new SpannableString(originContent);        // 设置颜色        ForegroundColorSpan colorSpan = new ForegroundColorSpan(Color.parseColor("#4DB6AC"));        content.setSpan(colorSpan, 7, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);        // 设置点击事件        MyClickableSpan myClickableSpan = new MyClickableSpan();        content.setSpan(myClickableSpan, 12, 14, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);        // 设置此方法后,点击事件才能生效        tvContent.setMovementMethod(LinkMovementMethod.getInstance());        tvContent.setText(content);    }    class MyClickableSpan extends ClickableSpan {        @Override        public void onClick(View widget) {            Toast.makeText(SpannableStringActivity.this, "我被点击了", Toast.LENGTH_SHORT).show();        }    }}
看下效果:

94d0a13e98965bea0ab84ff30dfb29d3.gif

简单说下,首先把要显示的内容转成SpannableString对象, 然后通过ForegroundColorSpan设置颜色, ClickableSpan设置点击事件,SpannableString通过调用setSpan方法 将颜色和点击事件应用到显示的内容中,setSpan方法需要传入设置格式 生效的起始坐标(比如颜色的起始坐标分别是7、8,那么就传入7,8+1), 最后注意一下Spanned.SPAN_EXCLUSIVE_EXCLUSIVE这个标志, 一共有四种flag可以选择,分别是:
  • Spanned.SPAN_INCLUSIVE_INCLUSIVE:前后都包括

  • Spanned.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括

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

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

这个flag是用来标识在Span范围内的文本,前后输入新的字符时是否也使用这个效果用的。一脸蒙圈,啥,你说的是啥?还是看图吧: 我们把flag设置为Spanned.SPAN_INCLUSIVE_EXCLUSIVE (前面包括,后面不包括)。

b55b975fb6ec0697fc3fc9b1fb333c3c.gif

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

实现

首先初始化一些数据

public class FillBlankView extends RelativeLayout {    private TextView tvContent;    private Context context;    // 答案集合    private List answerList;    // 答案范围集合    private List rangeList;    // 填空题内容    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);    }    ...}
定义一个设置数据的方法,供外部调用
/** * 设置数据 * * @param originContent   源数据 * @param answerRangeList 答案范围集合 */public void setData(String originContent, List answerRangeList) {    if (TextUtils.isEmpty(originContent) || answerRangeList == null            || answerRangeList.isEmpty()) {        return;    }    // 获取课文内容    content = new SpannableStringBuilder(originContent);    // 答案范围集合    rangeList = answerRangeList;    // 设置下划线颜色    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("");    }    // 设置填空处点击事件    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);    }    // 设置此方法后,点击事件才能生效    tvContent.setMovementMethod(LinkMovementMethod.getInstance());    tvContent.setText(content);}
代码中已经写了很全的注释,主要是设置填空处的颜色和点击事件。 点击事件
/** * 点击事件 */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));        // 获取焦点        popupWindow.setFocusable(true);        // 为了防止弹出菜单获取焦点之后,点击Activity的其他组件没有响应        popupWindow.setBackgroundDrawable(new PaintDrawable());        // 设置PopupWindow在软键盘的上方        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) {                // 填写答案                String answer = etInput.getText().toString();                fillAnswer(answer, position);                popupWindow.dismiss();            }        });        // 显示软键盘        InputMethodManager inputMethodManager =                (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);        inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);    }    @Override    public void updateDrawState(TextPaint ds) {        // 不显示下划线        ds.setUnderlineText(false);    }}
点击填空处弹出一个PopupWindow输入框,输入答案后点击确定, 调用fillAnswer方法将答案设置到填空处。

填写答案

前方高能,请减速慢行!
/** * 填写答案 * * @param answer   当前填空处答案 * @param position 填空位置 */private void fillAnswer(String answer, int position) {    answer = " " + answer + " ";    // 替换答案    AnswerRange range = rangeList.get(position);    content.replace(range.start, range.end, answer);    // 更新当前的答案范围    AnswerRange currentRange = new AnswerRange(range.start, range.start + answer.length());    rangeList.set(position, currentRange);    // 答案设置下划线    content.setSpan(new UnderlineSpan(),            currentRange.start, currentRange.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    // 将答案添加到集合中    answerList.set(position, answer.replace(" ", ""));    // 更新内容    tvContent.setText(content);    for (int i = 0; i < rangeList.size(); i++) {        if (i > position) {            // 获取下一个答案原来的范围            AnswerRange oldNextRange = rangeList.get(i);            int oldNextAmount = oldNextRange.end - oldNextRange.start;            // 计算新旧答案字数的差值            int difference = currentRange.end - range.end;            // 更新下一个答案的范围            AnswerRange nextRange = new AnswerRange(oldNextRange.start + difference,                    oldNextRange.start + difference + oldNextAmount);            rangeList.set(i, nextRange);        }    }}
首先把填空处的下划线或旧答案替换成新答案,然后更新一下当前的答案范围,由于下划线已经被答案替换了,所以需要为答案设置一条下划线,最后把答案更新到集合中,这样一个填空就完成了。 But,当一个填空处的答案范围改变后,后面所有的填空处答案范围都要跟着改变,所以还需要再更新一下后面填空处的答案范围。首先获取下一个答案原来的范围,计算一下需要向前或向后移动的距离,然后更新一下答案范围就大功告成了。 最后看下如何设置数据
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 = "纷纷扬扬的________下了半尺多厚。天地间________的一片。我顺着________工地走了四十多公里," +                "只听见各种机器的吼声,可是看不见人影,也看不见工点。一进灵官峡,我就心里发慌。";        // 答案范围集合        List 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 到这里就结束啦 往期精彩回顾:
  • Android实现短信验证码自动填充功能

  • Android仿echo精美弹幕功能

  • Android实现头像重叠排列功能

  • Android仿QQ个性标签功能

  • Android仿QQ侧滑删除的功能

c5dd269f16de38816ac829ce7a4f2f44.png

ec67d7ae5ce5d1cd0bc6724dba0bef06.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值