Android 自定义可编辑图文混排EditText
2016-01-14 19:31
375 查看
深知各位看官品性,所以先上图,然后再听我吹《我和图文混排不得不说的故事》系列之----没有故事!(为了死气沉沉的代码更有生气,我给各位看官下载的美女)
各位同道,时隔半年,我胡汉三又回来了!不是我不想来,实在是。。人在江湖,身不由己啊!总结一句话,一入码门深如海,从此节操是路人!
(不发图就感觉无法表达我此刻丰富的内心)
这半年来,更证实了一句话:需求是永无止境的。在我们迈向成功的路上,需求才是最大的绊脚石。没有需求,我们就自由了!(失业了)。
如果要问我为什么这个界面这么丑,我只好说:因为小学,中学,高中的美术课都被语数外给占用了。。。
发几句牢骚,当真你就输了,哈哈。
正文:
现在对于大多数APP来说,社区模块已经习以为常了。在发表言论时,发图功能也变得非常常见(比如和我一样内心复杂的你们)。实现类似的功能,除了富文本,还有动态布局listview,这里讲的是另外一种,自定义EditText。(就是在EditText基础上改吧改吧)
一贯的宗旨:知其然必知其所以然!
Google 为我们提供了 ImageSpan 类,专门应对在文本中插入图片。该类构造方法比较多,主要针对Bitmap 和 Drawable ,也可以根据资源ID直接加载图片。本文中用到的构造函数为:
好了,注释也写得很详细,核心代码就在这里。那么我宣布自定义图文混排EditText完成了。(当然不可能了)。还是有很多坑,需要带领大家一步一步淌过来的。
通常的逻辑是在空白的编辑框里逐步的输入文字,插入图片等。。这里带大家反着顺序走一遍。
首先,假设我们有一个集合,里面存储着编辑框里的内容,包括文字和图片地址。这个时候,要做的步骤是:
看代码:
很简单,复写了onTouchEvent方法,在不同的状态时,申请是否获取焦点。。这里的设置是按下时获取焦点,移动距离超过20时,取消焦点。好了,相信大家已经都明白了,万事俱备,只欠东风。。贴出全部代码。
看我这高大上的控件名字。
EditActivity
xml
转载请注明出处。。
各位同道,时隔半年,我胡汉三又回来了!不是我不想来,实在是。。人在江湖,身不由己啊!总结一句话,一入码门深如海,从此节操是路人!
(不发图就感觉无法表达我此刻丰富的内心)
这半年来,更证实了一句话:需求是永无止境的。在我们迈向成功的路上,需求才是最大的绊脚石。没有需求,我们就自由了!(失业了)。
如果要问我为什么这个界面这么丑,我只好说:因为小学,中学,高中的美术课都被语数外给占用了。。。
发几句牢骚,当真你就输了,哈哈。
正文:
现在对于大多数APP来说,社区模块已经习以为常了。在发表言论时,发图功能也变得非常常见(比如和我一样内心复杂的你们)。实现类似的功能,除了富文本,还有动态布局listview,这里讲的是另外一种,自定义EditText。(就是在EditText基础上改吧改吧)
一贯的宗旨:知其然必知其所以然!
Google 为我们提供了 ImageSpan 类,专门应对在文本中插入图片。该类构造方法比较多,主要针对Bitmap 和 Drawable ,也可以根据资源ID直接加载图片。本文中用到的构造函数为:
public ImageSpan(Context context, Bitmap b)ImageSpan 代表用来构建的样式是图片样式,那么既然有构建的样式,就会有使用样式的方法,即用什么引入样式。Google 提供了Spannable对象来引入各种样式。用法如下:
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); }
好了,注释也写得很详细,核心代码就在这里。那么我宣布自定义图文混排EditText完成了。(当然不可能了)。还是有很多坑,需要带领大家一步一步淌过来的。
通常的逻辑是在空白的编辑框里逐步的输入文字,插入图片等。。这里带大家反着顺序走一遍。
首先,假设我们有一个集合,里面存储着编辑框里的内容,包括文字和图片地址。这个时候,要做的步骤是:
/** * 设置数据 */ 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); } } } }设置数据,先判断集合中是否有数据,如果有,就设置进来,设置的时候,判断是否包含图片的标记,如果包含,那就去获取图片,然后通过ImageSpan设置进来。插入图片的方法为:
/** * 插入图片 * * @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); }哈哈,就是这么简单。到了这里,大体可以使用了。为什么说是大体可以用,因为在这里使用,会发现我们一次性插入了很多图,先不考虑内存溢出的问题,因为这是图片优化的事情,除此之外,我们面临的问题是自定义的EditText滑动不顺畅,换句话说就是,由于EditText时刻获取焦点,长图片的时候,会自动对焦点在图片底部。这就引起了无法顺畅滑动,或者干脆滑动不到上面或者下面的图片位置。无法精准定位的问题。怎么办?凉拌!代码来说话:
@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之DatePicker和TimePicker实现以及自定义大小
- Android Lint工具 优化代码 <12>
- android sqlite 不存在插入,存在更新语句
- Android性能优化典范(一)
- 从sd卡中显示一个图片 Android
- 【Android】Genymotion打开出现Unable to load VirtualBox engine
- android打开网页,使用手机浏览器
- 在Android studio 项目中使用 9patch常见问题解决
- Android 透明(沉浸)状态栏 各个版本的完全解决方案
- Android JSON解析库的使用
- android动画,透明度渐变,图片移动
- 【Android应用界面编程】-----ExpendableListView
- Android中的几种Toast
- android中Baseadapter的 getItem 和 getItemId 的作用和重写
- android状态栏颜色修改
- android允许模拟位置
- android中的Fragment
- Android状态栏颜色修改
- android控件SwipeRefreshLayout实现下拉刷新
- android集成语音功能