前段時間 需要做 富文本編輯筆記功能,要求能夠插入圖片、待辦事項、無序列表、引用,能夠修改字體大小、段落對齊方式、粗體、斜體、下劃線、中劃線。經過一段時間的努力完成了功能,現在對開發過程進行記錄。實現效果如下圖:
項目地址RichEditor
編輯器涉及到的功能點較多,所以將是一系列的文章。文章內容按照以下的關鍵點進行展開:
字體大小 對齊方式(左對齊 居中 右對齊)
列表項 (多級列表)實現 以及樣式的取消
引用項 實現以及樣式的取消
行間距問題的處理(行高)
待辦事項如何實現
圖片如何插入 (todo ) 插入樣式合并
圖片等控件刪除鍵 點擊事件操作
各個樣式 刪除鍵與回退鍵 的處理
上傳格式,生成HTML 樣式片段
Html 片段的解析 dom 解析 span的解析 (系統代碼的修改)
關于長圖生成
這篇文章先進行頁面組成分析
界面構成
當時看到這個界面的時候一臉懵逼,整個界面的要求
- 整體界面可滾動
- 內容可編輯可以插入文字、圖片、視頻等
- 圖片、視頻提供按鈕操作。
- 軟鍵盤刪除鍵可刪除圖片
- 可插入待辦事項,前方 CheckBox 可點擊
根據界面要求作出以下分析
- 可插入圖片、視頻 界面不能用一個 EditText 來做,需要使用LinearLayout添加不同的控件
- 界面可滑動最外層使用ScrollView
- 可插入待辦事項,單個編輯控件使用LinearLayout包裹
- 圖片區域 包含可操作按鈕,使用RelativeLayout進行包裹
最終實現的 布局結構如下圖:
強烈建議在測試編輯器的時候 打開 開發者模式的顯示布局邊界
構建界面
經過以上分析,界面是由多個輸入區域拼接而成,暫且把輸入區域 稱為 InputWeight
圖片區域稱為ImageWeight 可轉為待辦事項區域稱為 TodoWeight
使用LinearLayout包含多個 InputWeight實現的難點:
- 記錄當前的焦點區域
- 輸入區域的刪除鍵處理
- 在TodoWeight 輸入的中間位置插入ImageWeight 樣式的合并
輸入區域
該部分會貼出 各個輸入區域的 布局和部分代碼,先了解整個布局的組成和一些基本的操作
最外層控件
ScrollView 內容區域為 標題 EditText 和正文編輯器
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/label_area"
android:layout_below="@id/toolbar"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/edt_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@null"
android:hint="@string/input_title"
android:maxLength="80"
android:paddingLeft="25dp"
android:paddingRight="25dp"
android:textColor="@color/text_333"
android:textColorHint="@color/text_999"
android:textSize="22dp"
android:textStyle="bold" />
<com.scwen.editor.RichEditer
android:id="@+id/editor_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="20dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingBottom="5dp"></com.scwen.editor.RichEditer>
</LinearLayout>
</ScrollView>
布局很簡單,其中 RichEditer是編輯器封裝框架,封裝了編輯區域
InputWeight
/**
* Created by scwen on 2019/4/29.
* QQ :811733738
* 作用:輸入控件基類
*/
public abstract class InputWeight {
protected Context mContext;
protected LayoutInflater mInflater;
protected View mContentView;
/**
* 是否顯示 待辦事項
*/
protected boolean isTodo;
public boolean isTodo() {
return isTodo;
}
public void setTodo(boolean todo) {
isTodo = todo;
}
public InputWeight(Context context, ViewGroup parent) {
this.mContext = context;
this.mInflater = LayoutInflater.from(mContext);
getView(parent);
}
public void getView(ViewGroup parent) {
mContentView = mInflater.inflate(provideResId(), parent, false);
initView();
}
public View getContentView() {
return mContentView;
}
/**
* 初始化 View
*/
protected abstract void initView();
/**
* 輸入區域內容轉Html
*
* @return
*/
public abstract String getHtml();
abstract @LayoutRes
int provideResId();
public void showTodo() {
}
public void hideTodo() {
}
public void checkTodo() {
}
public void unCheckTodo() {
}
/**
* 獲取輸入區域的 EditText
*
* @return
*/
abstract public EditText getEditText();
/**
* 獲取輸入的文本
*
* @return
*/
abstract public String getContent();
}
TodoWeight
包含待辦事項的輸入區域:
/**
* Created by scwen on 2019/4/18.
* QQ :811733738
* 作用: 包含 待辦事項的 輸入區域
*/
public class TodoWeight extends InputWeight {
private CheckBox cb_todo_state;
private EditText et_input;
public TodoWeight(Context context, ViewGroup parent) {
super(context, parent);
}
@Override
protected void initView() {
cb_todo_state = mContentView.findViewById(R.id.cb_todo_state);
et_input = mContentView.findViewById(R.id.et_input);
Editable editable = et_input.getText();
cb_todo_state.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
//選中 表示已完成
Editable text = et_input.getText();
et_input.setTextColor(Color.parseColor("#cccccc"));
} else {
uncheckStyle();
}
}
});
}
@Override
public void checkTodo() {
cb_todo_state.setChecked(true);
}
@Override
public void unCheckTodo() {
uncheckStyle();
}
private void uncheckStyle() {
//反選 表示未完成
Editable text = et_input.getText();
et_input.setTextColor(Color.parseColor("#333333"));
}
@Override
public String getHtml() {
if (TextUtils.isEmpty((et_input.getText()))) {
return "";
}
return "";
}
@Override
public String getContent() {
String content = et_input.getText().toString().trim().replaceAll("\n", "");
return content;
}
public String provideCheckBox() {
String checked = "";
if (cb_todo_state.isChecked()) {
checked = "checked";
}
String regix = "<p><form><input type=\"checkbox\" disabled %s>%s</form></p>";
return String.format(regix, checked, et_input.getText().toString());
}
@Override
int provideResId() {
return R.layout.note_input_todo;
}
@Override
public EditText getEditText() {
return et_input;
}
public boolean hasDone() {
return cb_todo_state.isChecked();
}
@Override
public void showTodo() {
et_input.setHint("待辦事項");
cb_todo_state.setVisibility(View.VISIBLE);
//執行樣式清除
setTodo(true);
}
@Override
public void hideTodo() {
cb_todo_state.setVisibility(View.GONE);
et_input.setHint("");
uncheckStyle();
setTodo(false);
}
}
內容比較簡單,初始化控件、添加CheckBox 點擊監聽,提供了切換CheckBox顯示方法
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/cb_todo_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:padding="5dp"
android:visibility="gone" />
<EditText
android:id="@+id/et_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:cursorVisible="true"
android:textColor="#333333"
android:textCursorDrawable="@drawable/cursor_drawable"
android:textSize="16dp" />
</LinearLayout>
LinearLayout 包裹CheckBox 和 EditText ,CheckBox 默認隱藏,當切換為 待辦事項時,顯示CheckBox
ImageWeight
/**
* Created by scwen on 2019/4/18.
* QQ :811733738
* 作用: 圖片區域
*/
public class ImageWeight extends InputWeight implements View.OnClickListener {
private ImageView iv_input_image; //圖片
private LinearLayout ll_bottom_tools; //底部控件
private RelativeLayout rl_delete; //刪除
private RelativeLayout rl_replace; //替換
private RelativeLayout rl_full; //全屏
private String path; //圖片 手機路徑
private String shortPath; //圖片上傳服務器 短路徑
public String getShortPath() {
return shortPath == null ? "" : shortPath;
}
public void setShortPath(String shortPath) {
this.shortPath = shortPath;
}
public String getPath() {
return path == null ? "" : path;
}
public void setPath(String path) {
this.path = path;
}
public void replacePath(String path) {
this.path = path;
loadImage(path);
}
public ImageWeight(Context context, ViewGroup parent, String path) {
super(context, parent);
this.path = path;
loadImage(path);
}
public void loadImage(String path) {
//Glide 加載圖片
RequestOptions options = new RequestOptions();
options.placeholder(R.drawable.big_image_placeholder)
.sizeMultiplier(0.5f)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.transform(new EditerTranform(mContext, 45));
Glide.with(mContext)
.load(path)
.apply(options)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
//記載圖片完成后 設置控件的 高度
ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
int minimumHeight = resource.getMinimumHeight();
layoutParams.height = minimumHeight;
return false;
}
})
.into(iv_input_image);
}
private ImageActionListener mImageActionListener;
public void setImageActionListener(ImageActionListener imageActionListener) {
mImageActionListener = imageActionListener;
}
@Override
public String getHtml() {
return provideHtml(shortPath);
}
public String provideHtml(String path) {
return String.format("<div class=\"image\"><img src=\"%s\"></img></div>", path);
}
@Override
int provideResId() {
return R.layout.note_input_image;
}
@Override
public String getContent() {
return "";
}
@Override
public EditText getEditText() {
return null;
}
private void initListener() {
iv_input_image.setOnClickListener(this);
rl_delete.setOnClickListener(this);
rl_replace.setOnClickListener(this);
rl_full.setOnClickListener(this);
}
@Override
public void initView() {
iv_input_image = mContentView.findViewById(R.id.iv_input_image);
ll_bottom_tools = mContentView.findViewById(R.id.ll_bottom_tools);
rl_delete = mContentView.findViewById(R.id.rl_delete);
rl_replace = mContentView.findViewById(R.id.rl_replace);
rl_full = mContentView.findViewById(R.id.rl_full);
initListener();
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.iv_input_image) {
//點擊圖片 顯示下方 按鈕區域
ll_bottom_tools.setVisibility(ll_bottom_tools.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
} else if (v.getId() == R.id.rl_delete) {
//觸發 刪除圖片監聽
if (mImageActionListener != null) {
mImageActionListener.onAction(ImageActionListener.ACT_DELETE, this);
}
} else if (v.getId() == R.id.rl_replace) {
//觸發替換圖片監聽
if (mImageActionListener != null) {
mImageActionListener.onAction(ImageActionListener.ACT_REPLACE, this);
}
} else if (v.getId() == R.id.rl_full) {
//觸發預覽圖片監聽
if (mImageActionListener != null) {
mImageActionListener.onAction(ImageActionListener.ACT_PREVIEW, this);
}
}
}
}
ImageActionListener
/**
* Created by scwen on 2019/4/23.
* QQ :811733738
* 作用:圖片操作監聽
*/
public interface ImageActionListener {
/**
* 刪除圖片
*/
int ACT_DELETE=0;
/**
* 替換圖片
*/
int ACT_REPLACE=1;
/**
* 預覽圖片
*/
int ACT_PREVIEW=2;
void onAction(int action, ImageWeight imageWeight);
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/iv_input_image"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/ll_bottom_tools"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_alignParentBottom="true"
android:background="#66000000"
android:orientation="horizontal"
android:visibility="gone">
<RelativeLayout
android:id="@+id/rl_delete"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:drawableLeft="@drawable/ic_delete_black_24dp"
android:drawablePadding="5dp"
android:gravity="center"
android:text="@string/delete"
android:textColor="#fff"
android:textSize="12dp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_replace"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:drawableLeft="@drawable/ic_image_white_24dp"
android:drawablePadding="5dp"
android:gravity="center"
android:text="@string/replace"
android:textColor="#fff"
android:textSize="12dp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_full"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:gravity="center"
android:src="@drawable/ic_fullscreen_black_24dp"
android:text="@string/delete" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
RelativeLayout包裹內容區域,ImageView 控件自適應高度,底部包含3個點擊區域
創建控件測試
創建Editor1控件,繼承自LinearLayout,并且設置 當前的方向為VERTICAL
提供創建ImageWeight 和TodoWeight方法添加到控件中
public class Editor1 extends LinearLayout {
/**
* 輸入控件的集合
*/
private List<InputWeight> inputWeights = new ArrayList<>();
public Editor1(Context context) {
this(context, null);
}
public Editor1(Context context, @Nullable AttributeSet attrs) {
this(context, null, 0);
}
public Editor1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始胡
*
* @param context
*/
private void init(Context context) {
//設置當前控件的方向為 VERTICAL
setOrientation(VERTICAL);
//默認需要創建 TodoWeight
TodoWeight todoWeight = addTodoWeight();
//默認第一個控件需要 Hint
todoWeight.getEditText().setHint(R.string.input_content);
}
/**
* 添加 EditText 控件
*
* @return
*/
public TodoWeight addTodoWeight() {
TodoWeight todoWeight = new TodoWeight(getContext(), this, null);
inputWeights.add(todoWeight);
addView(todoWeight.getContentView());
return todoWeight;
}
/**
* 添加Image 控件
* @return
*/
public ImageWeight addImageWeight() {
ImageWeight imageWeight = new ImageWeight(getContext(), this, null);
inputWeights.add(imageWeight);
addView(imageWeight.getContentView());
return imageWeight;
}
}
Activity測試
Activity 中創建兩個Button 測試添加輸入區域
btn_add_edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TodoWeight todoWeight = editor1.addTodoWeight();
//測試顯示 CheckBox
todoWeight.showTodo();
}
});
btn_add_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
editor1.addImageWeight();
}
});
測試界面效果如下:
焦點EditText記錄
當前的編輯器已經添加了多個InputWeight,現在的問題在于需要記錄當前編輯的EditText,在應用樣式的時候定位到輸入的控件,在編輯器中添加如下變量:
private EditText lastFocusEdit; //當前正在編輯的EditText
如何監聽當前的輸入控件呢,這就用到了OnFocusChangeListener
private OnFocusChangeListener focusListener; // 所有EditText的焦點監聽listener
在init方法中,創建對象
focusListener = new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
lastFocusEdit = (EditText) v;
}
}
};
改造剛剛的 addTodoWeight方法
/**
* 添加 EditText 控件
*
* @return
*/
public TodoWeight addTodoWeight() {
TodoWeight todoWeight = new TodoWeight(getContext(), this, null);
inputWeights.add(todoWeight);
//
todoWeight.getEditText().setOnFocusChangeListener(focusListener);
todoWeight.getEditText().requestFocus();
addView(todoWeight.getContentView());
return todoWeight;
}
注意上面的代碼 ,一定要先setOnFocusChangeListener(focusListener) 再 requestFocus
圖片插入
上方測試的插入圖片功能只是最簡單的在最末尾加入圖片控件,現在要增加在輸入的文本中間插入功能
分為四種情況
- 當前焦點EditText 內容為空
- 當前輸入光標在EditText已輸入內容最前端
- 當前輸入光標在EditText已輸入內容最末端
- 當前輸入光標在EditText已輸入內容中間
判斷四種情況代碼:
public ImageWeight insertImage() {
//lastFocusEdit獲取焦點的EditText
Editable preContent = lastFocusEdit.getText();
//獲取控件位置
int lastEditIndex = indexOfChild((View) lastFocusEdit.getParent());
ImageWeight imageWeight = null;
if (preContent.length() == 0) {
//當前焦點EditText 內容為空
} else {
//獲取光標所在位置
int cursorIndex = lastFocusEdit.getSelectionStart();
//獲取光標前面的 內容
CharSequence start = preContent.subSequence(0, cursorIndex);
//獲取光標后面內容
CharSequence end = preContent.subSequence(cursorIndex, preContent.length());
if (start.length() == 0) {
//如果光標已經頂在了editText的最前面
} else if (end.length() == 0) {
// 如果光標已經頂在了editText的最末端
} else {
//如果光標已經頂在了editText的最中間,
}
}
return imageWeight;
}
針對以上四種情況的處理
- 直接在EditText下方插入圖片,插入新的EditText
- 直接在EditText下方插入圖片,并且插入新的EditText
- 則需要添加新的imageView和EditText
- 則需要分割字符串,分割成兩個EditText,并在兩個EditText中間插入圖片
需要在指定位置插入TodoWeight 和ImageWeight,增加addTodoWeightAtIndex 和 addImageWeightAtIndex方法
最終改造的代碼如下:
public ImageWeight insertImage(String path) {
//lastFocusEdit獲取焦點的EditText
Editable preContent = lastFocusEdit.getText();
//獲取控件位置
int lastEditIndex = indexOfChild((View) lastFocusEdit.getParent());
ImageWeight imageWeight = null;
if (preContent.length() == 0) {
//如果當前獲取焦點的EditText為空,直接在EditText下方插入圖片,并且插入空的EditText
addTodoWeightAtIndex(lastEditIndex + 1, "");
imageWeight = addImageWeightAtIndex(lastEditIndex + 1, path);
} else {
//獲取光標所在位置
int cursorIndex = lastFocusEdit.getSelectionStart();
//獲取光標前面的 內容
CharSequence start = preContent.subSequence(0, cursorIndex);
//獲取光標后面內容
CharSequence end = preContent.subSequence(cursorIndex, preContent.length());
if (start.length() == 0) {
//如果光標已經頂在了editText的最前面,則直接插入圖片,并且EditText下移即可
imageWeight = addImageWeightAtIndex(lastEditIndex, path);
//同時插入一個空的EditText,防止插入多張圖片無法寫文字
addTodoWeightAtIndex(lastEditIndex + 1, "");
} else if (end.length() == 0) {
// 如果光標已經頂在了editText的最末端,則需要添加新的imageView和EditText
addTodoWeightAtIndex(lastEditIndex + 1, "");
imageWeight = addImageWeightAtIndex(lastEditIndex + 1, path);
} else {
//如果光標已經頂在了editText的最中間,則需要分割字符串,分割成兩個EditText,并在兩個EditText中間插入圖片
//把光標前面的字符串保留,設置給當前獲得焦點的EditText(此為分割出來的第一個EditText)
lastFocusEdit.setText(start);
//把光標后面的字符串放在新創建的EditText中(此為分割出來的第二個EditText)
addTodoWeightAtIndex(lastEditIndex + 1, end);
//在第二個EditText的位置插入一個空的EditText,以便連續插入多張圖片時,有空間寫文字,第二個EditText下移
addTodoWeightAtIndex(lastEditIndex + 1, "");
//在空的EditText的位置插入圖片布局,空的EditText下移
imageWeight = addImageWeightAtIndex(lastEditIndex + 1, path);
}
}
return imageWeight;
}
public TodoWeight addTodoWeightAtIndex(int index, CharSequence sequence) {
TodoWeight todoWeight = new TodoWeight(getContext(), this, focusListener);
inputWeights.add(index, todoWeight);
//設置 顯示的內容
if (sequence != null && sequence.length() > 0) {
todoWeight.getEditText().setText(sequence);
}
todoWeight.getEditText().setOnFocusChangeListener(focusListener);
addView(todoWeight.getContentView(), index);
lastFocusEdit = todoWeight.getEditText();
lastFocusEdit.requestFocus();
lastFocusEdit.setSelection(sequence.length(), sequence.length());
return todoWeight;
}
public ImageWeight addImageWeightAtIndex(int index, String path) {
ImageWeight imageWeight = new ImageWeight(getContext(), this, path);
inputWeights.add(index, imageWeight);
addView(imageWeight.getContentView(), index);
return imageWeight;
}
刪除鍵處理
到此已經能夠創建 輸入控件和圖片控件了 ,可以隨意的添加圖片了。
現在我們開始處理下一個問題:刪除
- 監聽刪除鍵的點擊
- 當光標在EditText 輸入中間,點擊刪除不進行處理正常刪除
- 當光標在EditText首端,判斷前一個控件,如果是圖片控件,刪除圖片控件,如果是輸入控件,刪除當前控件并將輸入區域合并成一個輸入區域
監聽刪除鍵
private OnKeyListener keyListener; //按鍵監聽
// 主要用來處理點擊回刪按鈕時,view合并操作
keyListener = new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
onBackspacePress((EditText) v);
return false;
}
}
return false;
}
};
修改 addTodoWeight方法
public TodoWeight addTodoWeightAtIndex(int index, CharSequence sequence) {
TodoWeight todoWeight = new TodoWeight(getContext(), this, focusListener);
inputWeights.add(index, todoWeight);
if (sequence != null && sequence.length() > 0) {
todoWeight.getEditText().setText(sequence);
}
//添加 鍵盤監聽
todoWeight.getEditText().setOnKeyListener(keyListener);
todoWeight.getEditText().setOnFocusChangeListener(focusListener);
addView(todoWeight.getContentView(), index);
lastFocusEdit = todoWeight.getEditText();
lastFocusEdit.requestFocus();
lastFocusEdit.setSelection(sequence.length(), sequence.length());
return todoWeight;
}
處理刪除鍵的代碼:
private void onBackspacePress(EditText editText) {
int selectionStart = editText.getSelectionStart();
//只有光標在 edit 區域的 最前方 判斷 上一個 控件的類型
if (selectionStart == 0) {
int editIndex = indexOfChild((View) editText.getParent());
//第一個控件 直接 返回
if (editIndex == 0) {
return;
}
//獲取前一個 輸入控件
InputWeight baseInputWeight = inputWeights.get(editIndex - 1);
//執行類型檢查
if (baseInputWeight instanceof ImageWeight) {
//前一個 控件是 圖片 控件 直接刪除
removeWeight(baseInputWeight);
} else if (baseInputWeight instanceof TodoWeight) {
//前一個控件是 edittext 進行 樣式的合并
//獲取當前輸入的 文本
Editable currContent = editText.getText();
//獲取 前一個輸入控件
EditText preEdit = baseInputWeight.getEditText();
//獲取前一個控件的 內容
Editable preEditContent = preEdit.getText();
//-----------------------
removeWeight(inputWeights.get(editIndex));
//將當前 輸入內容 添加到 前一個控件中
preEditContent.insert(preEditContent.length(), currContent);
//移動光標
preEdit.setSelection(preEditContent.length(), preEditContent.length());
//獲取焦點
preEdit.requestFocus();
lastFocusEdit = preEdit;
}
}
}
/**
* 移除控件
* @param inputWeight
*/
public void removeWeight(InputWeight inputWeight) {
removeView(inputWeight.getContentView());
inputWeights.remove(inputWeight);
}
小結
本篇文章作為整個系列的第一篇,展示了編輯器的基本功能,分析了界面的組成,并且結合代碼完成了創建輸入控件、添加控件、插入圖片控件、輸入控件刪除鍵功能。
項目地址RichEditor
下一篇預告:
Span實現基礎樣式 粗體 斜體 下劃線 中劃線 字體大小 對齊方式(左對齊 居中 右對齊)