Android提词器实现富文本样式

news2025/1/25 9:08:19

前提

前一段时间做了一个程序,提词器APP,结合greendao保存数据。最近新增了一个需求,实现部分文字富文本的展现。师傅找了一个网上的SDK,但是在集成的时候总是出问题,我又不想把项目挪进来,感觉很麻烦,自己搞了一个比较low的。过程一步一步走下来,最后效果很Low。

效果图

查询了下Android中富文本的实现,主要是SpannableStringBuilder类,我的想法是获取到选中的文字,然后直接设置富文本的格式,展示出来。

设计思路

第一步:

EditText中选中文本后菜单的设置,用到了setCustomSelectionActionModeCallback

代码如下:

//选择文本的菜单
        text.setCustomSelectionActionModeCallback(new ActionMode.Callback2() {
           //Callback2()接口要求SDK>23 android6以上的设备 也可以实现Callback()接口

          @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                MenuInflater menuInflater = mode.getMenuInflater();
                menuInflater.inflate(R.menu.selection_menu, menu);
                return true;//返回false不会显示弹窗
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                selectionStart = text.getSelectionStart();
                selectionEnd = text.getSelectionEnd();
                text.requestFocus();
                if (selectionStart != selectionEnd) {
                    //选择的字体
                    charSequence = text.getText().subSequence(selectionStart, selectionEnd);
                }
                return false;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.menu_add:
                        //设置字体增大
                        break;
                    case R.id.menu_dec:
                        //设置字体减小
                        break;
                    case R.id.menu_red:
                        //红色字体
                        break;
                    case R.id.menu_blue:
                        //蓝色字体
                        break;
                    case R.id.menu_black:
                        //黑色字体
                        break;
                }
                return false;//返回true系统默认的菜单选项会不见
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {

            }
        });

第二步

设置富文本样式

