前提
私は少し前に、データを保存するために greendao と組み合わせたテレプロンプター APP というプログラムを作成しました。最近、一部のテキストリッチテキストの表示を実現するための要件が追加されました。マスターはオンラインSDKを見つけましたが、統合するときに常に問題が発生し、プロジェクトを移動したくなく、非常に面倒に感じたので、比較的低いものを構築しました。プロセスは段階的に進められ、最終的な効果は非常に低かった。
レンダリング
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を使ってリッチテキスト属性を保存していました。ページ、開始座標、終了座標、テキスト サイズ、テキストの色などのいくつかの属性に対応するデータ テーブル。このようにして、設定されるたびにオブジェクトとして保存されます。このオブジェクトはページにバインドされます。クリックして別のページに入ると、そのページに応じてそのページに属性オブジェクトがいくつあるかを問い合わせます。それを 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() メソッドを呼び出すことができます。
現段階ではデザイン性が非常に低いように感じますが、何か良い方法はありますか?!!
リッチテキスト設定の概要
//创建一个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); //地图