在好例子网,分享、交流、成长!
您当前所在位置:首页Java 开发实例Android平台开发 → Android富文本编辑器,图文详细

Android富文本编辑器,图文详细

Android平台开发

下载此实例
  • 开发语言:Java
  • 实例大小:2.29M
  • 下载次数:39
  • 浏览次数:792
  • 发布时间:2017-02-15
  • 实例类别:Android平台开发
  • 发 布 人: 冰凉的感觉
  • 文件格式:.zip
  • 所需积分:2
 相关标签: 编辑器 Android d 编辑 a

实例介绍

Android富文本编辑器,图文详细
【实例截图】
package com.example.androidrichtexteditor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;

/**
 * 这是一个富文本编辑器,给外部提供insertImage接口,添加的图片跟当前光标所在位置有关
 * 
 * @author xmuSistone
 */
public class RichTextEditor extends InterceptLinearLayout {
private static final int EDIT_PADDING = 10; // edittext常规padding是10dp
private static final int EDIT_FIRST_PADDING_TOP = 10; // 第一个EditText的paddingTop值

private int viewTagIndex = 1; // 新生的view都会打一个tag,对每个view来说,这个tag是唯一的。
private LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup
private LayoutInflater inflater;
private OnKeyListener keyListener; // 所有EditText的软键盘监听器
private OnClickListener btnListener; // 图片右上角红叉按钮监听器
private OnFocusChangeListener focusListener; // 所有EditText的焦点监听listener
private EditText lastFocusEdit; // 最近被聚焦的EditText
private LayoutTransition mTransitioner; // 只在图片View添加或remove时,触发transition动画
private int editNormalPadding = 0; //
private int disappearingImageIndex = 0;
private Context context;
private FileUtils fileUtils;

public interface LayoutClickListener {
void layoutClick();
}

private LayoutClickListener mLayoutClickListener;

public void setLayoutClickListener(LayoutClickListener mLayoutClickListener) {
this.mLayoutClickListener = mLayoutClickListener;
}

public RichTextEditor(Context context) {
this(context, null);
}

public RichTextEditor(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public RichTextEditor(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init();
}

public void setIntercept(boolean b) {
super.setIntercept(b);
}

private void init() {
fileUtils = new FileUtils(context);
inflater = LayoutInflater.from(context);

// 1. 初始化allLayout
allLayout = this;
allLayout.setOrientation(LinearLayout.VERTICAL);
allLayout.setBackgroundColor(Color.WHITE);
setupLayoutTransitions();
// LayoutParams layoutParams = new
// LayoutParams(LayoutParams.MATCH_PARENT,
// LayoutParams.WRAP_CONTENT);
// addView(allLayout, layoutParams);

// 2. 初始化键盘退格监听
// 主要用来处理点击回删按钮时,view的一些列合并操作
keyListener = new OnKeyListener() {

@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
EditText edit = (EditText) v;
onBackspacePress(edit);
}
return false;
}
};

// 3. 图片叉掉处理
btnListener = new OnClickListener() {

@Override
public void onClick(View v) {
RelativeLayout parentView = (RelativeLayout) v.getParent();
onImageCloseClick(parentView);
}
};

focusListener = new OnFocusChangeListener() {

@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
lastFocusEdit = (EditText) v;
}
}
};

LayoutParams firstEditParam = new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
editNormalPadding = dip2px(EDIT_PADDING);
EditText firstEdit = createEditText("", dip2px(EDIT_FIRST_PADDING_TOP));
allLayout.addView(firstEdit, firstEditParam);
lastFocusEdit = firstEdit;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
if (mLayoutClickListener != null)
mLayoutClickListener.layoutClick();
break;
}
return super.onInterceptTouchEvent(ev);
}

/**
* 处理软键盘backSpace回退事件

* @param editTxt
*            光标所在的文本输入框
*/
private void onBackspacePress(EditText editTxt) {
int startSelection = editTxt.getSelectionStart();
// 只有在光标已经顶到文本输入框的最前方,在判定是否删除之前的图片,或两个View合并
if (startSelection == 0) {
int editIndex = allLayout.indexOfChild(editTxt);
View preView = allLayout.getChildAt(editIndex - 1); // 如果editIndex-1<0,
// 则返回的是null
if (null != preView) {
if (preView instanceof RelativeLayout) {
// 光标EditText的上一个view对应的是图片
onImageCloseClick(preView);
} else if (preView instanceof EditText) {
// 光标EditText的上一个view对应的还是文本框EditText
String str1 = editTxt.getText().toString();
EditText preEdit = (EditText) preView;
String str2 = preEdit.getText().toString();

// 合并文本view时,不需要transition动画
allLayout.setLayoutTransition(null);
allLayout.removeView(editTxt);
allLayout.setLayoutTransition(mTransitioner); // 恢复transition动画

// 文本合并
preEdit.setText(str2 str1);
preEdit.requestFocus();
preEdit.setSelection(str2.length(), str2.length());
lastFocusEdit = preEdit;
}
}
}
}

