Android uses code to implement a fill-in question


cover

GitHub Portal

1. Write before

Recently, the project is busy. I haven't updated my blog for more than a month. Use my spare time to summarize the problems encountered in the project and share them with you!

When I first saw the need to fill in the blanks, the first response was to go to Baidu, ah... No, Google search for similar Demo, but all of them were Android interview questions. Alas, let's do it honestly, first see the effect:


Completion

2. Learn some basics

Let's start by learning how to locally set colors and click events for TextView, using an important class, 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 = "You see I can not only change color, but also click.";
        SpannableString content = new SpannableString(originContent);

        // Set Colors
        ForegroundColorSpan colorSpan = new ForegroundColorSpan(Color.parseColor("#4DB6AC"));
        content.setSpan(colorSpan, 7, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        // Set Click Events
        MyClickableSpan myClickableSpan = new MyClickableSpan();
        content.setSpan(myClickableSpan, 12, 14, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        // Click events will not take effect until this method is set
        tvContent.setMovementMethod(LinkMovementMethod.getInstance());

        tvContent.setText(content);
    }

    class MyClickableSpan extends ClickableSpan {

        @Override
        public void onClick(View widget) {
            Toast.makeText(SpannableStringActivity.this, "I was clicked on", Toast.LENGTH_SHORT).show();
        }
    }
}

See the effect:


SpannableString

Simply put, first convert the content you want to display into a SpannableString object, then set the color through ForegroundColorSpace, ClickableSpan set the click event, SpannableString applies the color and click event to the content you display by calling the setSpan method. The setSpan method needs to pass in the starting coordinates from which the formatting takes effect (for example, the starting coordinates of the color are 7, 8, respectively).Then pass in 7,8+1), and finally notice the flag Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, which has four flags to choose from:

  • Spanned.SPAN_INCLUSIVE_INCLUSIVE: both before and after include

  • Spanned.SPAN_EXCLUSIVE_EXCLUSIVE: Neither before nor after

  • Spanned.SPAN_INCLUSIVE_EXCLUSIVE: Included before, excluded after

  • Spanned.SPAN_EXCLUSIVE_INCLUSIVE: not included before, but included after

This flag is used to identify the text within the Span range and whether it is also used when entering new characters back and forth.Masked face, what, what did you say?Let's look at the picture:

We set flag to Spanned.SPAN_INCLUSIVE_EXCLUSIVE (both before and after).


Spanned.SPAN_INCLUSIVE_EXCLUSIVE

Is it much clearer, if you still can't read the picture, walk slowly without sending it!?

3. Implementation

Initialize some data first

public class FillBlankView extends RelativeLayout {

    private TextView tvContent;
    private Context context;
    // Answer Set
    private List<String> answerList;
    // Answer Range Collection
    private List<AnswerRange> rangeList;
    // Fill in the blanks
    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);
    }

    ...
}

Define a method to set data for external calls

/**
 * Set up data
 *
 * @param originContent   source data
 * @param answerRangeList Answer Range Collection
 */
public void setData(String originContent, List<AnswerRange> answerRangeList) {
    if (TextUtils.isEmpty(originContent) || answerRangeList == null
            || answerRangeList.isEmpty()) {
        return;
    }

    // Get Text Content
    content = new SpannableStringBuilder(originContent);
    // Answer Range Collection
    rangeList = answerRangeList;

    // Set underline color
    for (AnswerRange range : rangeList) {
        ForegroundColorSpan colorSpan = new ForegroundColorSpan(Color.parseColor("#4DB6AC"));
        content.setSpan(colorSpan, range.start, range.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    // Answer Set
    answerList = new ArrayList<>();
    for (int i = 0; i < rangeList.size(); i++) {
        answerList.add("");
    }

    // Set Fill-in Click Event
    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);
    }

    // Click events will not take effect until this method is set
    tvContent.setMovementMethod(LinkMovementMethod.getInstance());
    tvContent.setText(content);
}

There are full comments in the code, mainly setting the color of the filling space and clicking events.

Click Event

/**
 * Click Event
 */
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);

        // Show original answers
        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));
        // Get Focus
        popupWindow.setFocusable(true);
        // To prevent the pop-up menu from gaining focus, the other components that click on the Activity do not respond
        popupWindow.setBackgroundDrawable(new PaintDrawable());
        // Set PopupWindow on top of the soft keyboard
        popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        // PopupWindow Popup
        popupWindow.showAtLocation(tvContent, Gravity.BOTTOM, 0, 0);

        btnFillBlank.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // Fill in the answer
                String answer = etInput.getText().toString();
                fillAnswer(answer, position);
                popupWindow.dismiss();
            }
        });

        // Show Soft Keyboard
        InputMethodManager inputMethodManager =
                (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        // Do not show underline
        ds.setUnderlineText(false);
    }
}

Click on the fill-in to pop up a PopupWindow input box, type the answer, click OK, and call the fillAnswer method to set the answer to the fill-in.

Fill in the answer

High energy ahead, slow down!

/**
 * Fill in the answer
 *
 * @param answer   Current Fill-in Answer
 * @param position Fill-in position
 */
private void fillAnswer(String answer, int position) {
    answer = " " + answer + " ";

    // Replace Answer
    AnswerRange range = rangeList.get(position);
    content.replace(range.start, range.end, answer);

    // Update the current answer range
    AnswerRange currentRange = new AnswerRange(range.start, range.start + answer.length());
    rangeList.set(position, currentRange);

    // Underline Answer Settings
    content.setSpan(new UnderlineSpan(),
            currentRange.start, currentRange.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

    // Add the answer to the set
    answerList.set(position, answer.replace(" ", ""));

    // Update Content
    tvContent.setText(content);

    for (int i = 0; i < rangeList.size(); i++) {
        if (i > position) {
            // Get the original range for the next answer
            AnswerRange oldNextRange = rangeList.get(i);
            int oldNextAmount = oldNextRange.end - oldNextRange.start;
            // Calculate the difference between old and new answer words
            int difference = currentRange.end - range.end;

            // Update the scope of the next answer
            AnswerRange nextRange = new AnswerRange(oldNextRange.start + difference,
                    oldNextRange.start + difference + oldNextAmount);
            rangeList.set(i, nextRange);
        }
    }
}

First replace the underline in the blank or the old answer with the new answer, then update the current answer range. Since the underline has been replaced by the answer, you need to underline the answer. Finally, update the answer to the set, and this filling is complete.But, when the answer range in one blank changes, the answer range in all subsequent blanks changes, so you need to update the answer range in the subsequent blanks.First get the original range of the next answer, calculate the distance you need to move forward or backward, and then update the range.

4. Write at the end

The source code is hosted on GitHub. Welcome Fork. Start now if you think it's good.

GitHub Portal

Welcome to your classmates'comments. If you think this blog is useful for you, leave a message or click on your favorite blog.

Tomorrow is National Day. Happy National Day to all of you!

In the next article, we will learn how to achieve a drag-and-drop fill-in-the-blank topic (word filling-in-the-blank). Please look forward to it!

Keywords: github Google Android ButterKnife

Added by Blissey on Mon, 20 May 2019 19:12:14 +0300