2018-11-26 仿抖音发布页@好友功能 点击删除键直接删除整个字段数据

写在前面:
最近心态有点暴

工程项目中,有个功能,发布功能,发布View可以选择@好友列表进行对应格式显示,直接上图


WX20181126-100308@2x.png

效果就是类似这个了。选择好友后,回调过来直接在页面显示对应格式、并且点击删除按钮,直接删除刚才的完整@好友的字段(注意,不是一个个文字删除)

好了。直接上源码:

 * Desc:@联系人 整块删除 不可编辑
 * <p>
 * Author: 
 * Date: 2018-09-13
 * Copyright: Copyright (c) 2010-2018
 * Company: 
 * Updater:
 * Update Time:
 * Update Comments:
 */
public class ContactEditText extends AppCompatAutoCompleteTextView {
    private int itemPadding;
    private String content = "";

    public ContactEditText(Context context) {
        super(context);
        init();
    }


    public ContactEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ContactEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }



    private void init() {
        itemPadding = dip2px(getContext(), 3);
    }


    @Override
    protected void onSelectionChanged(int selStart, int selEnd) {
        MyImageSpan[] spans = getText().getSpans(0, getText().length(), MyImageSpan.class);
        for (MyImageSpan myImageSpan : spans) {
            if (getText().getSpanEnd(myImageSpan) - 1 == selStart) {
                selStart = selStart + 1;
                setSelection(selStart);
                break;
            }
        }
        super.onSelectionChanged(selStart, selEnd);
    }

    private void flushSpans() {
        Editable editText = getText();
        Spannable spannableString = new SpannableString(editText);
        MyImageSpan[] spans = spannableString.getSpans(0, editText.length(), MyImageSpan.class);
        List<UnSpanText> texts = getAllTexts(spans, editText);
        for (UnSpanText unSpanText : texts) {
            if (!TextUtils.isEmpty(unSpanText.showText.toString().trim())) {
                generateOneSpan(spannableString, unSpanText);
            }
        }
        setText(spannableString);
        setSelection(spannableString.length());
    }

    private List<UnSpanText> getAllTexts(MyImageSpan[] spans, Editable edittext) {
        List<UnSpanText> texts = new ArrayList<>();
        int start;
        int end;
        CharSequence text;
        List<Integer> sortStartEnds = new ArrayList<>();
        sortStartEnds.add(0);
        for (MyImageSpan myImageSpan : spans) {
            sortStartEnds.add(edittext.getSpanStart(myImageSpan));
            sortStartEnds.add(edittext.getSpanEnd(myImageSpan));
        }
        sortStartEnds.add(edittext.length());
        Collections.sort(sortStartEnds);
        for (int i = 0; i < sortStartEnds.size(); i = i + 2) {
            start = sortStartEnds.get(i);
            end = sortStartEnds.get(i + 1);
            text = edittext.subSequence(start, end);
            if (!TextUtils.isEmpty(text)) {
                // TODO: 2018/9/13 这里的ID为测试id 
                texts.add(new UnSpanText(start, end, text, "-1"));
            }
        }

        return texts;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
//            flushSpans();//文字转换成图片块 这里是自动联想的功能,可根据需求进行添加
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void performFiltering(CharSequence text, int keyCode) {
        Log.d("dds", "performFiltering" + text + ":" + keyCode);
        super.performFiltering(text, keyCode);
    }

    //添加一个Span
    public void addSpan(PatRecordUserBean userBean) {
        String showText = userBean.getUserName();
        String showId = userBean.getUserId();
        getText().append(showText);
        SpannableString spannableString = new SpannableString(getText());
        generateOneSpan(spannableString, new UnSpanText(spannableString.length() - showText.length(), spannableString.length(), showText, showId));
        setText(spannableString);
        setSelection(spannableString.length());
    }

    public void insertSpan(PatRecordUserBean userBean) {//根据位置插入
        String showText = userBean.getUserName();
        String showId = userBean.getUserId();
        Editable editable = getText();
        int start = getSelectionStart();
        int end = start + showText.length();
        editable.insert(start, showText);
        SpannableString spannableString = new SpannableString(getText());
        generateOneSpan(spannableString, new UnSpanText(start, end, showText, showId));
        setText(spannableString);
        setSelection(end);
    }

    public void insertText(String insertData) {//插入文案信息
        if (null != insertData) {
            CharSequence charSequence = insertData;
            Editable editable = getText();
            int start = getSelectionStart();
            int end = start + charSequence.length();
            editable.insert(start, charSequence);
        }
    }

    private void generateOneSpan(Spannable spannableString, UnSpanText unSpanText) {
    //文字转图片
        View spanView = getSpanView(getContext(), unSpanText.showText.toString(), getMeasuredWidth());
        BitmapDrawable bitmapDrawable = (BitmapDrawable) convertViewToDrawable(spanView);
        bitmapDrawable.setBounds(0, 0, bitmapDrawable.getIntrinsicWidth(), bitmapDrawable.getIntrinsicHeight());
        MyImageSpan what = new MyImageSpan(bitmapDrawable, unSpanText.showText.toString(), unSpanText.showId);
        final int start = unSpanText.start;
        final int end = unSpanText.end;
        spannableString.setSpan(what, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    public Drawable convertViewToDrawable(View view) {
        int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        view.measure(spec, spec);
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        c.translate(-view.getScrollX(), -view.getScrollY());
        view.draw(c);
        view.setDrawingCacheEnabled(true);
        Bitmap cacheBmp = view.getDrawingCache();
        Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
        cacheBmp.recycle();
        view.destroyDrawingCache();
        return new BitmapDrawable(viewBmp);
    }

    public View getSpanView(Context context, String text, int maxWidth) {
        TextView view = new TextView(context);
//        view.setMaxWidth(maxWidth);
//        view.setText(text);
//        view.setEllipsize(TextUtils.TruncateAt.END);
//        view.setSingleLine(true);

        view.setText(text);
        //设置文字框背景色
//        view.setBackgroundResource(R.drawable.shape_corner_rectangle);
        //view.setBackgroundResource(R.drawable.shape_corner_rectangle);
        view.setTextSize(getTextSize());
        //设置文字颜色
        //view.setTextColor(getCurrentTextColor());
        view.setTextColor(Color.WHITE);
        FrameLayout frameLayout = new FrameLayout(context);
        frameLayout.setPadding(itemPadding, itemPadding, itemPadding, itemPadding);
        frameLayout.addView(view);
        return frameLayout;
    }

    private class UnSpanText implements Serializable {
        int start;
        int end;
        CharSequence showText;
        String showId;

        UnSpanText(int start, int end, CharSequence showText, String showId) {
            this.start = start;
            this.end = end;
            this.showText = showText;
            this.showId = showId;
        }
    }

    public class MyImageSpan extends ImageSpan implements Serializable {
        private String showText;


        public String getShowId() {
            return showId;
        }

        public void setShowId(String showId) {
            this.showId = showId;
        }

        private String showId;

        public MyImageSpan(Drawable d, String showText, String showId) {
            super(d);
            this.showText = showText;
            this.showId = showId;
        }

        public String getShowText() {
            return showText;
        }
    }

    /**
     * dip转换px
     */
    public static int dip2px(Context context, int dip) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dip * scale + 0.5f);
    }


    public void resumeSpanText(String oldString, ArrayList<PatRecordUserBean> str) {//恢复草稿标题数据
        int contentStart = 0;
        int contentEnd;
        int tmpCutStart;
        int tmpCutEnd;
        if (str.size() == 0) {
            insertText(oldString);
            return;
        }

        for (int i = 0; i < str.size(); i++) {
            Matcher matcher = matchStringByRegularExpression(oldString, str.get(i).getUserName());
            if (matcher != null) {
                int start = matcher.start();
                int end = matcher.end();
                contentEnd = start;//裁剪的结尾
                content = oldString.substring(contentStart, contentEnd);
                contentStart = end;
                Log.d("数据", ">>>>" + start + ">>>" + end + ">>>" + content);
                if (!TextUtils.isEmpty(content)) {
                    insertText(content);
                }
                addSpan(str.get(i));
            }
        }
        if (getText().length() < oldString.length()) {
            String substring = oldString.substring(getText().length(), oldString.length());
            if (!TextUtils.isEmpty(substring)) {
                insertText(substring);
            }
        }

    }

    public static Matcher matchStringByRegularExpression(String parent, String child) {
        Pattern p = Pattern.compile(child);
        Matcher m = p.matcher(parent);
        while (m.find()) {
            return m;
        }
        return null;
    }

    public ArrayList<PatRecordUserBean> getFinalSpan() {//获取最终的@好友的用户Bean数据

        ArrayList<PatRecordUserBean> patRecordUserBeans = new ArrayList<>();
        Editable editText = getText();//这里不能使用toString()。否则格式会被重置。
        Spannable spannableString = new SpannableString(editText);
        MyImageSpan[] spans = spannableString.getSpans(0, editText.length(), MyImageSpan.class);
        if (spans == null || spans.length == 0) {
            return patRecordUserBeans;
        } else {
            for (int i = 0; i < spans.length; i++) {
                PatRecordUserBean bean = new PatRecordUserBean();
                bean.setUserId(spans[i].getShowId());
                bean.setUserName(spans[i].getShowText());
                patRecordUserBeans.add(bean);
            }
            return patRecordUserBeans;
        }

    }
}

