SpannableString文本的 格式化 保存,显示,事件处理

关于 spannableStrting 基本的一些用法效果在这里
http://blog.csdn.net/kongbaidepao/article/details/12087197

一、对 edittext 或 textview 中的 特殊显示效果字符进行保存 与显示

1.基本抽象方法定义基本功能

public abstract interface IMediaHandler {
//处理联系人
    public abstract void handleContact(SpannableStringBuilder builder, int start, int end,
            String name,
            String phone);
//处理图片
    public abstract void handleImage(SpannableStringBuilder builder, int start, int end, String name);
//处理删除线
    public abstract void handleStrike(SpannableStringBuilder builder, int start, int end);

}

2.对要显示的数据进行处理

public class MediaHandler implements IMediaHandler {

        public void handleContact(SpannableStringBuilder builder, int start, int end, String name,
                String phone) {

            //得到 ContactImage
            Bitmap localBitmap = RichEditView.this.getContactImage(name);

            //这里设置 联系人的 TouchableSpan
            TouchableContactSpan span = new TouchableContactSpan(mContext, localBitmap,
                    RichEditView.this, name,
                    phone);
            //首先替换该处联系人的字符
            String objStr = "\uFFFC";
            builder.replace(start, end, objStr);
            //再替换成联系人图片(span)
            builder.setSpan(span, start, start + objStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        }

        public void handleImage(SpannableStringBuilder builder, int start, int end, String name) {

            //同联系人
            TouchableImageSpan span = new TouchableImageSpan(mContext, RichEditView.this, name);
            span.initialize();

            String objStr = "\uFFFC";
            builder.replace(start, end, objStr);
            builder.setSpan(span, start, start + objStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        }

        public void handleStrike(SpannableStringBuilder builder, int start, int end) {
            //设置删除线
            builder.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        }
    }

3.调用

Spanned localSpanned = SpanUtils.parseSpannable(paramCharSequence.toString(), mMediaHandler);
setText(localSpanned);

4. 工具类

import java.io.File;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.text.style.StrikethroughSpan;
import android.widget.TextView;

public class SpanUtils {
    public static final char OBJ_CHAR = '\uFFFC';  //类似小图片的特殊符
    public static final char NEWLINE_CHAR = '\n';  //换行符
    public static final char IMG_START_CHAR = '\u263A'; //&
    public static final char IMG_END_CHAR = '\u263B'; //笑脸
    public static final char CONTARCT_START_CHAR = '\u263E';    //符号 (
    public static final char CONTARCT_END_CHAR = '\u263D';     //符号 )
    public static final char CONTARCT_SPEATROR_CHAR = '\u263C'; //虫子

    public static final char CHECK_CHAR = '\u221A';//对号。
    public static final char BLANK_CHAR = ' ';


    //删除线的匹配(正则)
    private static final String REGEX_STRIKE_SPAN = "^" + CHECK_CHAR + "([^\n" + CHECK_CHAR
            + "]+)$";
    //图片的匹配 (正则)
    private static final String REGEX_IMAGE_SPAN = IMG_START_CHAR + "([A-Za-z0-9]+)" + IMG_END_CHAR;

    //电话号码的匹配 , add phone number rules (正则)
    private static final String REGEX_CONTACT_SPAN = 
             //电话号码的正则匹配
            CONTARCT_START_CHAR + 
            "(\\(\\d{1,4}\\)\\s\\d{1,4}-\\d{1,4}|" +              
            "\\d{1,3}-\\d{1,8}|" +              // 029 - 123456789
            "\\+\\d{1,13}|" +                      //             \\ 13484668924 
            "\\+\\d{2}\\s\\d{1,11}|" +     //             86 123484466456
            "\\+\\d{2}\\s\\d{1,4}\\s\\d{1,4}\\s\\d{1,4}|" +   
            "\\+\\d{2}-\\d{1,11}|" +
            "\\+\\d{2}-\\d{1,11}-\\d{1,11}|" +
            "\\+\\d{2}-\\d{1,11}-\\d{1,11}\\*\\d{1,5}|" +    
            "\\d{1,4}\\s\\d{1,4}\\s\\d{1,4}|" +                 
            "\\d{1,13}|" +                                                 
            "\\d{1,2}\\s\\d{1,4}-\\d{1,4}-\\d{1,4}+)" +          //  11 1344-4584-7456
            CONTARCT_SPEATROR_CHAR +  
            "([^" 
            + IMG_START_CHAR + IMG_END_CHAR + CONTARCT_START_CHAR + CONTARCT_END_CHAR
            + CONTARCT_SPEATROR_CHAR + CHECK_CHAR + "]+)"
            + CONTARCT_END_CHAR;



    //对数据库取出来的数据, 要在界面显示的文字进行处理
    public static Spanned parseSpannable(String text, IMediaHandler handler) {
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        int start, end;
        //正则进行匹配先,这里是删除线
        Pattern p1 = Pattern.compile(REGEX_STRIKE_SPAN, Pattern.MULTILINE);
        Matcher m1 = p1.matcher(builder);
        while (m1.find()) {
            String content = m1.group(1);
            start = m1.start();
            end = m1.end();
            handler.handleStrike(builder, start, end);
            builder.replace(start, end, content);
            m1.reset(builder);
        }
        //电话号码
        Pattern p2 = Pattern.compile(REGEX_CONTACT_SPAN, Pattern.MULTILINE);
        Matcher m2 = p2.matcher(builder);
        while (m2.find()) {
            String phone = m2.group(1);
            String name = m2.group(2);
            start = m2.start();
            end = m2.end();
            handler.handleContact(builder, start, end, name, phone);
            //reset the Matcher
            m2.reset(builder);
        }
        //image
        Pattern p3 = Pattern.compile(REGEX_IMAGE_SPAN, Pattern.MULTILINE);
        Matcher m3 = p3.matcher(builder);
        while (m3.find()) {
            String imgName = m3.group(1);
            start = m3.start();
            end = m3.end();
            handler.handleImage(builder, start, end, imgName);
            //reset the Matcher
            m3.reset(builder);
        }
        return builder;
    }

    //单独删除线的处理(闹钟显示,widget 对从数据库中取出来的数据,正则匹配,然后还原成原来的样子),
    public static Spanned parseStrikethroughSpan(CharSequence showText) {
        SpannableStringBuilder builder = new SpannableStringBuilder(showText);
        Pattern p = Pattern.compile(REGEX_STRIKE_SPAN, Pattern.MULTILINE);
        Matcher m = p.matcher(builder);
        int start, end;
        while (m.find()) {
            String content = m.group(1);
            start = m.start();
            end = m.end();
            builder.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
            builder.replace(start, end, content);
            m.reset(builder);
        }
        return builder;
    }

    // widget 和 dialog 上显示联系人  [ lilei: 1234567890 ],进行格式化
    public static CharSequence normalizeSnippet(String content) {
        return parseStrikethroughSpan(normalizeSnippet(null, content));
    }

    // 将图片信息删掉,将联系人信息重新组织输出,调用下面紧跟着的这个方法,用于显示在 其他不能点击的地方,比如 widget 和 dialog
    public static String normalizeSnippet(String content, NoteImgInfo info) {
        Pattern p = Pattern.compile(REGEX_IMAGE_SPAN, Pattern.MULTILINE);
        Matcher m = p.matcher(content);
        boolean bool = false;
        while (m.find()) {
            String name = m.group(1);
            if (!bool) {
                bool = true;
                if (info != null)
                    info.firstImgName = name;
            }
        }
        return normalizeSnippet(null, content).toString();
    }

    // 单独格式化联系人((闹钟显示, 因为闹钟提醒里面的话,不像平常文本可点击查看联系人  [  lilei: 1234567890 ]              )
    public static CharSequence normalizeSnippet(Context context, String content) {
        String out = new StringBuilder(content).toString();
        Pattern p1 = Pattern.compile(REGEX_CONTACT_SPAN, Pattern.MULTILINE);
        Matcher m1 = p1.matcher(out);
        while (m1.find()) {
            out = m1.replaceFirst("[" + m1.group(2) + ": " + m1.group(1) + "]");
            m1 = p1.matcher(out);
        }
        Pattern p2 = Pattern.compile(REGEX_IMAGE_SPAN, Pattern.MULTILINE);
        Matcher m2 = p2.matcher(out);
        if (m2.find())
            out = m2.replaceAll("");
        return out;
    }


    //当前的文本对spannble格式化,存入数据库
    public static CharSequence buildSpannable(Editable editable) {
        // 此处传入的是当前edit窗口的editable的引用,不能修改,含有span对象,创建新的editable对象时需将其序列化?传入
        Editable out = Editable.Factory.getInstance().newEditable(
                editable.subSequence(0, editable.length()));

        int start, end;
        String content;
        //查看是否有联系人
        TouchableContactSpan[] contacts = out.getSpans(0, out.length(), TouchableContactSpan.class);
        for (int i = 0; i < contacts.length; i++) {
            start = out.getSpanStart(contacts[i]);
            end = out.getSpanEnd(contacts[i]);
            content = CONTARCT_START_CHAR + contacts[i].getPhoneNumber() + CONTARCT_SPEATROR_CHAR
                    + contacts[i].getContactName()
                    + CONTARCT_END_CHAR;
            out.replace(start, end, content);
        }

        //图片
        TouchableImageSpan[] images = out.getSpans(0, out.length(), TouchableImageSpan.class);
        for (int i = 0; i < images.length; i++) {
            start = out.getSpanStart(images[i]);
            end = out.getSpanEnd(images[i]);
            content = IMG_START_CHAR + images[i].getName() + IMG_END_CHAR;
            if (start >= 0 && end >= 0)
                out.replace(start, end, content);
        }

        //删除线
        StrikethroughSpan[] spans = out.getSpans(0, out.length(), StrikethroughSpan.class);
        for (int i = 0; i < spans.length; i++) {
            start = out.getSpanStart(spans[i]);
            end = out.getSpanEnd(spans[i]);
            content = out.subSequence(start, end).toString();
            if (start >= 0 &&  out.charAt(start)  != CHECK_CHAR )
                out.replace(start, end, CHECK_CHAR + content);
        }
        return out;
    }




    public static class SimpleBuilder extends SpannableStringBuilder {
        public SimpleBuilder(String text) {
            super(text);
        }
        public SimpleBuilder() {
            super();
        }
        public boolean isEmpty() {
            return TextUtils.isEmpty(this.toString());
        }
        public void appendNewLine() {
            this.append("\n");
        }
        public void appendImage(String name) {
            // File img = new File(name);
            // if (img.exists()) {
            // BitmapFactory.Options option = new BitmapFactory.Options();
            // option.inJustDecodeBounds = false;
            // Bitmap bmp = BitmapFactory.decodeFile(img.getAbsolutePath(),
            // option);
            // // ImageSpan spans = new ImageSpan(bmp);
            // TouchableImageSpan span = new TouchableImageSpan(mContext, this,
            // imgName);
            // span.initialize();
            // // ImageSpan span = new ImageSpan(getMissedDrawable());
            // int start = this.length();
            // this.append(OBJ_CHAR);
            // this.setSpan(span, start, end,
            //
            // this.setSpan(spans, start, start + 1,
            // Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            // Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            // }
            this.append(IMG_START_CHAR + name + IMG_END_CHAR);
        }

        public void appendText(String text) {
            this.append(text);
        }

    }

    public static ArrayList<String> retrieveImages(Activity mActivity, CharSequence content) {
        ArrayList<String> out = new ArrayList<String>();
        Pattern p = Pattern.compile(REGEX_IMAGE_SPAN, Pattern.MULTILINE);
        Matcher m = p.matcher(content);
        while (m.find()) {
            String name = m.group(1);
            out.add(name);
        }
        return out;
    }


    public static class NoteImgInfo {
        public String firstImgName;

        public NoteImgInfo() {
            firstImgName = null;
        }
    }


    //查看中是否有图片
    public static boolean findImageInSnippet(String content) {
        Pattern p = Pattern.compile(REGEX_IMAGE_SPAN, Pattern.MULTILINE);
        Matcher m = p.matcher(content);
        if (m.find())
            return true;
        return false;
    }

    public static ArrayList<String> collectImageNames(String content) {
        ArrayList<String> names = new ArrayList<String>();
        Pattern p = Pattern.compile(REGEX_IMAGE_SPAN, Pattern.MULTILINE);
        Matcher m = p.matcher(content);
        while (m.find()) {
            names.add(m.group(1));
        }
        return names;
    }
}

二、内容事件处理

1.定义基本接口

private static abstract interface IActionDetector {
    public abstract boolean handleMotionEvent(MotionEvent event);

}
  1. 联系人的处理
private class ClickContactDetector implements IActionDetector {
        private TouchableContactSpan nActiveSpan;
        private boolean nTraking;
        private ClickContactDetector() {
        }

        //对点击的地方处理,得到坐标进行处理
        private TouchableContactSpan getActiveSpan(MotionEvent event) {
            Editable editable = getText();
            TouchableContactSpan[] spans = (TouchableContactSpan[]) editable.getSpans(0,
                    editable.length(), TouchableContactSpan.class);

            for (int i = 0; i < spans.length; i++) {
                if (isTouchInContactSpan(spans[i], event))
                    return spans[i];
            }
            return null;
        }

        //对坐标进行判断,是否点击的是 可点击的联系人区域
        private boolean isTouchInContactSpan(TouchableContactSpan span,
                MotionEvent event) {
            if (span != null)
                return span.isTouched((int) event.getX(),
                        (int) event.getY());
            return false;
        }

       //对接口类方法的实现,外部调用此方法
       public boolean handleMotionEvent(MotionEvent event) {
            boolean bool = false;
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    nActiveSpan = getActiveSpan(event);
                    if (nActiveSpan != null) {
                        nTraking = true;
                        bool = true;
                    }
                    else
                        nTraking = false;
                    break;
                case MotionEvent.ACTION_UP:
                    if (nTraking && isTouchInContactSpan(nActiveSpan, event)) {
                        viewContact(nActiveSpan);
                        nTraking = false;
                        nActiveSpan = null;
                        bool = true;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (nTraking)
                        break;
                case MotionEvent.ACTION_CANCEL:
                    if (!nTraking)
                        break;
                default:
                    nTraking = false;
                    nActiveSpan = null;
                    break;
            }
            return bool;
        }

    }

调用系统联系人显示

    private void viewContact(TouchableContactSpan span) {
        Uri localUri = Contact
                .getContactUri(mContext, span.getContactName(), span.getPhoneNumber());
        if (localUri != null)
            ContactsContract.QuickContact.showQuickContact(getContext(), this, localUri,
                    ContactsContract.QuickContact.MODE_LARGE, null);
    }

通过查询

 public static Uri getContactUri(Context context, String name, String phoneNumber) {
        Cursor cursor = getContactCursor(context, name, phoneNumber);
        Uri uri = null;
        if (cursor != null) {
            try {
                if (cursor.moveToNext()) {
                    String str = cursor.getString(0);
                    long l = cursor.getLong(1);
                    uri = ContactsContract.Contacts.CONTENT_LOOKUP_URI.buildUpon().appendPath(str)
                            .appendPath(Long.toString(l)).build();
                }
            } catch (Exception e) {
                // TODO: handle exception
            } finally {
                if (cursor != null)
                    cursor.close();
            }
        }
        return uri;
    }

查询

private static Cursor getContactCursor(Context context, String name, String phoneNumber) {
        Object[] arrayOfObject = new Object[2];
        arrayOfObject[0] = "display_name";
        arrayOfObject[1] = "data1";
        String str = String.format(Locale.US, "%s=? AND PHONE_NUMBERS_EQUAL(%s,?)", arrayOfObject);
        String[] arrayOfString = new String[] {
                name, phoneNumber
        };
        return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
                IDENTITY_PROJECTION, str, arrayOfString, null);
    }

2.对问题点击到图片的处理

