当前位置: 首页 > news >正文

仿小米便签图文混排 EditText解决尾部插入文字bug

一直想实现像小米便签那样的图文混排效果,收集网上的办法无非三种:
1、自定义布局,每张图片是一个ImageView,插入图片后插入EditText,缺点是实现复杂,不能像小米便签那样同时选中图片和文字
2、通过Html.fromHtml(source),可以将图片加载写进ImageGetter,实现后无bug,但是只能显示Html,当EditText setText后,想取出之前的HTML格式
     图片得到的是一个obj的字符,查看了很多博客,包括stackoverflow也没给出办法从editable中解析出spanned对象。若谁有方法希望不吝啬告诉我。
3、通过ImageSpan和SpannableString,这是我实现的方法,而且较为理想,不但可以写入EditText,也可以从EditText中解析出图文混排排版。

1、插入图片到光标位置

/**
     * 将图片路径映射到Bitmap,再通过SpannableString 和 ImageSpan显示到EditText
     */
    private void setImageView() {
        // 如果EditText中已经有相同资源的ImageSpan,则不再读取图片
        ImageSpan imageSpan = getImageSpanFromExistence(imagePath);
        if (imageSpan != null) {
            insertIntoEditor(imageSpan, imagePath);
            return;
        }
 
        if (imagePath != null && (!imagePath.equals("null"))
                && (!"".equals(imagePath))) {
            insertIntoEditor(imagePath);
            /*
             * 不再用缓存模式
             */
        }
    }
 
/**
     * 从当前的EditText获取ImageSpan,如果存在则返回否则返回Null
     *
     * @return
     */
    private ImageSpan getImageSpanFromExistence(String source) {
        Editable edit = contentText.getText();
        ImageSpan[] spans = edit.getSpans(0, edit.length(), ImageSpan.class);
        for (ImageSpan ip : spans) {
            int start = edit.getSpanStart(ip);
            int end = edit.getSpanEnd(ip);
            String path = edit.toString().substring(start, end);
            path = path.substring(5, path.length() - 5);
 
            if (source.equals(path)) {
                Logg.D("find existed ImageSpan");
                return new ImageSpan(ip.getDrawable(), ImageSpan.ALIGN_BASELINE);
            }
        }
        return null;
    }
 
/**
     * 向光标位置插入ImageSpan,针对EditText已经有ImageSpan的情况
     *
     * @param ip
     *            ImageSpan
     * @param path
     *            路径
     */
    private void insertIntoEditor(ImageSpan span, String path) {
        if(("<img " + path + " img>").length()+contentText.getText().length() > MAX_CONTENT){
            Toast.makeText(getApplicationContext(), R.string.toast_reached_max_text, 2000).show();
            return;
        }
        SpannableString ss = new SpannableString("<img " + path + " img>");
        if (span == null)
            throw new NullPointerException("span cant be null");
        ss.setSpan(span, 0, ("<img " + path + " img>").length(),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        Editable et = contentText.getText();// 先获取Edittext中的内容
        int start = contentText.getSelectionStart();
        et.insert(start, ss);// 设置ss要添加的位置
        contentText.setSelection(start + ss.length());// 设置Edittext中光标在最后面显示
        Logg.D("insertIntoEditor by using existed ImageSpan");
    }
 
/**
     * 向光标位置插入ImageSpan,针对EditText没有图片的情况
     *
     * @param path
     *            图片路径
     */
    private void insertIntoEditor(String path) {
        if(("<img " + path + " img>").length()+contentText.getText().length() > MAX_CONTENT){
            Toast.makeText(getApplicationContext(), R.string.toast_reached_max_text, 2000).show();
            return;
        }
        SpannableString ss = new SpannableString("<img " + path + " img>");
        // 不再用缓存模式
        // Bitmap bm = mEditorHelper.getImage(path);
        Bitmap bm = PictureHelper.getImageFromPath(imagePath,
                screenWidth * 0.7F, screenWidth * 0.7F, false, 100,
                Editor.this, imgPadding, false);
        if (bm == null) {
            throw new NullPointerException("bm cant be null");
        }
 
        ImageSpan span = new ImageSpan(this, bm, ImageSpan.ALIGN_BASELINE);
        ss.setSpan(span, 0, ("<img " + path + " img>").length(),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        Editable et = contentText.getText();// 先获取Edittext中的内容
        int start = contentText.getSelectionStart();
        et.insert(start, ss);// 插入图片到光标处
//        contentText.setSelection(start + ss.length());// 设置Edittext中光标在最后面显示
        Logg.D("insertIntoEditor by loading new");
    }

以上插入图片前会查找edittext有没有相同地址的ImageSpan,如果有则不再读取新的Bitmap,直接复用drawable

2、解决尾部插入Bug\

Bug来源:http://www.baidufe.com/item/65fd7eba51123bbe80bc.html
一直没能理解作者说的半角站位
我实现了一个方法,当光标到达图片末尾,直接跳转至正确位置
实现:http://mxw3755.iteye.com/admin/blogs/2164905
当光标嵌入spannableString的字符从末尾前一位,我们可以将光标位置+1,这样光标会在两张图之间
3、如何保存解析图文混排的EditText

可以将ImageSpan的位置保存到Arraylist中,怎么保存至数据库请参考 http://mxw3755.iteye.com/admin/blogs/2165147

 android 图文结合,使用SpannableString和ImageSpan类

    Drawable drawable = getResources().getDrawable(id); 
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); 
        //需要处理的文本,[smile]是需要被替代的文本 
        SpannableString spannable = new SpannableString(getText().toString()+"[smile]"); 
        //要让图片替代指定的文字就要用ImageSpan 
        ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE); 
        //开始替换,注意第2和第3个参数表示从哪里开始替换到哪里替换结束(start和end) 
       //最后一个参数类似数学中的集合,[5,12)表示从5到12,包括5但不包括12 
        spannable.setSpan(span, getText().length(),getText().length()+"[smile]".length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);   
        setText(spannable); 

 

相关文章:

  • 前端展示用部分CSS
  • 解剖SQLSERVER 第三篇 数据类型的实现(译)
  • DB2数据库用 With语句分隔字符
  • 处理和引发事件的规范
  • 图像的边缘提取
  • Linux之shell编程基础
  • 測试之路2——对照XML文件1
  • 魅族 连接 mac 调试
  • PhotoSwipe - 移动开发必备的 iOS 风格相册
  • https://github.com/cykl/infoqscraper/
  • 数据结构实验之栈四:括号匹配
  • django 安装/部署过程
  • 使用expdp的心得
  • 安装Ubuntu开发工具中心
  • Linux学习之CentOS(九)--Linux系统的网络环境配置
  • .pyc 想到的一些问题
  • 【Linux系统编程】快速查找errno错误码信息
  • 10个确保微服务与容器安全的最佳实践
  • angular2 简述
  • GitUp, 你不可错过的秀外慧中的git工具
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • Phpstorm怎样批量删除空行?
  • React组件设计模式(一)
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • Spring Cloud Feign的两种使用姿势
  • 从tcpdump抓包看TCP/IP协议
  • 从零开始学习部署
  • 分布式事物理论与实践
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 巧用 TypeScript (一)
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 一文看透浏览器架构
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • #{} 和 ${}区别
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (独孤九剑)--文件系统
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (九十四)函数和二维数组
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • ../depcomp: line 571: exec: g++: not found
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .NET 反射的使用
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 WeakDictionary)
  • .net专家(高海东的专栏)
  • @Async注解的坑,小心