700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > android 编辑标签 在一个文本框输入标签回车添加退格删除

android 编辑标签 在一个文本框输入标签回车添加退格删除

时间:2020-12-23 19:53:26

相关推荐

android 编辑标签 在一个文本框输入标签回车添加退格删除

先上图:

最近产品需要一个编辑标签的页面如图所示。

需要实现可以输入标签,按回车键或逗号生成标签。并且可以移动光标删除标签,和插入标签。

并且单个标签有字符限制(英文算一个中文算两个字符)

直接上代码

activity的代码

package com.fan.tagtest;import android.content.Context;import android.graphics.Bitmap;import android.os.Bundle;import android.support.annotation.NonNull;import android.support.v7.app.AppCompatActivity;import android.text.Editable;import android.text.InputFilter;import android.text.Selection;import android.text.Spannable;import android.text.SpannableString;import android.text.SpannableStringBuilder;import android.text.Spanned;import android.text.TextPaint;import android.text.TextWatcher;import android.text.style.ImageSpan;import android.util.TypedValue;import android.view.KeyEvent;import android.view.View;import android.view.inputmethod.InputMethodManager;import android.widget.EditText;import android.widget.FrameLayout;import android.widget.TextView;import java.util.ArrayList;import java.util.Arrays;import parator;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;public class MainActivity extends AppCompatActivity {List<String> source = new ArrayList<>();private static final int maxLength = 8;private static final boolean isChinese2English = true;public static void setEditTextInhibitInputSpeChat(EditText editText) {InputFilter filter = new InputFilter() {@Overridepublic CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {String chars = "\r\n\t ";String speChat = "[" + chars + "]";Pattern pattern = pile(speChat);Matcher matcher = pattern.matcher(source.toString());if (matcher.find()) {String str = source.toString();char[] charArr = toCharArray(chars);for (char c : charArr) {str = str.replaceAll(new String(new char[]{c}), "");}return str;} else return null;}};InputFilter emojiFilter = new InputFilter() {@Overridepublic CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {for (int index = start; index < end; index++) {int type = Character.getType(source.charAt(index));if (type == Character.SURROGATE) {return "";}}return null;}};editText.setFilters(new InputFilter[]{filter, emojiFilter});}@NonNullprivate static MyImageSpanImage[] getSortedImageSpans(final Editable text) {MyImageSpanImage[] spans = text.getSpans(0, text.length(), MyImageSpanImage.class);Arrays.sort(spans, new Comparator<MyImageSpanImage>() {@Overridepublic int compare(MyImageSpanImage o1, MyImageSpanImage o2) {int start1 = text.getSpanStart(o1);int start2 = text.getSpanStart(o2);if (start1 > start2) {return 1;} else if (start1 < start2) {return -1;}return 0;}});return spans;}EditText etTags;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setTitle("编辑标签");setContentView(R.layout.activity_main);etTags = (EditText) findViewById(R.id.et_tags);findViewById(R.id.ll).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {setSoftInputVis(etTags, true);}});findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ok();}});initHint();init();etTags.setOnKeyListener(new View.OnKeyListener() {@Overridepublic boolean onKey(View v, int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_COMMA) {initTags();return true;}return false;}});etTags.addTextChangedListener(new TextWatcher() {int start;int count;int before;@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {this.start = start;this.before = before;this.count = count;}@Overridepublic void afterTextChanged(Editable editable) {if (count <= 0) {return;}etTags.removeTextChangedListener(this);if (Selection.getSelectionEnd(editable) != start + count) {//如果光标位置和最新变化的结尾不相等说明不是正常输入的 全部作废处理editable.replace(start, start + count, "");} else {onChange(editable);}etTags.addTextChangedListener(this);}private void onChange(Editable editable) {String changeString = editable.subSequence(start, start + count).toString();int sumOfComma = removeAllComma(editable);count -= sumOfComma;count -= delIfOverMax();if (sumOfComma > 0) {initTags();return;}if (count == 0) {return;}setTextSpan(editable);}private void setTextSpan(Editable editable) {CharSequence string = editable.subSequence(start, start + count);char[] chars = toCharArray(string);int i = 0;for (char c : chars) {editable.setSpan(new MyImageSpanText(MainActivity.this, getImage(new String(new char[]{c}), false)), start + i, start + i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);i++;}}private int removeAllComma(Editable editable) {int sum = 0;while (editable.toString().contains(",")) {//删除所有逗号int selEndIndex = Selection.getSelectionEnd(editable);int i = editable.toString().indexOf(",");editable.replace(i, i + 1, "");Selection.setSelection(editable, selEndIndex - (selEndIndex >= i ? 1 : 0));sum++;}return sum;}});setEditTextInhibitInputSpeChat(etTags);}private int delIfOverMax() {final Editable text = etTags.getText();int selEndIndex = Selection.getSelectionEnd(text);int lastEnd = 0;MyImageSpanImage[] spans = getSortedImageSpans(text);for (MyImageSpanImage span : spans) {int start = text.getSpanStart(span);int length = init(text, selEndIndex, lastEnd, start);if (length > 0)return length;int end = text.getSpanEnd(span);lastEnd = Math.max(end, lastEnd);}int length = text.length();length = init(text, selEndIndex, lastEnd, length);if (length > 0)return length;return 0;}private int init(Editable text, int selectedIndex, int start, int end) {if (start >= end) {return 0;}if (selectedIndex >= start && selectedIndex <= end) {//因为只会正常输入不会异常插入所以可以用光标位置判断某块是否超长String blockString = text.subSequence(start, end).toString();int length = calculateLength(blockString);if (length > maxLength) {//光标右边的字符串String rightString = text.subSequence(selectedIndex, end).toString();//光标左边的字符串String leftString = text.subSequence(start, selectedIndex).toString();//光标右边的字符串英文长度int rightLength = calculateLength(rightString);//光标左边的字符串英文长度int leftLength = calculateLength(leftString);//char[] leftStringChars = toCharArray(leftString);int okIndex = selectedIndex;int charSum = 0;int leaveLength = maxLength - rightLength;for (int i = leftStringChars.length - 1; i >= 0; i--) {char c = leftStringChars[i];if ((c & 0xffff) <= 0xff) {charSum += 1;okIndex--;} else {charSum += 2;okIndex--;}int nowLeaveLength = leftLength - charSum;if (nowLeaveLength <= leaveLength) {break;}}text.replace(okIndex, selectedIndex, "");return selectedIndex - okIndex;}} else {setImageSpan(text, start, end);}return 0;}private void initHint() {String string = "多个标签用逗号或回车分割";SpannableString text = new SpannableString(string);Bitmap image = getImage(string, true);text.setSpan(new MyImageSpanText(this, image), 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);etTags.setHint(text);}private void initTags() {final Editable text = etTags.getText();int lastEnd = 0;MyImageSpanImage[] spans = getSortedImageSpans(text);for (MyImageSpanImage span : spans) {int start = text.getSpanStart(span);int end = text.getSpanEnd(span);if (lastEnd < start) {//如果lastEnd 不等于 起始位置 中间的一段就是普通字符串setImageSpan(text, lastEnd, start);}lastEnd = Math.max(end, lastEnd);}if (lastEnd != text.length()) {setImageSpan(text, lastEnd, text.length());}}private static char[] toCharArray(CharSequence str) {if (str instanceof SpannableStringBuilder) {str.length();}char[] charArray = new char[str.length()];for (int i = 0; i < str.length(); i++) {charArray[i] = str.charAt(i);}return charArray;}private void setImageSpan(Editable text, int start, int end) {Bitmap tagImage = getTagImage(text.subSequence(start, end).toString());for (MyImageSpanText span2 : text.getSpans(0, etTags.length(), MyImageSpanText.class)) {text.removeSpan(span2);}text.setSpan(new MyImageSpanImage(this, tagImage), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);}private void init() {StringBuilder sb = new StringBuilder();for (String str : source) {str = str.trim().replaceAll(",", "").replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", "");if (str.length() == 0) {continue;}sb.append(str);}etTags.setText(sb.toString());etTags.setSelection(etTags.length());int length = 0;Editable text = etTags.getText();for (String str : source) {str = str.trim().replaceAll(",", "").replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", "");if (str.length() == 0) {continue;}int strLength = str.length();Bitmap tagImage = getTagImage(str);text.setSpan(new MyImageSpanImage(this, tagImage), length, strLength + length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);length += strLength;}}private void ok() {initTags();processImageSpan();}private void processImageSpan() {final Editable text = etTags.getText();MyImageSpanImage[] spans = getSortedImageSpans(text);source.clear();int lastEnd = 0;for (MyImageSpanImage span : spans) {int start = text.getSpanStart(span);int end = text.getSpanEnd(span);if (lastEnd == end || start == end) {lastEnd = end;continue;}lastEnd = end;source.add(text.toString().substring(start, end));}//结果this.source = source;finish();}private Bitmap getImage(String string, boolean isHint) {if (string == null) {return null;}FrameLayout fl = new FrameLayout(this);fl.setPadding(0, getPx(6), 0, getPx(6));TextView tv = new TextView(this);tv.setMaxLines(1);tv.setLines(1);tv.setTextSize(PLEX_UNIT_DIP, 14);tv.setText(string);tv.setTextColor(isHint ? 0xff888888 : 0xff444444);fl.addView(tv);return getBitmapViewByMeasure(fl, (int) getTextViewLength(tv, string), getPx(32));}// 计算出该TextView中文字的长度(像素)public static float getTextViewLength(TextView textView, String text) {if (text == null) {return 0;}TextPaint paint = textView.getPaint();// 得到使用该paint写上text的时候,像素为多少float textLength = paint.measureText(text);return textLength;}private Bitmap getTagImage(String string) {if (string == null) {return null;}string = string.replaceAll(",", "");if (string.length() == 0) {return null;}FrameLayout fl = new FrameLayout(this);fl.setPadding(getPx(4), getPx(2), getPx(4), getPx(2));TextView tv = new TextView(this);tv.setPadding(getPx(4), getPx(4), getPx(4), getPx(4));tv.setMaxLines(1);tv.setLines(1);tv.setTextSize(PLEX_UNIT_DIP, 14);tv.setText(string);tv.setTextColor(0xffffffff);tv.setBackgroundResource(R.drawable.shape_edit_v1_tag);fl.addView(tv);return getBitmapViewByMeasure(fl, (int) getTextViewLength(tv, string) + getPx(16), getPx(32));}public static Bitmap getBitmapViewByMeasure(View view, int width, int height) {//打开图像缓存view.setDrawingCacheEnabled(true);//必须调用measure和layout方法才能成功保存可视组件的截图到png图像文件//测量View大小if (height <= 0) {view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));} else if (height > 0) {view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));}//发送位置和尺寸到View及其所有的子Viewview.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());Bitmap bitmap = null;try {//获得可视组件的截图bitmap = view.getDrawingCache();} catch (Exception e) {e.printStackTrace();}return bitmap;}private static class MyImageSpanText extends ImageSpan {public MyImageSpanText(Context context, Bitmap b) {super(context, b);}}private static class MyImageSpanImage extends ImageSpan {public MyImageSpanImage(Context context, Bitmap b) {super(context, b);}}public void setSoftInputVis(View view, boolean vis) {InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);if (vis) {imm.showSoftInput(view, InputMethodManager.SHOW_FORCED);} else {imm.hideSoftInputFromWindow(view.getWindowToken(), 0);}}public int getPx(int dp) {return (int) (getResources().getDisplayMetrics().density * dp);}public int calculateLength(CharSequence c) {if (!isChinese2English) {return c.length();}double len = 0;for (int i = 0; i < c.length(); i++) {char cc = c.charAt(i);if ((cc & 0xffff) <= 0xff) {len += 0.5;} else {len++;}}len = len * 2;return (int) Math.round(len);}}

layout的代码

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:id="@+id/ll"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"android:text="完成" /><EditTextandroid:id="@+id/et_tags"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:background="#0000"android:inputType="textMultiLine"android:textSize="14sp" /></LinearLayout>

标签的背景图片

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="/apk/res/android"android:shape="rectangle"><corners android:radius="3dp" /><solid android:color="#ffd800" /></shape>

各位雅正!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。