2024年安卓最新Android富文本开发,android学习路线图

结语

网上高级工程师面试相关文章鱼龙混杂,要么一堆内容,要么内容质量太浅, 鉴于此我整理了上述安卓开发高级工程师面试题以及答案。希望帮助大家顺利进阶为高级工程师。
目前我就职于某大厂安卓高级工程师职位,在当下大环境下也想为安卓工程师出一份力,通过我的技术经验整理了面试经常问的题,答案部分是一篇文章或者几篇文章,都是我认真看过并且觉得不错才整理出来。

大家知道高级工程师不会像刚入门那样被问的问题一句话两句话就能表述清楚,所以我通过过滤好文章来帮助大家理解。

1307页字节跳动Android面试真题解析火爆全网,完整版开放下载

现在都说互联网寒冬,其实只要自身技术能力够强,咱们就不怕!我这边专门针对Android开发工程师整理了一套【Android进阶学习视频】、【全套Android面试秘籍】、【Android知识点PDF】。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

07.如果对选中文字加粗

  • Span 的分类介绍
  • 字符外观,这种类型修改字符的外形但是不影响字符的测量,会触发文本重新绘制但是不触发重新布局。
  • ForegroundColorSpan,BackgroundColorSpan,UnderlineSpan,StrikethrougnSpan
  • 字符大小布局,这种类型Span会更改文本的大小和布局,会触发文本的重新测量绘制
  • StyleSpan,RelativeSizeSpan,AbsoluteSizeSpan
  • 影响段落级别,这种类型Span 在段落级别起作用,更改文本块在段落级别的外观,修改对齐方式,边距等。
  • AlignmentSpan,BulletSpan,QuoteSpan
  • 实现基础样式 粗体、 斜体、 下划线 、中划线 的设置和取消。举个例子,对文本加粗,文字设置span样式注意要点,这里需要区分几种情况
  • 当前选中区域不存在 bold 样式 这里我们选中BB。两种情况
  • 当前区域紧靠左侧或者右侧不存在粗体样式: AABBCC 这时候直接设置 span即可
  • 当前区域紧靠左侧或者右侧存在粗体样式如: AABBCC AABBCC AABBCC。这时候需要合并左右两侧的span,只剩下一个 span
  • 当前选中区域存在了Bold 样式 选中 ABBC。四种情况:
  • 选中样式两侧不存在连续的bold样式 AABBCC
  • 选中内部两端存在连续的bold 样式 AABBCC
  • 选中左侧存在连续的bold 样式 AABBCC
  • 选中右侧存在连续的bold 样式 AABBCC
  • 这时候需要合并左右两侧已经存在的span,只剩下一个 span
  • 接下来逐步分解,然后处理span的逻辑顺序如下所示
  • 首先对选中文字内容样式情况判断
  • 边界判断与设置
  • 取消Span(当我们选中的区域在一段连续的 Bold 样式里面的时候,再次选择Bold将会取消样式)
  • 什么时候取消span呢,这个逻辑是比较复杂的,具体看看下面的举例。
  • 当我们选中的区域在一段连续的 Bold 样式里面的时候,再次选择Bold将会取消样式
  • 用户可以随意的删除文本,在删除过程中可能会出现如下的情况:
  • 用户输入了 AABBCCDD
  • 用户选择了粗体样式 AABBCCDD
  • 用户删除了CC然后显示如下 : AABB DD
  • 这个时候选中其中的BD 此时,在该区域中 存在两个span ,并且没有一个 span 完全包裹选中的 BD
  • 在这种情况下 仍需要进行左右侧边界判断进行删除。这个具体可以看代码逻辑。

08.利用Span对文字属性处理

  • 这里仅仅是对字体加粗进行介绍,其实设置span可以找到规律。多个span样式,考虑到后期的拓展性,肯定要进行封装和抽象,具体该如何处理呢?

  • 设置文本选中内容加粗模式,代码如下所示,可以看到这里只需要传递一个lastFocusEdit对象即可,这个对象是最近被聚焦的EditText。

/**

  • 修改加粗样式
    */
    public void bold(EditText lastFocusEdit) {
    //获取editable对象
    Editable editable = lastFocusEdit.getEditableText();
    //获取当前选中的起始位置
    int start = lastFocusEdit.getSelectionStart();
    //获取当前选中的末尾位置
    int end = lastFocusEdit.getSelectionEnd();
    HyperLogUtils.i(“bold select Start:” + start + " end: " + end);
    if (checkNormalStyle(start, end)) {
    return;
    }
    new BoldStyle().applyStyle(editable, start, end);
    }

  • 然后如何调用这个,在HyperTextEditor类中代码如下所示。为何要这样写,可以把HyperTextEditor富文本类中设置span的逻辑放到SpanTextHelper类中处理,该类专门处理各种span属性,这样代码结构更加清晰,也方便后期增加更多span属性,避免一个类代码太臃肿。