实体类

/**
 * Desc:@好友功能实体类
 * <p>
 * Author: 
 * Date: 2018-09-13
 * Copyright: Copyright (c) 2010-2018
 * Company: 
 * Updater:
 * Update Time:
 * Update Comments:
 */
public class PatRecordUserBean implements Serializable {
    private String userId;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    private String userName;
}

知道你们不喜欢看源码,直接看用法吧
1.当前页面有保存草稿功能,如果需要恢复上一次的草稿数据,直接调用

 ArrayList<PatRecordUserBean> oldUserBean = draftPatRecordParamJson.getUserSpan();
     if (oldUserBean != null) {
           editTitle.resumeSpanText(videoTitle, oldUserBean);
         } 

2.设置监听、我这里是最大只能60个字符

editTitle.addTextChangedListener(new TextWatcher() {
            private CharSequence temp;
            private int starts, end;
            private int deleteIndex;

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int lengthBefore, int lengthAfter) {
                temp = s;
                if (lengthAfter == 1 && "@".equals(String.valueOf(s.charAt(start))) && s.length() <= 60) {
                   //这里可以直接跳转选择好友的页面
                    editTitle.getText().delete(s.length() - 1, s.length());
                } else if (lengthBefore == 1 && lengthAfter == 0) {  //向前删除一个字符,@后的内容必须大于一个字符,可以在后面加一个空格
                }
            }

            @Override
            public void afterTextChanged(Editable s) {//变化
                if (temp.length() > 60) {
                    ToastUtils.showShort(R.string.patrecord_public_title_word_count);
                    s.delete(editTitle.getSelectionStart() - 1, editTitle.getSelectionStart());
                }
            }
        });