//创建富文本对象
SpannableStringBuilder str=new SpannableStringBuilder(textAll);
//将要设置的整体文本加载进来
str =  textAll = text.getText().toString();
//设置字体的大小 start是起始角标 end是结束角标
str.setSpan(new AbsoluteSizeSpan(font), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//设置字体颜色
ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(color);
str.setSpan(foregroundColorSpan, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);

//应用字体
 text.setText(str, TextView.BufferType.SPANNABLE);

本以为这就可以了,但是这样就导致我每次更新,上一次的富文本样式就会被刷没,然后我就使用greendao来保存富文本属性。一个数据表,里面对应了几个属性:page页面、起始坐标、终止坐标、文字大小、文字颜色。这样每设置一个,就把他作为一个对象存储起来,这个对象与page绑定,在点击进入不同页面的时候,就会根据page去查询该页有几个属性对象,以此设置进str中,然后setText出来。

第三步

富文本样式与greendao绑定,主要代码是这样的

greendao管理类

public class DetailManger {

    private static final String TAG = "DetailManger";

    private static final String dbName = "detail.db";
    private final Context context;
    private DetailBeanDao dao;

    private DaoMaster.DevOpenHelper openHelper;
    public static DaoSession daoSession;

    private static DetailManger mInstance;

    private DetailManger(Context context){
        this.context = context;
        openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
        Database db = openHelper.getWritableDb();
        daoSession = new DaoMaster(db).newSession();


        DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
        DaoSession daoSession = daoMaster.newSession();
         dao = daoSession.getDetailBeanDao();

    }

    //单例
    public static DetailManger getInstance(Context context){
        if (mInstance==null){
            synchronized (DetailManger.class){
                if (mInstance==null){
                    mInstance=new DetailManger(context);
                }
            }
        }
        return mInstance;
    }
    //可读数据库
    private SQLiteDatabase getReadableDatabase() {
        if (openHelper == null) {
            openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
        }
        SQLiteDatabase db = openHelper.getReadableDatabase();
        return db;
    }

    //可写数据库
    private SQLiteDatabase getWritableDatabase() {
        if (openHelper == null) {
            openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
        }
        SQLiteDatabase db = openHelper.getWritableDatabase();
        return db;
    }

    //插入
    public void insert(DetailBean detailBean) {

        dao.insert(detailBean);
    }


    //删除数据
    public void delete(DetailBean detailBean) {

        dao.delete(detailBean);
    }

    //更改
    public void update(DetailBean detailBean) {


        dao.update(detailBean);
    }

    //查询
    public ArrayList<DetailBean> query() {

        QueryBuilder<DetailBean> qb = dao.queryBuilder();
        ArrayList<DetailBean> list = (ArrayList<DetailBean>) qb.list();
//        for (int i = 0; i < list.size(); i++) {
//            list.get(i).setIsFlush(false);
//        }
        return list;
    }

    public ArrayList<DetailBean> queryByPage(int page){
        return (ArrayList<DetailBean>) dao.queryBuilder().where(DetailBeanDao.Properties.Page.eq(page)).list();
    }
}

在每次点击后都将新设置的样式存入,重新读取一下,把所有的富文本刷新出来

            
                 case R.id.menu_add:
                        //设置字体增大
                        if (charSequence != null) {
                            saveState(page, selectionStart, selectionEnd, size*2, Color.BLACK);//保存数据
                            readState(page);
                        }
                    break;


    //保存富文本样式
    private void saveState(int page, int start, int end, int font, int color) {
        SpannableStringBuilder str;
        textAll = text.getText().toString();
        DetailBean bean = new DetailBean();
        bean.setPage(page);
        bean.setStart(start);
        bean.setEnd(end);
        bean.setFont(font);
        bean.setColor(color);
        DetailManger.getInstance(MainActivity.this).insert(bean);
        Log.d(TAG, "saveState: " + bean.getPage() + " " + bean.getStart() + " " + bean.getEnd() + " " + bean.getFont() + " " + bean.getColor());

        str = new SpannableStringBuilder(textAll);

        str.setSpan(new AbsoluteSizeSpan(font), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(color);
        str.setSpan(foregroundColorSpan, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        readState(page);
    }


    //读取显示富文本样式
    private void readState(int page) {
        SpannableStringBuilder str;
        textAll = text.getText().toString();
        ArrayList<DetailBean> detailBeans = DetailManger.getInstance(MainActivity.this).queryByPage(page);
        str = new SpannableStringBuilder(textAll);
        for (int j = 0; j < detailBeans.size(); j++) {
            DetailBean bean = detailBeans.get(j);
            int start = bean.getStart();
            int end = bean.getEnd();
            int font = bean.getFont();
            int color = bean.getColor();
            if (selectionStart == bean.getStart() && selectionEnd == bean.getEnd()) {
                Log.d(TAG, "readState: 1111111");
            }

            if (str!=null){
                str.setSpan(new AbsoluteSizeSpan(font), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(color);
                str.setSpan(foregroundColorSpan, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
            }


        }
        text.setText(str, TextView.BufferType.SPANNABLE);

    }

并且在每次刚进入APP的时候、点击不同页面的时候、以及修改菜单选项后都调用一个readState()方法就可以了。

现阶段觉得设计很low,大佬们有什么好的方法吗!!!

富文本的设置总结

//创建一个SpannableString对象
sStr = new SpannableString("文本文本文本文本文本文本文本文本文本文本文本文本");

//设置字体(default,default-bold,monospace,serif,sans-serif)
sStr.setSpan(new TypefaceSpan("default"), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new TypefaceSpan("default-bold"), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new TypefaceSpan("monospace"), 4, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new TypefaceSpan("serif"), 6, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new TypefaceSpan("sans-serif"), 8, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置字体大小(绝对值,单位:像素),第二个参数boolean dip,如果为true,表示前面的字体大小单位为dip,否则为像素
sStr.setSpan(new AbsoluteSizeSpan(20), 10, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new AbsoluteSizeSpan(20, true), 12, 14, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置字体大小(相对值,单位:像素) 参数表示为默认字体大小的多少倍 ,0.5表示一半
sStr.setSpan(new RelativeSizeSpan(0.5f), 14, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置字体前景色
sStr.setSpan(new ForegroundColorSpan(Color.RED), 16, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置字体背景色
sStr.setSpan(new BackgroundColorSpan(Color.CYAN), 18, 20, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置字体样式: NORMAL正常,BOLD粗体,ITALIC斜体,BOLD_ITALIC粗斜体
sStr.setSpan(new StyleSpan(android.graphics.Typeface.NORMAL), 20, 21, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 21, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 22, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new StyleSpan(android.graphics.Typeface.BOLD_ITALIC), 23, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置下划线
sStr.setSpan(new UnderlineSpan(), 24, 26, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置删除线
sStr.setSpan(new StrikethroughSpan(), 26, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置上下标
sStr.setSpan(new SubscriptSpan(), 28, 30, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sStr.setSpan(new SuperscriptSpan(), 30, 32, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置字体大小(相对值,单位:像素) 参数表示为默认字体宽度的多少倍 ,2.0f表示默认字体宽度的两倍,即X轴方向放大为默认字体的两倍,而高度不变
sStr.setSpan(new ScaleXSpan(2.0f), 32, 34, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//设置项目符号
sStr.setSpan(new BulletSpan(android.text.style.BulletSpan.STANDARD_GAP_WIDTH,Color.GREEN), 0 ,sStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //第一个参数表示项目符号占用的宽度,第二个参数为项目符号的颜色

//设置图片
Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
sStr.setSpan(new ImageSpan(drawable), 24, 26, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

tv.setText(sStr);
tv.setMovementMethod(LinkMovementMethod.getInstance());

sStr2 = new SpannableString("电话邮件百度一下短信彩信进入地图");
//超级链接(需要添加setMovementMethod方法附加响应)
sStr2.setSpan(new URLSpan("tel:8008820"), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //电话
sStr2.setSpan(new URLSpan(" mailto:kejunlu@qq.comsStr2.setSpan(new URLSpan("mailto:kejunlu@qq.com"), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //邮件
sStr2.setSpan(new URLSpan(" http://www.baidu.com"), 4, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //网络
sStr2.setSpan(new URLSpan("sms:10086"), 8, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //短信 使用sms:或者smsto:
sStr2.setSpan(new URLSpan("mms:10086"), 10, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

//彩信 使用mms:或者mmsto:
sStr2.setSpan(new URLSpan("geo:32.123456,-17.123456"), 12, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //地图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/396741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Oracle P6 Professional相比与Microsoft Project的8个优势

目录 引言 1. 自上而下的调度 2. 努力程度 (LOE) 活动 3. 最长路径 4. 多浮动路径分析功能 6.预算材料成本 7. 开始和完成里程碑 8. 工作公式类型 概括 引言 哪种日程安排工具更适合您的情况&#xff0c;Oracle Primavera P6 还是 Microsoft Project(MSP) 经常有一些…

MySQL8.0Linux安装及主从的搭建

MySQL8.0Linux安装教程 下载并安装 需要说明的一点是我使用的是SSH secure shell Client连接linux系统的&#xff0c;它的用法和命令窗口差不多。界面如图&#xff1a;一样的使用Linux命令操作。 话不多说 第一步&#xff1a; 1&#xff09;、切换到 /usr/local下 cd /usr/…

已解决hint : See above for output from the failure.

已解决&#xff08;pip install wxPython安装失败&#xff09;error: legacy-instal1-failure Encountered error while trying to install package.wxPython note: This is an issue with the package mentioned above&#xff0c;not pip. hint : See above for output from …

关于世界坐标系,相机坐标系,图像坐标系,像素坐标系的一些理解

关于世界坐标系&#xff0c;相机坐标系&#xff0c;图像坐标系&#xff0c;像素坐标系的一些理解前言一、各坐标系的含义二、坐标系转换1.世界坐标系与相机坐标系&#xff08;旋转与平移&#xff09;2.相机坐标系与图像坐标系&#xff08;透视&#xff09;3.图像坐标系与像素坐…

【UE4 RTS游戏】02-摄像机运动_完成摄像机在X轴上运动的相关步骤

效果通过控制键盘WS键使得“CameraPawn”进行前后移动步骤将landscape的Z轴位置更改为0删除“PostProcessVolume”将“LightmassImportanceVolume”移入Lighting文件夹内新建一个蓝图类&#xff0c;父类是Pawn&#xff0c;命名为“CameraPawn”将“MyController”重命名为“Cam…

详解JVM

详解JVM 最近学习了&#xff1a;周志明《深入理解高并发编程》&#xff1b;&#xff1b; 特此简要对学习做了部分总结&#xff0c;方便后续对JVM相关知识的完善和巩固&#xff1b; 若想深入了解学习&#xff0c;可阅读上述参考原著&#xff1b; Java内存区域与OOM 运行时数据…

大数据 | (三)centos7图形界面无法执行yum命令

大家好&#xff0c;今天是三八女神节了&#xff01; 你知道吗&#xff1f;世界上第一位电脑程序设计师是名女性&#xff0c;Ada Lovelace (1815-1852)。 她是一位英国数学家兼作家&#xff0c;第一位主张计算机不只可以用来算数的人&#xff0c;也发表了第一段分析机用的演算…

vector中迭代器失效的问题及解决办法

目录 vector常用接口 vector 迭代器失效问题 vector中深浅拷贝问题 vector的数据安排以及操作方式&#xff0c;与array非常相似。两者的唯一差别在于空间的运用的灵活性。array 是静态空间&#xff0c;一旦配置了就不能改变&#xff1b;要换个大(或小) 一点的房子&#x…

CorelDRAW Graphics Suite2023更新内容介绍

懂设计的职场人都知道这款软件&#xff0c;CorelDRAW是一款非常高效的矢量图形设计软件。CorelDRAW操作界面简洁易懂&#xff0c;能够为用户提供精确地创建物体的尺寸和位置的功能&#xff0c;减少点击步骤&#xff0c;提高设计效率&#xff0c;节省设计时间。功能比普通的美图…

简单理解TransFormer

背景:听了李宏毅老师关于transformer的讲解&#xff0c;觉得有必要记录一下&#xff0c;里面的PPT都是李宏毅老师的内容(不喜勿喷)1.self-attention在介绍transformer之前&#xff0c;必须先了解self-attention(1) 先将X输入Embedding(a Wx), 然后a乘相关的权重&#xff0c;生…

Day11-网页布局实战-CSS3动画

文章目录一 CSS3动画1 2D动画案例1-鼠标输入移入DIV 让图片旋转90度案例2-鼠标输入移入DIV 缩放图片案例3-贯穿项目-DIV移动2 animation动画播放器案例1-基础案例案例2-使用百分比关键帧定义动画案例3-旋转的图片案例4-贯穿案例-轮播图3 多余文本省略号...代替案例1-多余文本..…

一 Go环境搭建

1. 下载地址 https://golang.google.cn/dl/ 傻瓜式安装&#xff0c;自动会配置path的变量&#xff0c;安装完成后可以使用go version 查看当前安装的版本 本文使用目前最新的1.20.2版本 2. 配置go环境 cmd控制栏打开输入以下命令&#xff08;如果cmd有问题可以尝试powershe…

340秒语音芯片,轻松实现语音交互,畅享智能生活WTV380语音ic方案

随着智能家居、安防报警、宠物用品 等&#xff0c;智能设备的普及&#xff0c;语音交互技术正在逐渐成为人机交互的主要方式之一。而如何实现稳定高效的语音交互&#xff0c;就需要借助先进的语音芯片技术。今天&#xff0c;我们介绍的是一款高性能的语音芯片——WTV380&#x…

Gamma矫正

Gamma 曲线Gamma校正被使用在8位RGB图中。用来解决在有限的存储空间中保存尽可能多的人类感受敏感的色彩内容。Gamma 矫正Gamma校正的方式就是采样时,和输出到显示器给人类看时,对亮度进行的调整.如采样时 Gamma1/2.2 调亮Gamma&#xff0c;如显示时 Gamma2.2 调暗Gamma实际亮度…

【Redis】Redis慢查询

文章目录慢查询记录慢查询两个配置参数修改配置参数慢查询日志慢查询记录 我们都知道像mysql等持久化数据库会有慢查询日志&#xff0c;其实Redis中也有慢查询日志的功能。慢查询就是系统在执行命令的前后计算每条命令的执行时间&#xff0c;如果超过我们预设的时间&#xff0c…

登录接口-简约版(工作日记4)

前提条件&#xff1a; 1、jmeter接口测试工具 2、接口信息 获取验证码信息 登录接口信息 题外话&#xff1a; 接口返回的数据中如果有文字就会乱码 怎么解决 1、进入jmter文件–进入bin文件夹&#xff0c;找到jmeter.properties文件 2、右键打开&#xff0c;可以用文本模…

基于遗传算法的CVRP建模求解(Python)

带容量约束的车辆路径优化问题&#xff0c;CVRP&#xff0c;对一系列装卸货点进行适当的路径规划&#xff0c;在满足约束条件&#xff08;客户需求、车辆载重和容积、车型、车辆行驶里程、配送中心数量等限制&#xff09;和目标最优化&#xff08;路程最短、成本最低、使用车辆…

点灯大师--点个正点原子阿尔法开发板的灯

点灯大师–点个正点原子阿尔法开发板的灯 文章目录点灯大师--点个正点原子阿尔法开发板的灯正点原子阿尔法开发板点灯1、使能 GPIO1 时钟2、设置 GPIO1_IO03 的复用功能3、配置 GPIO1_IO034、设置 GPIO5、控制 GPIO 的输出电平五种点灯的方法1.在一个驱动文件中实现寄存器初始化…

【C语言】编译+链接

一、程序的翻译环境和执行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境&#xff0c;它用于实际执行代码。详解编译链接翻译环境1.组成一个程序的每个源文件通过…

STM32电动车报警器

目录 项目需求 项目框图 硬件清单 振动传感器介绍及实战 振动传感器介绍 振动传感器编程实现 继电器介绍及实战 继电器工作原理 433M无线发射接收模块介绍及实战 433M无线发射接收模块介绍 ​编辑 433M编程实现 项目设计及实现 编程实现 项目需求 点击遥控器A 按键…