/**

  • 修改加粗样式
    */
    public void bold() {
    SpanTextHelper.getInstance().bold(lastFocusEdit);
    }

public void applyStyle(Editable editable, int start, int end) {
//获取 从 start 到 end 位置上所有的指定 class 类型的 Span数组
E[] spans = editable.getSpans(start, end, clazzE);
E existingSpan = null;
if (spans.length > 0) {
existingSpan = spans[0];
}
if (existingSpan == null) {
//当前选中内部无此样式,开始设置span样式
checkAndMergeSpan(editable, start, end, clazzE);
} else {
//获取 一个 span 的起始位置
int existingSpanStart = editable.getSpanStart(existingSpan);
//获取一个span 的结束位置
int existingSpanEnd = editable.getSpanEnd(existingSpan);
if (existingSpanStart <= start && existingSpanEnd >= end) {
//在一个 完整的 span 中
//删除 样式
//
removeStyle(editable, start, end, clazzE, true);
} else {
//当前选中区域存在了某某样式,需要合并样式
checkAndMergeSpan(editable, start, end, clazzE);
}
}
}

09.如何设置插入多张图片

Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) {
try{
hte_content.measure(0, 0);
List mSelected = Matisse.obtainResult(data);
// 可以同时插入多张图片
for (Uri imageUri : mSelected) {
String imagePath = HyperLibUtils.getFilePathFromUri(NewActivity.this, imageUri);
Bitmap bitmap = HyperLibUtils.getSmallBitmap(imagePath, screenWidth, screenHeight);
//压缩图片
imagePath = SDCardUtil.saveToSdCard(bitmap);
emitter.onNext(imagePath);
}
emitter.onComplete();
}catch (Exception e){
e.printStackTrace();
emitter.onError(e);
}
}
})
.subscribeOn(Schedulers.io())//生产事件在io
.observeOn(AndroidSchedulers.mainThread())//消费事件在UI线程
.subscribe(new Observer() {
@Override
public void onComplete() {
ToastUtils.showRoundRectToast(“图片插入成功”);
}

@Override
public void onError(Throwable e) {
ToastUtils.showRoundRectToast(“图片插入失败:”+e.getMessage());
}

@Override
public void onSubscribe(Disposable d) {

}

@Override
public void onNext(String imagePath) {
//插入图片
hte_content.insertImage(imagePath);
}
});

10.如何设置插入网络图片

  • 插入图片有两种情况,一种是本地图片,一种是网络图片。由于富文本中对插入图片的宽高有限制,即可以动态设置图片的高度,这就要求请求网络图片后,需要对图片进行处理。

