Android提词器实现富文本样式
创始人
2024-05-30 18:04:42
0

前提

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

效果图

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

设计思路

第一步:

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

代码如下:

//选择文本的菜单text.setCustomSelectionActionModeCallback(new ActionMode.Callback2() {//Callback2()接口要求SDK>23 android6以上的设备 也可以实现Callback()接口@Overridepublic boolean onCreateActionMode(ActionMode mode, Menu menu) {MenuInflater menuInflater = mode.getMenuInflater();menuInflater.inflate(R.menu.selection_menu, menu);return true;//返回false不会显示弹窗}@Overridepublic 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;}@Overridepublic 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系统默认的菜单选项会不见}@Overridepublic 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 query() {QueryBuilder qb = dao.queryBuilder();ArrayList list = (ArrayList) qb.list();
//        for (int i = 0; i < list.size(); i++) {
//            list.get(i).setIsFlush(false);
//        }return list;}public ArrayList queryByPage(int page){return (ArrayList) 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 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); //地图

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...