Android开发学习

带插入图片功能的EditText

2017-11-08  本文已影响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
个人博客:小白的博客

上一篇下一篇

猜你喜欢

热点阅读