/**

  • 在特定位置添加ImageView
    */
    public void addImageViewAtIndex(final int index, final String imagePath) {
    if (TextUtils.isEmpty(imagePath)){
    return;
    }
    try {
    imagePaths.add(imagePath);
    final RelativeLayout imageLayout = createImageLayout();
    HyperImageView imageView = imageLayout.findViewById(R.id.edit_imageView);
    imageView.setAbsolutePath(imagePath);
    HyperManager.getInstance().loadImage(imagePath, imageView, rtImageHeight);
    layout.addView(imageLayout, index);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

HyperManager.getInstance().setImageLoader(new ImageLoader() {
@Override
public void loadImage(final String imagePath, final ImageView imageView, final int imageHeight) {
Log.e(“—”, "imageHeight: "+imageHeight);
//如果是网络图片
if (imagePath.startsWith(“http://”) || imagePath.startsWith(“https://”)){
//直接用图片加载框架加载图片即可
} else { //如果是本地图片

}
}
});

11.如何避免插入图片OOM

  • 加载一个本地的大图片或者网络图片,从加载到设置到View上,如何减下内存,避免加载图片OOM。
  • 在展示高分辨率图片的时候,最好先将图片进行压缩。压缩后的图片大小应该和用来展示它的控件大小相近,在一个很小的ImageView上显示一张超大的图片不会带来任何视觉上的好处,但却会占用相当多宝贵的内存,而且在性能上还可能会带来负面影响。
  • 加载图片的内存都去哪里呢?
  • 其实我们的内存就是去bitmap里了,BitmapFactory的每个decode函数都会生成一个bitmap对象,用于存放解码后的图像,然后返回该引用。如果图像数据较大就会造成bitmap对象申请的内存较多,如果图像过多就会造成内存不够用自然就会出现out of memory的现象。
  • 为何容易OOM?
  • 通过BitmapFactory的decode的这些方法会尝试为已经构建的bitmap分配内存,这时就会很容易导致OOM出现。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。
  • 如何对图片进行压缩?
  • 1.解析图片,获取图片资源的属性
  • 2.计算图片的缩放值
  • 3.最后对图片进行质量压缩

public static Bitmap getSmallBitmap(String filePath, int newWidth, int newHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// Calculate inSampleSize
// 计算图片的缩放值
options.inSampleSize = calculateInSampleSize(options, newWidth, newHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
// 质量压缩
Bitmap newBitmap = compressImage(bitmap, 500);
if (bitmap != null){
//手动释放资源
bitmap.recycle();
}
return newBitmap;
}

  • 思考:inJustDecodeBounds这个参数是干什么的?
  • 如果设置为true则表示decode函数不会生成bitmap对象,仅是将图像相关的参数填充到option对象里,这样我们就可以在不生成bitmap而获取到图像的相关参数了。
  • 为何设置两次inJustDecodeBounds属性?
  • 第一次:设置为true则表示decode函数不会生成bitmap对象,仅是将图像相关的参数填充到option对象里,这样我们就可以在不生成bitmap而获取到图像的相关参数。
  • 第二次:将inJustDecodeBounds设置为false再次调用decode函数时就能生成bitmap了。而此时的bitmap已经压缩减小很多了,所以加载到内存中并不会导致OOM。

12.如何删除图片或者文字

/**

  • 处理图片上删除的点击事件
  • 删除类型 0代表backspace删除 1代表按红叉按钮删除
  • @param view 整个image对应的relativeLayout view
    */
    private void onImageCloseClick(View view) {
    try {
    //判断过渡动画是否结束,只能等到结束才可以操作
    if (!mTransition.isRunning()) {
    disappearingImageIndex = layout.indexOfChild(view);
    //删除文件夹里的图片
    List dataList = buildEditData();
    HyperEditData editData = dataList.get(disappearingImageIndex);
    if (editData.getImagePath() != null){
    if (onHyperListener != null){
    onHyperListener.onRtImageDelete(editData.getImagePath());
    }
    //SDCardUtil.deleteFile(editData.imagePath);
    //从图片集合中移除图片链接
    imagePaths.remove(editData.getImagePath());
    }
    //然后移除当前view
    layout.removeView(view);
    //合并上下EditText内容
    mergeEditText();
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

13.删除和插入图片添加动画

  • 为什么要添加插入图片的过渡动画
  • 当向一个ViewGroup添加控件或者移除控件;这种场景虽然能够实现效果,并没有一点过度效果,直来直去的添加或者移除,显得有点生硬。有没有办法添加一定的过度效果,让实现的效果显得圆滑呢?
  • LayoutTransition简单介绍
  • LayoutTransition类实际上Android系统中的一个实用工具类。使用LayoutTransition类在一个ViewGroup中对布局更改进行动画处理。
  • 如何运用到插入或者删除图片场景中
  • 向一个ViewGroup添加控件或者移除控件,这两种效果的过程是应对应于控件的显示、控件添加时其他控件的位置移动、控件的消失、控件移除时其他控件的位置移动等四种动画效果。这些动画效果在LayoutTransition中,由以下四个关键字做出了相关声明:
  • APPEARING:元素在容器中显现时需要动画显示。
  • CHANGE_APPEARING:由于容器中要显现一个新的元素,其它元素的变化需要动画显示。
  • DISAPPEARING:元素在容器中消失时需要动画显示。
  • CHANGE_DISAPPEARING:由于容器中某个元素要消失,其它元素的变化需要动画显示。
  • 也就是说,ViewGroup中有多个ImageView对象,如果需要删除其中一个ImageView对象的话,该ImageView对象可以设置动画(即DISAPPEARING 动画形式),ViewGroup中的其它ImageView对象此时移动到新的位置的过程中也可以设置相关的动画(即CHANGE_DISAPPEARING 动画形式);
  • 若向ViewGroup中添加一个ImageView,ImageView对象可以设置动画(即APPEARING 动画形式),ViewGroup中的其它ImageView对象此时移动到新的位置的过程中也可以设置相关的动画(即CHANGE_APPEARING 动画形式)。
  • 给ViewGroup设置动画很简单,只需要生成一个LayoutTransition实例,然后调用ViewGroup的setLayoutTransition(LayoutTransition)函数就可以了。当设置了布局动画的ViewGroup添加或者删除内部view时就会触发动画。

mTransition = new LayoutTransition();
mTransition.addTransitionListener(new LayoutTransition.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();
}
}
});
mTransition.enableTransitionType(LayoutTransition.APPEARING);
mTransition.setDuration(300);
layout.setLayoutTransition(mTransition);

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mTransition!=null){
//移除Layout变化监听
mTransition.removeTransitionListener(transitionListener);
}
}

  • 动画执行先后的顺序
  • 分析源码可以知道,默认情况下DISAPPEARING和CHANGE_APPEARING类型动画会立即执行,其他类型动画则会有个延迟。也就是说如果删除view,被删除的view将先执行动画消失,经过一些延迟受影响的view会进行动画补上位置,如果添加view,受影响的view将会先给添加的view腾位置执行CHANGE_APPEARING动画,经过一些时间的延迟才会执行APPEARING动画。这里就不贴分析源码的思路呢!

14.点击图片可以查看大图

  • 编辑状态时,由于图片有空能比较大,在显示在富文本的时候,会裁剪局中显示,也就是图片会显示不全。那么后期如果是想添加点击图片查看,则需要暴露给开发者监听事件,需要考虑到后期拓展性,代码如下所示:

  • 这样做的目的是是暴露给外部开发者调用,点击图片的操作只需要传递view还有图片即可。

// 图片处理
btnListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (v instanceof HyperImageView){
HyperImageView imageView = (HyperImageView)v;
// 开放图片点击接口
if (onHyperListener != null){
onHyperListener.onImageClick(imageView, imageView.getAbsolutePath());
}
}
}
};

