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

来源:互联网 发布:ios9越狱mac 编辑:程序博客网 时间:2024/06/16 05:07

关于 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;    }
1 0
原创粉丝点击