Android teleprompter implements rich text style

premise

I made a program some time ago, a teleprompter APP, combined with greendao to save data. Recently, a requirement has been added to realize the display of some text rich text. The master found an online SDK, but there were always problems when integrating it, and I didn't want to move the project in, it felt very troublesome, so I built a relatively low one. The process went down step by step, and the final effect was very low.

renderings

I checked the implementation of rich text in Android, mainly the SpannableStringBuilder class. My idea is to get the selected text, and then directly set the rich text format and display it.

Design ideas

first step:

The setting of the menu after selecting the text in EditText uses setCustomSelectionActionModeCallback

code show as below:

//选择文本的菜单
        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) {

            }
        });

second step

Set rich text style

//创建富文本对象
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);

I thought this would be all right, but this caused the previous rich text style to be erased every time I updated, and then I used greendao to save the rich text attributes. A data table, which corresponds to several attributes: page page, start coordinates, end coordinates, text size, text color. In this way, every time one is set, it will be stored as an object. This object is bound to the page. When clicking to enter a different page, it will query how many attribute objects the page has according to the page, and then set it into str. Then setText comes out.

third step

The rich text style is bound to greendao, the main code is like this

greendao management class

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();
    }
}

After each click, save the newly set style, read it again, and refresh all the rich text

            
                 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);

    }

And every time you first enter the APP, when you click on a different page, and after modifying the menu options, you can call a readState() method.

At this stage, I feel that the design is very low. Do you guys have any good methods? ! !

Summary of rich text settings

//创建一个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:[email protected](new URLSpan("mailto:[email protected]"), 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); //地图

Guess you like

Origin blog.csdn.net/m0_56366502/article/details/129398598