15.如何暴露设置文字属性方法

/**

  • 修改加粗样式
    */
    public void bold() {
    SpanTextHelper.getInstance().bold(lastFocusEdit);
    }

/**

  • 修改斜体样式
    */
    public void italic() {
    SpanTextHelper.getInstance().italic(lastFocusEdit);
    }

/**

  • 修改删除线样式
    */
    public void strikeThrough() {
    SpanTextHelper.getInstance().strikeThrough(lastFocusEdit);
    }

/**

  • 修改下划线样式
    */
    public void underline() {
    SpanTextHelper.getInstance().underline(lastFocusEdit);
    }

public abstract class NormalStyle {

private Class clazzE;

public NormalStyle() {
//利用反射
clazzE = (Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}

/**

  • 样式情况判断
  • @param editable editable
  • @param start start
  • @param end end
    */
    public void applyStyle(Editable editable, int start, int end) {

}
}

public class ItalicStyle extends NormalStyle {
@Override
protected ItalicStyleSpan newSpan() {
return new ItalicStyleSpan();
}
}

public class UnderlineStyle extends NormalStyle {
@Override
protected UnderLineSpan newSpan() {
return new UnderLineSpan();
}
}

16.文字中间添加图片注意事项

  • 在文字中添加图片比较特殊,因此这里单独拿出来说一下。在文字内容中间插入图片,则需要分割字符串,分割成两个EditText,并在两个EditText中间插入图片,那么这个光标又定位在何处呢?

  • 对于光标前面的字符串保留,设置给当前获得焦点的EditText(此为分割出来的第一个EditText)

  • 把光标后面的字符串放在新创建的EditText中(此为分割出来的第二个EditText)

  • 在第二个EditText的位置插入一个空的EditText,以便连续插入多张图片时,有空间写文字,第二个EditText下移

  • 在空的EditText的位置插入图片布局,空的EditText下移。注意,这个过程添加动画过渡一下插入的效果比较好,不然会比较生硬

//获取光标所在位置
int cursorIndex = lastFocusEdit.getSelectionStart();
//获取光标前面的字符串
String editStr1 = lastEditStr.substring(0, cursorIndex).trim();
//获取光标后的字符串
String editStr2 = lastEditStr.substring(cursorIndex).trim();

lastFocusEdit.setText(editStr1);
addEditTextAtIndex(lastEditIndex + 1, editStr2);
addEditTextAtIndex(lastEditIndex + 1, “”);
addImageViewAtIndex(lastEditIndex + 1, imagePath);

17.键盘弹出和收缩优化

