本文主要是介绍Android 自定义可编辑图文混排EditText,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
深知各位看官品性,所以先上图,然后再听我吹《我和图文混排不得不说的故事》系列之----没有故事!(为了死气沉沉的代码更有生气,我给各位看官下载的美女)
各位同道,时隔半年,我胡汉三又回来了!不是我不想来,实在是。。人在江湖,身不由己啊!总结一句话,一入码门深如海,从此节操是路人!(不发图就感觉无法表达我此刻丰富的内心)
这半年来,更证实了一句话:需求是永无止境的。在我们迈向成功的路上,需求才是最大的绊脚石。没有需求,我们就自由了!(失业了)。
如果要问我为什么这个界面这么丑,我只好说:因为小学,中学,高中的美术课都被语数外给占用了。。。
发几句牢骚,当真你就输了,哈哈。
正文:
现在对于大多数APP来说,社区模块已经习以为常了。在发表言论时,发图功能也变得非常常见(比如和我一样内心复杂的你们)。实现类似的功能,除了富文本,还有动态布局listview,这里讲的是另外一种,自定义EditText。(就是在EditText基础上改吧改吧)
一贯的宗旨:知其然必知其所以然!
Google 为我们提供了 ImageSpan 类,专门应对在文本中插入图片。该类构造方法比较多,主要针对Bitmap 和 Drawable ,也可以根据资源ID直接加载图片。本文中用到的构造函数为:

- public ImageSpan(Context context, Bitmap b)

- Editable edit_text = getEditableText();
- int index = getSelectionStart(); // 获取光标所在位置
- //插入换行符,使图片单独占一行
- SpannableString newLine = new SpannableString("\n");
- edit_text.insert(index, newLine);
- // 创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像
- path = mBitmapTag + path + mBitmapTag;
- SpannableString spannableString = new SpannableString(path);
- // 根据Bitmap对象创建ImageSpan对象
- ImageSpan imageSpan = new ImageSpan(mContext, bitmap);
- // 用ImageSpan对象替换你指定的字符串
- spannableString.setSpan(imageSpan, 0, path.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- // 将选择的图片追加到EditText中光标所在位置
- if (index < 0 || index >= edit_text.length()) {
- edit_text.append(spannableString);
- } else {
- edit_text.insert(index, spannableString);
- }
通常的逻辑是在空白的编辑框里逐步的输入文字,插入图片等。。这里带大家反着顺序走一遍。
首先,假设我们有一个集合,里面存储着编辑框里的内容,包括文字和图片地址。这个时候,要做的步骤是:

- /**
- * 设置数据
- */
- private void insertData() {
- if (mContentList.size() > 0) {
- for (String str : mContentList) {
- if (str.indexOf(mBitmapTag) != -1) {//判断是否是图片地址
- String path = str.replace(mBitmapTag, "");//还原地址字符串
- Bitmap bitmap = getSmallBitmap(path, 480, 800);
- //插入图片
- insertBitmap(path, bitmap);
- } else {
- //插入文字
- SpannableString ss = new SpannableString(str);
- append(ss);
- }
- }
- }
- }

- /**
- * 插入图片
- *
- * @param bitmap
- * @param path
- * @return
- */
- private SpannableString insertBitmap(String path, Bitmap bitmap) {
- Editable edit_text = getEditableText();
- int index = getSelectionStart(); // 获取光标所在位置
- //插入换行符,使图片单独占一行
- SpannableString newLine = new SpannableString("\n");
- edit_text.insert(index, newLine);//插入图片前换行
- // 创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像
- path = mBitmapTag + path + mBitmapTag;
- SpannableString spannableString = new SpannableString(path);
- // 根据Bitmap对象创建ImageSpan对象
- ImageSpan imageSpan = new ImageSpan(mContext, bitmap);
- // 用ImageSpan对象替换你指定的字符串
- spannableString.setSpan(imageSpan, 0, path.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- // 将选择的图片追加到EditText中光标所在位置
- if (index < 0 || index >= edit_text.length()) {
- edit_text.append(spannableString);
- } else {
- edit_text.insert(index, spannableString);
- }
- edit_text.insert(index, newLine);//插入图片后换行
- return spannableString;
- }
看代码:

- /**
- * 插入图片
- *
- * @param path
- */
- public void insertBitmap(String path) {
- Bitmap bitmap = getSmallBitmap(path, 480, 800);
- insertBitmap(path, bitmap);
- }

- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- oldY = event.getY();
- requestFocus();
- break;
- case MotionEvent.ACTION_MOVE:
- float newY = event.getY();
- if (Math.abs(oldY - newY) > 20) {
- clearFocus();
- }
- break;
- case MotionEvent.ACTION_UP:
- break;
- default:
- break;
- }
- return super.onTouchEvent(event);
- }
很简单,复写了onTouchEvent方法,在不同的状态时,申请是否获取焦点。。这里的设置是按下时获取焦点,移动距离超过20时,取消焦点。好了,相信大家已经都明白了,万事俱备,只欠东风。。贴出全部代码。
看我这高大上的控件名字。