3.选择好友后、onActivityForResult进行数据显示

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PatRecordConstant.PatRecordParamsType.PAT_COVER) {
          if (resultCode == PatRecordConstant.PatRecordParamsType.PAT_FRIEND_CODE) {
                friendName = "@" + data.getStringExtra(PatRecordConstant.PatRecordParamsType.Pat_PUBLIC_FRIEND_NAME) + "";
                friendId = data.getStringExtra(PatRecordConstant.PatRecordParamsType.Pat_PUBLIC_FRIEND_NAME_ID) + "";
                ArrayList<PatRecordUserBean> finalSpan = editTitle.getFinalSpan();
                for (int i = 0; i < finalSpan.size(); i++) {
                    PatRecordUserBean patRecordUserBean = finalSpan.get(i);
                    String userId = patRecordUserBean.getUserId();
                    if (!TextUtils.isEmpty(friendId) && userId.equals(friendId)) {
                        return;
                    }
                }
                if (editTitle.getText().length() + friendName.length() <= 60) {
                    PatRecordUserBean userBean = new PatRecordUserBean();
                    userBean.setUserName(friendName);
                    userBean.setUserId(friendId);
                    editTitle.insertSpan(userBean);//插入数据(@好友的数据)
               
                } else {
                    ToastUtils.showShort(R.string.patrecord_public_title_word_count);
                }
            } 
        }
    }

好了,我发现大家都一样,一般网上找解决方案。只要结果。不要过程的,其实我也一样。
但是总要学习,总要进步,毕竟家里没矿~~

有问题留言沟通,并且点个喜欢,点个收藏之类的,项目中有参考了其他同志的项目,因为写的有点久,这里我也找不出项目地址了。有介意的同志联系我

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 13,082评论 2 59
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 174,020评论 25 709
  • 下午在朋友的茶叶店里小坐了一会儿。聊聊夏季茶叶的贮存,聊聊政府强拆西瓜大棚的事儿,最后聊到了各自的孩子。朋友...
    雨霏心理阅读 406评论 0 1
  • 《一日常规》十二、教室必静1、 离开座位前,须将座椅放入桌下。不可随起随走,不可座椅歪斜不正。2、 不可乱扔纸屑垃...
    缘孟阅读 718评论 0 0
  • 《毕业十年 : 为‘理想’画一个圈》 变形金刚都拍到第五部了,回想起观看第一部时那令人叹为观止的特...
    王泽er阅读 305评论 0 0