  • 软键盘弹出的时机

  • 如果不做任何处理,系统默认的是,进入页面,第一个输入框自动获取焦点软键盘自动弹出,这种用户交互方式,往往不是产品想要的,往往会提出以下优化需求:

  • 需求1:editText获取焦点,但是不弹出软键盘(也就是说光标显示第一个输入框,不主动弹软键盘)

  • 在第一个输入框的最直接父布局加入:android:focusable=“true”;android:focusableInTouchMode=“true”

  • (效果:软键盘不弹出,光标不显示,其他输入框也不获取焦点,ps非直接父布局没有效果)

  • android:windowSoftInputMode=“stateAlwaysHidden”

  • (效果:软键盘不弹出,光标显示在第一个输入框中)

  • 需求2:editText不获取焦点,当然软键盘不会主动弹出(光标也不显示)

  • 在第一个输入框的最直接父布局加入:android:focusable=“true”;android:focusableInTouchMode=“true”

  • (效果:软键盘不弹出,光标不显示,其他输入框也不获取焦点,ps非直接父布局没有效果)

  • 在父布局最顶部添加一个高度为0的EditText,抢了焦点但不展示;

  • 软键盘遮挡界面的问题

  • 当界面中有输入框,需要弹起软键盘输入信息的时候,软键盘可能遮挡部分布局,更有甚者,当前输入框如果在屏幕下方,软键盘也会直接遮挡输入框,这种情况对用户体验是相当不友好的,所以要根据具体的情况作出相应的处理。

  • android定义了一个属性,名字为windowSoftInputMode, 这个属性用于设置Activity主窗口与软键盘的交互模式,用于避免软键盘遮挡内容的问题。我们可以在AndroidManifet.xml中对Activity进行设置。

stateUnspecified-未指定状态:软件默认采用的交互方式,系统会根据当前界面自动调整软键盘的显示模式。
stateUnchanged-不改变状态:当前界面软键盘状态由上个界面软键盘的状态决定;
stateHidden-隐藏状态:进入页面,无论是否有输入需求,软键盘是隐藏的,但是如果跳转到下一个页面软键盘是展示的,回到这个页面,软键盘可能也是展示的,这个属性区别下个属性。
stateAlwaysHidden-总是隐藏状态:当设置该状态时,软键盘总是被隐藏,和stateHidden不同的是,当我们跳转到下个界面,如果下个页面的软键盘是显示的,而我们再次回来的时候,软键盘就会隐藏起来。
stateVisible-可见状态:当设置为这个状态时,软键盘总是可见的,即使在界面上没有输入框的情况下也可以强制弹出来出来。
stateAlwaysVisible-总是显示状态:当设置为这个状态时,软键盘总是可见的,和stateVisible不同的是,当我们跳转到下个界面,如果下个页面软键盘是隐藏的,而我们再次回来的时候,软键盘就会显示出来。
adjustUnspecified-未指定模式:设置软键盘与软件的显示内容之间的显示关系。当你跟我们没有设置这个值的时候,这个选项也是默认的设置模式。在这中情况下,系统会根据界面选择不同的模式。
adjustResize-调整模式:当软键盘显示的时候,当前界面会自动重绘,会被压缩,软键盘消失之后,界面恢复正常(正常布局,非scrollView父布局);当父布局是scrollView的时候,软键盘弹出,会将布局顶起(保证输入框不被遮挡),不压缩,而且可以软键盘不消失的情况下,手动滑出被遮挡的布局;
adjustPan-默认模式:软键盘弹出,软键盘会遮挡屏幕下半部分布局,当输入框在屏幕下方布局,软键盘弹起,会自动将当前布局顶起,保证,软键盘不遮挡当前输入框(正常布局,非scrollView父布局)。当父布局是scrollView的时候,感觉没啥变化,还是自定将布局顶起,输入框不被遮挡,不可以手动滑出被遮挡的布局(白瞎了scrollView);

  • 看了上面的属性,那么该如何设置呢?具体效果可以看demo案例。

  • 软键盘及时退出的问题
  • 当用户输入完成之后,必须手动点击软键盘的收回键,软键盘才收起。如果能通过代码主动将软键盘收起,这对于用户体验来说,是一个极大的提升,思前想后,参考网上的文档,个人比较喜欢的实现方式是通过事件分发机制来解决这个问题。

View rootView = hte_content.getRootView();
rootView.setBackgroundColor(Color.WHITE);

18.前后台切换编辑富文本优化

