仿微信@好友功能 輸入@跳转、删除整块

最近在做聊天功能的时候,有一个需求是仿照微信做@好友的功能,本来以为挺简单,但是做到这块的时候,发现和想象的有点不一样,什么整块删除,块可编辑,总之,加个@的功能很简单,但是要做和微信的一样还是费了一些功夫,下面是一个demo仅供参考,防止遗忘

先上个效果图

这里写图片描述

就是这么个功能

我又照着做了这个,仅供参考

这里写图片描述

1. 分析需求

  • 输入@跳转到联系人界面,选中一个或者多个好友返回到当前界面
  • 按退格键删除整块内容
  • 块内的内容可编辑,编辑完了之后将不附带@功能,只是单纯的文字

2. 开始编码

既然是文本输入首先继承EditText自定义一个控件

public class MsgEditText extends AppCompatEditText {


    public MsgEditText(Context context) {
        super(context);
    }

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

    public MsgEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }
}

到底从哪里开始入手呢,首先完成变成块的需求,
无意中看到这个项目https://github.com/g707175425/CloudEditText ,他是这么写的

 private void generateOneSpan(Spannable spannableString, UnSpanText unSpanText) {
        //生成一个TextView
        View spanView = getSpanView(getContext(),      unSpanText.showText.toString(), getMeasuredWidth());
        //再将TextView转换为一个图片
        BitmapDrawable bitmpaDrawable = (BitmapDrawable) UIUtils.convertViewToDrawable(spanView);
        bitmpaDrawable.setBounds(0, 0, bitmpaDrawable.getIntrinsicWidth(), bitmpaDrawable.getIntrinsicHeight());
        //最后将这个图片放到Span里,
        MyImageSpan what = new MyImageSpan(bitmpaDrawable, unSpanText.showText.toString(),unSpanText.returnText);
        final int start = unSpanText.start;
        final int end = unSpanText.end;
        spannableString.setSpan(what, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        //设置一个Span
        spannableString.setSpan(touchableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

看到这里我们就记得了一个关于SpanableString的用法,它可以设置图片,可以随意的设置文字的背景的前景,等等一系列比较酷炫的效果,而且只需要一个TextView,如果需要深入了解Span,可自行百度和Google,我看的是这篇博客http://blog.csdn.net/u012422440/article/details/52155924,关于Span的进阶用法,于是就有了下面的实现

    //这个是需要成块删除的内容
    private class MyTextSpan extends MetricAffectingSpan {
        private String showText;
        private long userId;

        //userId是为了适应需求,如果不需要请自行去掉
        public MyTextSpan(String showText, long userId) {
            this.showText = showText;
            this.userId = userId;
        }


        public String getShowText() {
            return showText;
        }

        public long getUserId() {
            return userId;
        }

        @Override
        public void updateMeasureState(TextPaint p) {

        }

        @Override
        public void updateDrawState(TextPaint tp) {

        }
    }

    //这个是非整块删除的内容,当然你如果想也是可以删除的
    private class UnSpanText {
        int start;
        int end;
        String returnText;

        UnSpanText(int start, int end, String returnText) {
            this.start = start;
            this.end = end;
            this.returnText = returnText;
        }
    }

刚开始我是这么写的

    //外部调用一个增加Span的方法
    public void addSpan(String showText, String returnText, long userId) {
        getText().append(showText);
        SpannableString spannableString = new SpannableString(getText());
        makeSpan(spannableString, new UnSpanText(spannableString.length() - showText.length(), spannableString.length(), showText, returnText), userId);
        setText(spannableString);
        setSelection(spannableString.length());
    }


    //生成一个需要整体删除的Span
    private void makeSpan(Spannable sps, UnSpanText unSpanText, long userId) {
        MyTextSpan what = new MyTextSpan(unSpanText.returnText, userId);
        int start = unSpanText.start;
        int end = unSpanText.end;
        sps.setSpan(what, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

写到现在这个整块添加已经做好了,下面开始做整块删除,刚开始的时候我是模仿上面的CloudEditText写的,但我发现好像会用各种问题,于是想了一种方法

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        //向前删除一个字符,@后的内容必须大于一个字符,可以在后面加一个空格
        if (lengthBefore == 1 && lengthAfter == 0) {
            MyTextSpan[] spans = getText().getSpans(0, getText().length(), MyTextSpan.class);
            for (MyTextSpan myImageSpan : spans) {
                if (getText().getSpanEnd(myImageSpan) == start && !text.toString().endsWith(myImageSpan.getShowText())) {
                    getText().delete(getText().getSpanStart(myImageSpan), getText().getSpanEnd(myImageSpan));
                    break;
                }
            }
        }

    }

上面的意思就是,如果你在EditText中执行删除一个字符的时候,判断前面一个是否是一个Span,如果是自定义的Span就把Span一同删除,关于这个,我可是测试可各种操作才定为这样的

最后是获取需要@的人员名单

    //获取用户Id列表,这只是个参考,可根据需求修改
    public String getUserIdString() {
        MyTextSpan[] spans = getText().getSpans(0, getText().length(), MyTextSpan.class);
        StringBuilder builder = new StringBuilder();
        for (MyTextSpan myTextSpan : spans) {
            String realText = getText().toString().substring(getText().getSpanStart(myTextSpan), getText().getSpanEnd(myTextSpan));
            String showText = myTextSpan.getShowText();
            if (realText.equals(showText)) {
                builder.append(myTextSpan.getUserId()).append(",");
            }
        }
        if (!TextUtils.isEmpty(builder.toString())) {
            builder.deleteCharAt(builder.length() - 1);
        }
        return builder.toString();
    }
最后我就大方的放个地址你们自己看吧

https://github.com/ddssingsong/AtFriend

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ddssingsong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值