Android提词器实现富文本样式

前提

前一段时间做了一个程序,提词器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); //地图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值