/**
* 处理图片叉掉的点击事件

* @param view
*            整个image对应的relativeLayout view
* @type 删除类型 0代表backspace删除 1代表按红叉按钮删除
*/
private void onImageCloseClick(View view) {
if (!mTransitioner.isRunning()) {
disappearingImageIndex = allLayout.indexOfChild(view);
allLayout.removeView(view);
}
}

/**
* 生成文本输入框
*/
private EditText createEditText(String hint, int paddingTop) {
EditText editText = (EditText) inflater.inflate(R.layout.richtextedit_textview, null);
editText.setOnKeyListener(keyListener);
editText.setTag(viewTagIndex );
editText.setPadding(editNormalPadding, paddingTop, editNormalPadding, 0);
editText.setHint(hint);
editText.setOnFocusChangeListener(focusListener);
return editText;
}

/**
* 生成图片View
*/
private RelativeLayout createImageLayout() {
RelativeLayout layout = (RelativeLayout) inflater.inflate(
R.layout.richtextedit_imageview, null);
layout.setTag(viewTagIndex );
View closeView = layout.findViewById(R.id.image_close);
closeView.setTag(layout.getTag());
closeView.setOnClickListener(btnListener);
return layout;
}

/**
* 根据绝对路径添加view

* @param imagePath
*/
public void insertImage(String imagePath) {
Bitmap bmp = getScaledBitmap(imagePath, getWidth());
insertImage(bmp, imagePath);
}

/**
* 插入文字

* @param text
*/
public void insertText(String text) {
View itemView = allLayout.getChildAt(allLayout.getChildCount() - 1);
if (itemView instanceof EditText) {
EditText item = (EditText) itemView;
if (item.getText() == null || item.getText().length() < 1)
item.setText(text);
else
addEditTextAtIndex(-1, text);
}
}

/**
* 插入一张图片
*/
private void insertImage(Bitmap bitmap, String imagePath) {
String lastEditStr = lastFocusEdit.getText().toString();
int cursorIndex = lastFocusEdit.getSelectionStart();
String editStr1 = lastEditStr.substring(0, cursorIndex).trim();
int lastEditIndex = allLayout.indexOfChild(lastFocusEdit);
lastFocusEdit.setText(editStr1);
String editStr2 = lastEditStr.substring(cursorIndex).trim();
addEditTextAtIndex(lastEditIndex 1, editStr2);
addImageViewAtIndex(lastEditIndex 1, bitmap, imagePath);
lastFocusEdit.requestFocus();
lastFocusEdit.setSelection(editStr1.length(), editStr1.length());
hideKeyBoard();
}

/**
* 隐藏小键盘
*/
public void hideKeyBoard() {
InputMethodManager imm = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(lastFocusEdit.getWindowToken(), 0);
}

/**
* 在特定位置插入EditText

* @param index
*            位置
* @param editStr
*            EditText显示的文字
*/
private void addEditTextAtIndex(final int index, String editStr) {
EditText editText2 = createEditText("", getResources()
.getDimensionPixelSize(R.dimen.richtextedit_padding_top));
editText2.setText(editStr);

// 请注意此处,EditText添加、或删除不触动Transition动画
allLayout.setLayoutTransition(null);
allLayout.addView(editText2, index);
allLayout.setLayoutTransition(mTransitioner); // remove之后恢复transition动画
}

/**
* 在特定位置添加ImageView
*/
private void addImageViewAtIndex(final int index, Bitmap bmp,
String imagePath) {
final RelativeLayout imageLayout = createImageLayout();
DataImageView imageView = (DataImageView) imageLayout
.findViewById(R.id.edit_imageView);
imageView.setImageBitmap(bmp);
imageView.setBitmap(bmp);
imageView.setAbsolutePath(imagePath);

// 调整imageView的高度
int imageHeight = getWidth() * bmp.getHeight() / bmp.getWidth();
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, imageHeight);
imageView.setLayoutParams(lp);

// onActivityResult无法触发动画,此处post处理
allLayout.postDelayed(new Runnable() {
@Override
public void run() {
allLayout.addView(imageLayout, index);
}
}, 200);
}