- package com.example.zhipeng.phototexteditordemo;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.text.Editable;
- import android.text.Spannable;
- import android.text.SpannableString;
- import android.text.style.ImageSpan;
- import android.util.AttributeSet;
- import android.util.DisplayMetrics;
- import android.view.MotionEvent;
- import android.widget.EditText;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * Created by zhipeng on 16/1/13.
- * 图文混排编辑器
- */
- public class PictureAndTextEditorView extends EditText {
- private final String TAG = "PATEditorView";
- private Context mContext;
- private List<String> mContentList;
- public static final String mBitmapTag = "☆";
- private String mNewLineTag = "\n";
- public PictureAndTextEditorView(Context context) {
- super(context);
- init(context);
- }
- public PictureAndTextEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
- public PictureAndTextEditorView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context);
- }
- private void init(Context context) {
- mContext = context;
- mContentList = getmContentList();
- insertData();
- }
- /**
- * 设置数据
- */
- private void insertData() {
- if (mContentList.size() > 0) {
- for (String str : mContentList) {
- if (str.indexOf(mBitmapTag) != -1) {//判断是否是图片地址
- String path = str.replace(mBitmapTag, "");//还原地址字符串
- Bitmap bitmap = getSmallBitmap(path, 480, 800);
- //插入图片
- insertBitmap(path, bitmap);
- } else {
- //插入文字
- SpannableString ss = new SpannableString(str);
- append(ss);
- }
- }
- }
- }
- /**
- * 插入图片
- *
- * @param bitmap
- * @param path
- * @return
- */
- private SpannableString insertBitmap(String path, Bitmap bitmap) {
- Editable edit_text = getEditableText();
- int index = getSelectionStart(); // 获取光标所在位置
- //插入换行符,使图片单独占一行
- SpannableString newLine = new SpannableString("\n");
- edit_text.insert(index, newLine);//插入图片前换行
- // 创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像
- path = mBitmapTag + path + mBitmapTag;
- SpannableString spannableString = new SpannableString(path);
- // 根据Bitmap对象创建ImageSpan对象
- ImageSpan imageSpan = new ImageSpan(mContext, bitmap);
- // 用ImageSpan对象替换你指定的字符串
- spannableString.setSpan(imageSpan, 0, path.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- // 将选择的图片追加到EditText中光标所在位置
- if (index < 0 || index >= edit_text.length()) {
- edit_text.append(spannableString);
- } else {
- edit_text.insert(index, spannableString);
- }
- edit_text.insert(index, newLine);//插入图片后换行
- return spannableString;
- }
- /**
- * 插入图片
- *
- * @param path
- */
- public void insertBitmap(String path) {
- Bitmap bitmap = getSmallBitmap(path, 480, 800);
- insertBitmap(path, bitmap);
- }
- /**
- * 用集合的形式获取控件里的内容
- *
- * @return
- */
- public List<String> getmContentList() {
- if (mContentList == null) {
- mContentList = new ArrayList<String>();
- }
- String content = getText().toString().replaceAll(mNewLineTag, "");
- if (content.length() > 0 && content.contains(mBitmapTag)) {
- String[] split = content.split("☆");
- mContentList.clear();
- for (String str : split) {
- mContentList.add(str);
- }
- } else {
- mContentList.add(content);
- }
- return mContentList;
- }
- /**
- * 设置显示的内容集合
- *
- * @param contentList
- */
- public void setmContentList(List<String> contentList) {
- if (mContentList == null) {
- mContentList = new ArrayList<>();
- }
- this.mContentList.clear();
- this.mContentList.addAll(contentList);
- insertData();
- }
- float oldY = 0;
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- oldY = event.getY();
- requestFocus();
- break;
- case MotionEvent.ACTION_MOVE:
- float newY = event.getY();
- if (Math.abs(oldY - newY) > 20) {
- clearFocus();
- }
- break;
- case MotionEvent.ACTION_UP:
- break;
- default:
- break;
- }
- return super.onTouchEvent(event);
- }
- // 根据路径获得图片并压缩,返回bitmap用于显示
- public Bitmap getSmallBitmap(String filePath, int reqWidth, int reqHeight) {
- final BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(filePath, options);
- // Calculate inSampleSize
- options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
- // Decode bitmap with inSampleSize set
- options.inJustDecodeBounds = false;
- Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
- DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
- int w_screen = dm.widthPixels;
- int w_width = w_screen;
- int b_width = bitmap.getWidth();
- int b_height = bitmap.getHeight();
- int w_height = w_width * b_height / b_width;
- bitmap = Bitmap.createScaledBitmap(bitmap, w_width, w_height, false);
- return bitmap;
- }
- //计算图片的缩放值
- public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
- final int height = options.outHeight;
- final int width = options.outWidth;
- int inSampleSize = 1;
- if (height > reqHeight || width > reqWidth) {
- final int heightRatio = Math.round((float) height / (float) reqHeight);
- final int widthRatio = Math.round((float) width / (float) reqWidth);
- inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
- }
- return inSampleSize;
- }
- }
EditActivity