  • 由于富文本中,用户会输入很多的内容,当关闭页面时候,需要提醒用户是否保存输入内容。同时,切换到后台的时候,需要注意保存输入内容,避免长时间切换后台进程内存吃紧,在回到前台输入的内容没有呢,查阅了汽车之家,易车等app等手机上的富文本编辑器,都会有这个细节点的优化。

19.生成html片段上传服务器

19.1 提交富文本
  • 客户端生成html片段到服务器
  • 在客户端提交帖子,文章。富文本包括图片,文字内容,还有文字span样式,同时会选择一些文章,帖子的标签。还有设置文章的类型,封面图,作者等许多属性。
  • 当点击提交的时候,客户端把这些数据,转化成html,还是转化成json对象提交给服务器呢?思考一下,会有哪些问题……
  • 转化成html
  • 对于将单个富文本转化成html相对来说是比较容易的,因为富文本中之存在文字,图片等。转化成html细心就可以。
  • 但是对于设置富文本的标签,类型,作者,封面图,日期,其他关联属性怎么合并到html中呢,这个相对麻烦。
  • 最后想说的是
  • 对于富文本写帖子,文章,如果写完富文本提交,则可以使用转化成html数据提交给服务器;
  • 对于富文本写完帖子,文章,还有下一步,设置标签,类型,封面图,作者,时间,还有其他属性,则可以使用转化成json数据提交给服务器;
19.2 编辑富文本
  • 服务器返回html给客户端加载
  • 涉及到富文本的加载,后台管理端编辑器生成的一段html 代码要渲染到移动端上面,一种方法是前端做成html页面,放到服务器上,移动端这边直接webView 加载url即可。
  • 还有一种后台接口直接返回这段html富文本的,String类型的,移动端直接加载的;具体的需求按实际情况而定。
  • 加载html文件流畅问题
  • webView直接加载url体验上没那么流畅,相对的加载html文件会好点。但是对比原生,体验上稍微弱点。
  • 如果不用WebView,使用TextView显示html富文本,则会出现图片不显示,以及格式问题。
  • 如果不用WebView,使用自定义富文本RichText,则需要解析html显示,如果对html标签,js不熟悉,也不太好处理。

20.生成json片段上传服务器

  • 参考了易车发布帖子,提交数据到服务器,针对富文本,是把它拼接成对象。将文字,图片按照富文本的顺序拼接成json片段,然后提交给服务器。
20.1 提交富文本

public class HyperEditData implements Serializable {

/**

  • 富文本输入文字内容
    /
    private String inputStr;
    /
    *
  • 富文本输入图片地址
    /
    private String imagePath;
    /
    *
  • 类型:1,代表文字;2,代表图片
    */
    private int type;

//省略很多set,get方法
}

/**

  • 对外提供的接口, 生成编辑数据上传
    */
    public List buildEditData() {
    List dataList = new ArrayList<>();
    try {
    int num = layout.getChildCount();
    for (int index = 0; index < num; index++) {
    View itemView = layout.getChildAt(index);
    HyperEditData hyperEditData = new HyperEditData();
    if (itemView instanceof EditText) {
    //文本
    EditText item = (EditText) itemView;
    hyperEditData.setInputStr(item.getText().toString());
    hyperEditData.setType(2);
    } else if (itemView instanceof RelativeLayout) {
    //图片
    HyperImageView item = itemView.findViewById(R.id.edit_imageView);
    hyperEditData.setImagePath(item.getAbsolutePath());
    hyperEditData.setType(1);
    }
    dataList.add(hyperEditData);
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    HyperLogUtils.d(“HyperTextEditor----buildEditData------dataList—”+dataList.size());
    return dataList;

最后

我这里整理了一份完整的学习思维以及Android开发知识大全PDF。

当然实践出真知,即使有了学习线路也要注重实践,学习过的内容只有结合实操才算是真正的掌握。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

(1);
}
dataList.add(hyperEditData);
}
} catch (Exception e) {
e.printStackTrace();
}
HyperLogUtils.d(“HyperTextEditor----buildEditData------dataList—”+dataList.size());
return dataList;

最后

我这里整理了一份完整的学习思维以及Android开发知识大全PDF。

[外链图片转存中…(img-lMHhyWz3-1715013719459)]

当然实践出真知,即使有了学习线路也要注重实践,学习过的内容只有结合实操才算是真正的掌握。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值