Android收藏集Android专题各种喜欢的

Android 端 (图文混排)富文本编辑器的开发(一)

2019-04-29  本文已影响174人  写代码的解先生

前段时间 需要做 富文本编辑笔记功能,要求能够插入图片、待办事项、无序列表、引用,能够修改字体大小、段落对齐方式、粗体、斜体、下划线、中划线。经过一段时间的努力完成了功能,现在对开发过程进行记录。实现效果如下图:

IMG_20190428_105644.jpg image

编辑器涉及到的功能点较多,所以将是一系列的文章。文章内容按照以下的关键点进行展开:

  1. 页面组成 分析
  2. Span实现基础样式 粗体 斜体 下划线 中划线
    3.字体大小 对齐方式(左对齐 居中 右对齐)
  3. 列表项 (多级列表)实现 以及样式的取消
  4. 引用项 实现以及样式的取消
  5. 行间距问题的处理(行高)
  6. 待办事项如何实现
  7. 图片如何插入 (todo ) 插入样式合并
  8. 图片等控件删除键 点击事件操作
  9. 各个样式 删除键与回退键 的处理
  10. 上传格式,生成HTML 样式片段
  11. Html 片段的解析 dom 解析 span的解析 (系统代码的修改)
  12. 关于长图生成

这篇文章先进行页面组成分析

界面构成

当时看到这个界面的时候一脸懵逼,整个界面的要求

根据界面要求作出以下分析

  1. 可插入图片、视频 界面不能用一个 EditText 来做,需要使用LinearLayout添加不同的控件
  2. 界面可滑动最外层使用ScrollView
  3. 可插入待办事项,单个编辑控件使用LinearLayout包裹
  4. 图片区域 包含可操作按钮,使用RelativeLayout进行包裹

最终实现的 布局结构如下图:

image

强烈建议在测试编辑器的时候 打开 开发者模式的显示布局边界

构建界面

经过以上分析,界面是由多个输入区域拼接而成,暂且把输入区域 称为 InputWeight

图片区域称为ImageWeight 可转为待办事项区域称为 TodoWeight

使用LinearLayout包含多个 InputWeight实现的难点:

  1. 记录当前的焦点区域
  2. 输入区域的删除键处理
  3. 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

提供创建ImageWeightTodoWeight方法添加到控件中

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

测试界面效果如下:

image

焦点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

图片插入

上方测试的插入图片功能只是最简单的在最末尾加入图片控件,现在要增加在输入的文本中间插入功能

分为四种情况

判断四种情况代码:

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

针对以上四种情况的处理

需要在指定位置插入TodoWeight 和ImageWeight,增加addTodoWeightAtIndexaddImageWeightAtIndex方法

最终改造的代码如下:

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

删除键处理

到此已经能够创建 输入控件和图片控件了 ,可以随意的添加图片了。

现在我们开始处理下一个问题:删除

监听删除键
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);
    }

小结

本篇文章作为整个系列的第一篇,展示了编辑器的基本功能,分析了界面的组成,并且结合代码完成了创建输入控件、添加控件、插入图片控件、输入控件删除键功能。

下一篇预告:
Span实现基础样式 粗体 斜体 下划线 中划线 字体大小 对齐方式(左对齐 居中 右对齐)

上一篇 下一篇

猜你喜欢

热点阅读