   private class ClickImageDetector implements IActionDetector {
        private TouchableImageSpan nActiveSpan;
        private boolean nTraking;

        private ClickImageDetector() {
        }

        private TouchableImageSpan getActiveSpan(MotionEvent event)
        {
            Editable editable = getText();
            TouchableImageSpan[] spans = (TouchableImageSpan[]) editable.getSpans(0,
                    editable.length(), TouchableImageSpan.class);

            for (int i = 0; i < spans.length; i++) {
                if (isTouchInImageSpan(spans[i], event))
                    return spans[i];
            }
            return null;
        }

        private boolean isTouchInImageSpan(TouchableImageSpan paramSmartImageSpan,
                MotionEvent paramMotionEvent) {
            if (paramSmartImageSpan != null)
                return paramSmartImageSpan.isTouched((int) paramMotionEvent.getX(),
                        (int) paramMotionEvent.getY());
            return false;
        }

        public boolean handleMotionEvent(MotionEvent paramMotionEvent) {
            boolean bool = false;
            switch (paramMotionEvent.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    nActiveSpan = getActiveSpan(paramMotionEvent);
                    if (nActiveSpan != null) {
                        nTraking = true;
                        bool = true;
                    }
                    else
                        nTraking = false;
                    break;
                case MotionEvent.ACTION_UP:
                    if (nTraking && isTouchInImageSpan(nActiveSpan, paramMotionEvent)) {
                        viewImage(nActiveSpan);
                        nTraking = false;
                        nActiveSpan = null;
                        bool = true;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (nTraking)
                        break;
                case MotionEvent.ACTION_CANCEL:
                    if (!nTraking)
                        break;
                default:
                    nTraking = false;
                    nActiveSpan = null;
                    break;
            }
            return bool;
        }
    }

使自定义的对点击事件的处理生效,

  1. 在界面创建时
mClickImageDetector = new ClickImageDetector();
mClickContactDetector = new ClickContactDetector();
  /*
     * 如果dispatchTouchEvent返回true
     * ,则交给这个view的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view 的
     * interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true
     * ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子
     * view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的
     * onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent
     * 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if ((mClickImageDetector.handleMotionEvent(event))
                || (mClickContactDetector.handleMotionEvent(event))
                || mStrokeDetector.handleMotionEvent(event)
                || (super.dispatchTouchEvent(event)))
            return true;
        return false;
    }
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

空白的泡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值