/**
* 插入网络图片

* @param url
*/
public void insertImageByURL(String url) {
if (url == null)
return;
final RelativeLayout imageLayout = createImageLayout();
final DataImageView imageView = (DataImageView) imageLayout.findViewById(R.id.edit_imageView);
imageView.setImageResource(R.drawable.logo);
imageView.setScaleType(ImageView.ScaleType.CENTER);
allLayout.addView(imageLayout);
addEditTextAtIndex(-1, "");
ImageLoader.getInstance().displayImage(url, imageView,
new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view,Bitmap loadedImage)
{
String path = fileUtils.savaRichTextImage(imageUri,loadedImage);
imageView.setImageBitmap(loadedImage);
imageView.setBitmap(loadedImage);
imageView.setAbsolutePath(path);
int imageHeight = getWidth() * loadedImage.getHeight()/ loadedImage.getWidth();
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, imageHeight);
imageView.setLayoutParams(lp);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
}
});
}

/**
* 根据view的宽度,动态缩放bitmap尺寸

* @param width
*            view的宽度
*/
private Bitmap getScaledBitmap(String filePath, int width) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
int sampleSize = options.outWidth > width ? options.outWidth / width
1 : 1;
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
return BitmapFactory.decodeFile(filePath, options);
}

/**
* 初始化transition动画
*/
private void setupLayoutTransitions() {
mTransitioner = new LayoutTransition();
allLayout.setLayoutTransition(mTransitioner);
mTransitioner.addTransitionListener(new TransitionListener() {

@Override
public void startTransition(LayoutTransition transition,
ViewGroup container, View view, int transitionType) {

}

@Override
public void endTransition(LayoutTransition transition,
ViewGroup container, View view, int transitionType) {
if (!transition.isRunning()
&& transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
// transition动画结束,合并EditText
// mergeEditText();
}
}
});
mTransitioner.setDuration(300);
}

/**
* 图片删除的时候,如果上下方都是EditText,则合并处理
*/
@SuppressWarnings("unused")
private void mergeEditText() {
View preView = allLayout.getChildAt(disappearingImageIndex - 1);
View nextView = allLayout.getChildAt(disappearingImageIndex);
if (preView != null && preView instanceof EditText && null != nextView
&& nextView instanceof EditText) {
EditText preEdit = (EditText) preView;
EditText nextEdit = (EditText) nextView;
String str1 = preEdit.getText().toString();
String str2 = nextEdit.getText().toString();
String mergeText = "";
if (str2.length() > 0) {
mergeText = str1 "\n" str2;
} else {
mergeText = str1;
}

allLayout.setLayoutTransition(null);
allLayout.removeView(nextEdit);
preEdit.setText(mergeText);
preEdit.requestFocus();
preEdit.setSelection(str1.length(), str1.length());
allLayout.setLayoutTransition(mTransitioner);
}
}

/**
* dp和pixel转换

* @param dipValue
*            dp值
* @return 像素值
*/
public int dip2px(float dipValue) {
float m = getContext().getResources().getDisplayMetrics().density;
return (int) (dipValue * m 0.5f);
}

/**
* 对外提供的接口, 生成编辑数据上传
*/
public List<EditData> buildEditData() {
List<EditData> dataList = new ArrayList<EditData>();
int num = allLayout.getChildCount();
for (int index = 0; index < num; index ) {
View itemView = allLayout.getChildAt(index);
EditData itemData = new EditData();
if (itemView instanceof EditText) {
EditText item = (EditText) itemView;
itemData.inputStr = item.getText().toString();
} else if (itemView instanceof RelativeLayout) {
DataImageView item = (DataImageView) itemView
.findViewById(R.id.edit_imageView);
itemData.imagePath = item.getAbsolutePath();
itemData.bitmap = item.getBitmap();
}
dataList.add(itemData);
}

return dataList;
}

public HashMap<String, Object> getRichEditData() {
HashMap<String, Object> data = new HashMap<String, Object>();
StringBuilder editTextSB = new StringBuilder();
List<String> imgUrls = new ArrayList<String>();
char separator = 26;
int num = allLayout.getChildCount();
for (int index = 0; index < num; index ) {
View itemView = allLayout.getChildAt(index);
if (itemView instanceof EditText) {
EditText item = (EditText) itemView;
editTextSB.append(item.getText().toString());
} else if (itemView instanceof RelativeLayout) {
DataImageView item = (DataImageView) itemView
.findViewById(R.id.edit_imageView);
imgUrls.add(item.getAbsolutePath());
editTextSB.append(separator);
}
}
data.put("text", editTextSB);
data.put("imgUrls", imgUrls);

return data;
}

class EditData {
String inputStr;
String imagePath;
Bitmap bitmap;
}
}


标签: 编辑器 Android d 编辑 a

实例下载地址

Android富文本编辑器,图文详细

不能下载?内容有错? 点击这里报错 + 投诉 + 提问

好例子网口号:伸出你的我的手 — 分享

网友评论

发表评论

(您的评论需要经过审核才能显示)

查看所有0条评论>>

小贴士

感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。

  • 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
  • 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
  • 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
  • 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。

关于好例子网

本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明

;
报警