- package com.example.zhipeng.phototexteditordemo;
- import android.app.Activity;
- import android.content.Intent;
- import android.net.Uri;
- import android.os.Bundle;
- import android.text.Editable;
- import android.text.TextWatcher;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * Created by zhipeng on 16/1/13.
- */
- public class EditActivity extends Activity {
- private PictureAndTextEditorView mEditText;
- private Button mButton;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_edit);
- mEditText = (PictureAndTextEditorView) findViewById(R.id.edit_text);
- mButton = (Button) findViewById(R.id.button_add_picture);
- ///storage/emulated/0/Mob/cn.xfz.app/cache/images/1450915925_4457.jpg
- ///storage/emulated/0/Pictures/1450770237621.jpg
- ///storage/emulated/0/Pictures/1450769835187.jpg
- ///storage/emulated/0/Mob/cn.xfz.app/cache/images/1450684805_82970.jpg
- List<String> list = new ArrayList<>();//这里是测试用的,对于图片地址,各位还是要自己设置一下
- list.add("你说");
- list.add(PictureAndTextEditorView.mBitmapTag+"/storage/emulated/0/Mob/cn.xfz.app/cache/images/1450915925_4457.jpg");
- list.add("我在哪");
- list.add(PictureAndTextEditorView.mBitmapTag+"/storage/emulated/0/Pictures/1450770237621.jpg");
- list.add("不告诉你");
- list.add(PictureAndTextEditorView.mBitmapTag+"/storage/emulated/0/Pictures/1450769835187.jpg");
- list.add(PictureAndTextEditorView.mBitmapTag+"/storage/emulated/0/Mob/cn.xfz.app/cache/images/1450684805_82970.jpg");
- list.add("嘿嘿");
- list.add(PictureAndTextEditorView.mBitmapTag+"/storage/emulated/0/Mob/cn.xfz.app/cache/images/1450915925_4457.jpg");
- // mEditText.setmContentList(list);
- mButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- PicturePickUtils.selectPicFromLocal(EditActivity.this,888);//获取手机本地图片的代码,大家可以自行实现
- }
- });
- mEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- Log.i("EditActivity",mEditText.getmContentList().toString());
- }
- @Override
- public void afterTextChanged(Editable s) {
- }
- });
- }
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
- switch (requestCode) {
- case 888:
- if (data != null) {
- Uri selectedImage = data.getData();
- String imageurl = UriUtils.getImageAbsolutePath(this, selectedImage);
- mEditText.insertBitmap(imageurl);
- }
- default:
- break;
- }
- }
- }
- }
xml

- <?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="match_parent"
- android:background="#ffffff"
- android:orientation="vertical">
- <com.example.zhipeng.phototexteditordemo.PictureAndTextEditorView
- android:id="@+id/edit_text"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:scrollbars="vertical"
- android:scrollbarStyle="outsideOverlay"
- android:gravity="start"
- android:hint="132456"
- />
- <Button
- android:id="@+id/button_add_picture"
- android:layout_width="match_parent"
- android:layout_height="50dp"
- android:text="点我插图"
- android:layout_alignParentBottom="true"/>
- </LinearLayout>
这篇关于Android 自定义可编辑图文混排EditText的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!