美文网首页Android开发学习
带插入图片功能的EditText

带插入图片功能的EditText

作者: 哥只是个小白 | 来源:发表于2017-11-08 15:55 被阅读0次

    效果图:

    image

    我们来看看怎么实现,首先我们肯定是在EditText上进行扩展肯定是继承于EditText。我们用到的最主要的两个类是SpannableString和ImageSpan,通过这两个类,我们就能实现图片和文字共存,一般适用的场景是论坛或者帖子的发表或者提交

    具体实现:

    我们首先要用SpannableString来编辑要插入的图片内容

    /**
        * 编辑插入的内容
        *
        * @param picPath
        * @return
        */
       private CharSequence getDrawableStr(String picPath) {
           String str = "<img src=\"" + picPath + "\"/>";
           Bitmap bm = createImageThumbnail(picPath);
           final SpannableString ss = new SpannableString(str);
           // 定义插入图片
           Drawable drawable = new BitmapDrawable(bm);
    
           float scenewidth = Util.getScene(Util.SCENE_WIDTH) / 3;
           float width = drawable.getIntrinsicWidth();
           float height = drawable.getIntrinsicHeight();
           if (width > scenewidth) {
               width = width - 20;
               height = height - 20;
           } else {
               float scale = (scenewidth) / width;
               width *= scale;
               height *= scale;
           }
    
           //设置图片的宽高
           drawable.setBounds(2, 0, (int) width, (int) height);
           //ALIGN_BOTTOM 调整图片距离字有一定的间隙
           VerticalCenterImageSpan span = new VerticalCenterImageSpan(drawable, 1);
           //SPAN_INCLUSIVE_EXCLUSIVE 会导致删除后面的文字消失
           ss.setSpan(span, 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
           /*
           Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括,即在指定范围的前面和后面插入新字符都不会应用新样式
           Spannable.SPAN_EXCLUSIVE_INCLUSIVE:前面不包括,后面包括。即仅在范围字符的后面插入新字符时会应用新样式
           Spannable.SPAN_INCLUSIVE_EXCLUSIVE:前面包括,后面不包括。
           Spannable.SPAN_INCLUSIVE_INCLUSIVE:前后都包括。
            */
           return ss;
       }
    

    其中需要插入创建的图片,我把创建图片的代码单独拉出来了

    /**
      * 创建图片
      * @param filePath
      * @return
      */
     public static Bitmap createImageThumbnail(String filePath) {
         Bitmap bitmap = null;
         BitmapFactory.Options opts = new BitmapFactory.Options();
         opts.inTempStorage = new byte[100 * 1024];
         // 默认是Bitmap.Config.ARGB_8888
         opts.inPreferredConfig = Bitmap.Config.RGB_565;
         opts.inSampleSize = 2;
         try {
             bitmap = BitmapFactory.decodeFile(filePath, opts);
         } catch (Exception e) {
         }
         return bitmap;
     }
    

    然后使用自定义的ImageSpan来调整图片的位置

    public class VerticalCenterImageSpan extends ImageSpan {
    
          public VerticalCenterImageSpan(Drawable d, int verticalAlignment) {
              super(d, verticalAlignment);
          }
    
          @Override
          public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
              Drawable b = getDrawable();
              canvas.save();
    
              int transY = bottom - b.getBounds().bottom;
              if (mVerticalAlignment == ALIGN_BASELINE) {
                  transY -= paint.getFontMetricsInt().descent;
              } else if (mVerticalAlignment == ALIGN_BOTTOM) {
    
              } else {
                  transY += paint.getFontMetricsInt().descent * 2;
              }
    
              canvas.translate(x, transY);
              b.draw(canvas);
              canvas.restore();
          }
      }
    

    最后通过EditText的Editable的insert方法把编辑好的图片插入到内容当中

    Editable editable = getText();
    CharSequence sequence = getDrawableStr(picPath);
    editable.insert(getSelectionStart(), sequence);
    

    这样就能把图片插入到内容当中了,实现图片和文字共存

    获取插入的图片集合

    调用getImage()方法就能获取到插入的图片的集合


    image

    具体的实现就是把插入进来的图片存到一个集合当中,然后监听删除和插入的内容是否是图片,然后做对应的操作,具体的实现可以看最下面的全部代码。

    全部代码

    package com.example.yinshuai.imageeditext.view;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.Drawable;
    import android.text.Editable;
    import android.text.Spannable;
    import android.text.SpannableString;
    import android.text.TextUtils;
    import android.text.TextWatcher;
    import android.text.style.ImageSpan;
    import android.util.AttributeSet;
    import android.view.Gravity;
    import android.view.MotionEvent;
    import android.widget.EditText;
    import android.widget.Toast;
    
    import com.example.yinshuai.imageeditext.util.Util;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
    * Created by yinshuai on 2017/8/14.
    *
    * @author yinshuai
    */
    public class EditTextPlus extends EditText {
      /**
       * 最大输入字符
       */
      public static final int MAXLENGTH = 2000;
    
      /**
       * 一张图片所占的字符长度
       */
      public static final int IMAGELENGTH = 2;
      /**
       * 占位符
       */
      private String placeholder = "&";
    
      /**
       * 最大添加图片数量
       */
      private int maxImage = 8;
      private Context mContext;
    
      private String submitCon = "";
      private boolean insertImage = false;
    
      private OnInsertionImageListener onInsertionImageListener;
      private OnDeleteConteneListener onDeleteConteneListener;
    
      private float startY;
      private float startX;
      private float selectionStart;
    
      private List<String> image = new ArrayList<>();
    
      public EditTextPlus(Context context) {
          super(context);
          mContext = context;
          init();
      }
    
      public EditTextPlus(Context context, AttributeSet attrs) {
          super(context, attrs);
          mContext = context;
          init();
      }
    
      public void init() {
          setGravity(Gravity.TOP);
          addTextChangedListener(watcher);
      }
    
      public interface OnInsertionImageListener {
          /**
           * 插入图片时的监听
           */
          void insertion();
      }
    
      public void setOnInsertionImageListener(OnInsertionImageListener onInsertionImageListener) {
          this.onInsertionImageListener = onInsertionImageListener;
      }
    
      public interface OnDeleteConteneListener {
          /**
           * 删除图片的监听
           */
          void delete();
      }
    
      public void setOnDeleteConteneListener(OnDeleteConteneListener onDeleteConteneListener) {
          this.onDeleteConteneListener = onDeleteConteneListener;
      }
    
    
      /**
       * 添加图片集合
       *
       * @param list
       */
      public void addImage(List<String> list) {
          if (getTextContent().length() + IMAGELENGTH > MAXLENGTH) {
              Toast.makeText(mContext, "输入的内容超过最大限制", Toast.LENGTH_SHORT).show();
              return;
          }
          Editable editable = getText();
          for (int i = 0; i < list.size(); i++) {
              if (getImage().size() >= maxImage) {
                  Toast.makeText(mContext, "图片超过最大数量", Toast.LENGTH_SHORT).show();
                  return;
              }
              if (list.get(i) != null && !TextUtils.isEmpty(list.get(i))) {
                  if (!TextUtils.isEmpty(getText().toString()) && !insertImage) {
                      //如果第一张就是图片不用换行
                      editable.insert(getSelectionStart(), "\n");
                  } else if (getSelectionStart() < getText().length()) {
                      //当从中间插入时
                      editable.insert(getSelectionStart(), "\n");
                  }
                  CharSequence sequence = getDrawableStr(list.get(i));
                  if (sequence != null) {
                      image.add(list.get(i));
                      editable.insert(getSelectionStart(), sequence);
                      editable.insert(getSelectionStart(), "\n");
                      insertImage = true;
                  }
              } else {
                  Toast.makeText(mContext, "图片路径为空", Toast.LENGTH_SHORT).show();
              }
          }
          //让光标始终在最后
          this.setSelection(getText().toString().length());
          if (onInsertionImageListener != null) {
              onInsertionImageListener.insertion();
          }
      }
    
    
      /**
       * 获取插入的图片列表
       *
       * @return
       */
      public List<String> getImage() {
          List<String> picPaths = new ArrayList<>();
          String content = this.getText().toString();
          for (int i = 0; i < image.size(); i++) {
              if (content.indexOf(image.get(i)) != -1) {
                  picPaths.add(image.get(i));
              }
          }
          return picPaths;
      }
    
      /**
       * 判断传进来的字符串是否是一个图片地址
       * @param content
       * @return
       */
      public boolean isImage(String content) {
          for (int i = 0; i < image.size(); i++) {
              if (content.indexOf(image.get(i)) != -1) {
                  return true;
              }
          }
          return false;
      }
    
      /**
       * 获取去除image后的文字内容
       *
       * @return
       */
      public String getTextContent() {
          return submitCon;
      }
    
    
      /**
       * 这个TextWatcher用来监听删除和输入的内容如果是图片的话 要相应把list集合中的图片也要移除 不然最后获取到的图片集合是错误的
       */
      private String tempString;
      private TextWatcher watcher = new TextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
              insertImage = false;
              //如果小于就是删除操作
              if (s.length() < tempString.length()) {
                  String deletString = tempString.substring(start, start + before);
                  if (image != null && image.size() > 0) {
                      for (int i = 0; i < image.size(); i++) {
                          //如果删除的内容中包含这张图片 那么就把图片集合中的对应的图片删除
                          if (deletString.toString().indexOf(image.get(i)) != -1) {
                              image.remove(i);
                              if (onDeleteConteneListener != null) {
                                  onDeleteConteneListener.delete();
                              }
                          }
                      }
                  }
              }
          }
    
          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
              tempString = s.toString();
          }
    
          @Override
          public void afterTextChanged(Editable s) {
              invalidate();
              requestLayout();
    
              StringBuffer stringBuffer = new StringBuffer(getText().toString());
              for (int i = 0; i < image.size(); i++) {
                  if (stringBuffer.indexOf(image.get(i)) != -1) {
                      int index = stringBuffer.indexOf(image.get(i));
                      stringBuffer.delete(index - 10, index + image.get(i).length() + 3);
                      stringBuffer.insert(index - 10, placeholder);
                  }
              }
    
              if (stringBuffer.toString().indexOf(placeholder) == 0) {
                  stringBuffer.insert(0, " ");
              }
              submitCon = stringBuffer.toString();
          }
    
    
      };
    
      /**
       * 编辑插入的内容
       *
       * @param picPath
       * @return
       */
      private CharSequence getDrawableStr(String picPath) {
          String str = "<img src=\"" + picPath + "\"/>";
          Bitmap bm = createImageThumbnail(picPath);
          final SpannableString ss = new SpannableString(str);
          // 定义插入图片
          Drawable drawable = new BitmapDrawable(bm);
    
          float scenewidth = Util.getScene(Util.SCENE_WIDTH) / 3;
          float width = drawable.getIntrinsicWidth();
          float height = drawable.getIntrinsicHeight();
          if (width > scenewidth) {
              width = width - 20;
              height = height - 20;
          } else {
              float scale = (scenewidth) / width;
              width *= scale;
              height *= scale;
          }
    
          //设置图片的宽高
          drawable.setBounds(2, 0, (int) width, (int) height);
          //ALIGN_BOTTOM 调整图片距离字有一定的间隙
          VerticalCenterImageSpan span = new VerticalCenterImageSpan(drawable, 1);
          //SPAN_INCLUSIVE_EXCLUSIVE 会导致删除后面的文字消失
          ss.setSpan(span, 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
          /*
          Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括,即在指定范围的前面和后面插入新字符都不会应用新样式
          Spannable.SPAN_EXCLUSIVE_INCLUSIVE:前面不包括,后面包括。即仅在范围字符的后面插入新字符时会应用新样式
          Spannable.SPAN_INCLUSIVE_EXCLUSIVE:前面包括,后面不包括。
          Spannable.SPAN_INCLUSIVE_INCLUSIVE:前后都包括。
           */
          return ss;
      }
    
    
      /**
       * 自定义ImageSpan 来调整图片的位置
       */
      public class VerticalCenterImageSpan extends ImageSpan {
    
          public VerticalCenterImageSpan(Drawable d, int verticalAlignment) {
              super(d, verticalAlignment);
          }
    
          @Override
          public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
              Drawable b = getDrawable();
              canvas.save();
    
              int transY = bottom - b.getBounds().bottom;
              if (mVerticalAlignment == ALIGN_BASELINE) {
                  transY -= paint.getFontMetricsInt().descent;
              } else if (mVerticalAlignment == ALIGN_BOTTOM) {
    
              } else {
                  transY += paint.getFontMetricsInt().descent * 2;
              }
    
              canvas.translate(x, transY);
              b.draw(canvas);
              canvas.restore();
          }
      }
    
    
      /**
       * 创建图片
       * @param filePath
       * @return
       */
      public static Bitmap createImageThumbnail(String filePath) {
          Bitmap bitmap = null;
          BitmapFactory.Options opts = new BitmapFactory.Options();
          opts.inTempStorage = new byte[100 * 1024];
          // 默认是Bitmap.Config.ARGB_8888
          opts.inPreferredConfig = Bitmap.Config.RGB_565;
          opts.inSampleSize = 2;
          try {
              bitmap = BitmapFactory.decodeFile(filePath, opts);
          } catch (Exception e) {
          }
          return bitmap;
      }
    
    
      /**
       * 重写dispatchTouchEvent是为了解决上下滑动时光标跳跃的问题
       *
       * @param event
       * @return
       */
      @Override
      public boolean dispatchTouchEvent(MotionEvent event) {
          switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN:
                  startY = event.getRawY();
                  startX = event.getRawX();
                  selectionStart = getSelectionStart();
                  break;
              case MotionEvent.ACTION_MOVE:
                  break;
              case MotionEvent.ACTION_UP:
                  float endY = event.getRawY();
                  float endX = event.getRawX();
    
                  if (Math.abs(endY - startY) > 10 || Math.abs(endX - startX) > 10) {
                      return true;
                  }
                  break;
              default:
                  break;
          }
          return super.dispatchTouchEvent(event);
      }
    }
    

    Github项目地址:ImageEditText
    个人博客:小白的博客

    相关文章

      网友评论

        本文标题:带插入图片功能的EditText

        本文链接:https://www.haomeiwen.com/subject/egkymxtx.html