先看下效果图:

上面是MTextView,下面是默认的TextView。

一、原因

用最简单的全英文句子为例,如果有一个很长的单词,这一行剩余的空间显示不下了,那么规则就是不打断单词,而是把整个单词丢到下一行开始显示。这样 本来没有错。一是咱们中国人都是方块字,怎么都放得下,不存在英文的这个问题。所以不习惯那个排版。二是如果TextView里面有图片,如图,不知道判断单词的代码是怎么弄得,总之它觉得最后一个啦字和后面的一串表情应该是一个整体,不能分开,就一起丢到第二行了,也就造成了这种难看的排版。要验证这个说法也很简单,自己去QQ里试一试,在每个表情之间都加一个空格,就会发现排版一下子正常了。

二、解决方法

最简单的就是表情之间加空格,如果不想这么做,就只有自己来画啦。

先给初学的朋友解释一下View绘制的流程,首先是onMeasure(int widthMeasureSpec, int heightMeasureSpec),onMeasure执行的时候,就是父View在问你,小朋友,你要占多大的地儿呀?当然,问你的时候,会给你个 限制条件,就是那两参数,以widthMeasureSpec为例,这参数不能直接用,得先拆开,用int widthMode = MeasureSpec.getMode(widthMeasureSpec) 和 int widthSize = MeasureSpec.getSize(widthMeasureSpec);widthMode就三种情况:

MeasureSpec.EXACTLY:你就widthSize那么宽就行了。

MeasureSpec.AT_MOST:你最多只能widthSize那么宽。

MeasureSpec.UNSPECIFIED:未指定,你爱多宽多宽。

当然,其实这只父View给你的建议,遵不遵守你自己看着办,但是自己乱来导致显示不全就不是父View的错了。

最终你听取了建议,思量了一番,觉得自己应该有width那么宽,height那么高,最后就得用setMeasuredDimension(width, height)这个函数真正确定自己的高宽。然后onMeasure()的工作就完了。

然后就是onDraw(Canvas canvas),这个就简单了,canvas就是父View给的一块画布,爱在上面画啥都行,比如写个字drawText(String text,float x, float y, Paint paint),

text是要写的字,paint是写字的笔,值得注意的是x,y坐标是相对于你自己这一小块画布的左上角的。最左上就是0,0右下是width,height

上代码

  1. /**
  2. * @author huangwei
  3. * @version SocialClient 1.2.0
  4. * @功能 图文混排TextView,请使用{@link #setMText(CharSequence)}
  5. * @2014年5月27日
  6. * @下午5:29:27
  7. */
  8. public class MTextView extends TextView
  9. {
  10. /**
  11. * 缓存测量过的数据
  12. */
  13. private static HashMap<String, SoftReference<MeasuredData>> measuredData = new HashMap<String, SoftReference<MeasuredData>>();
  14. private static int hashIndex = ;
  15. /**
  16. * 存储当前文本内容,每个item为一行
  17. */
  18. ArrayList<LINE> contentList = new ArrayList<LINE>();
  19. private Context context;
  20. /**
  21. * 用于测量字符宽度
  22. */
  23. private TextPaint paint = new TextPaint();
  24.  
  25. // private float lineSpacingMult = 0.5f;
  26. private int textColor = Color.BLACK;
  27. //行距
  28. private float lineSpacing;
  29. private int lineSpacingDP = ;
  30. /**
  31. * 最大宽度
  32. */
  33. private int maxWidth;
  34. /**
  35. * 只有一行时的宽度
  36. */
  37. private int oneLineWidth = -;
  38. /**
  39. * 已绘的行中最宽的一行的宽度
  40. */
  41. private float lineWidthMax = -;
  42. /**
  43. * 存储当前文本内容,每个item为一个字符或者一个SpanObject
  44. */
  45. private ArrayList<Object> obList = new ArrayList<Object>();
  46. /**
  47. * 是否使用默认{@link #onMeasure(int, int)}和{@link #onDraw(Canvas)}
  48. */
  49. private boolean useDefault = false;
  50. private CharSequence text = "";
  51.  
  52. private int minHeight;
  53. /**
  54. * 用以获取屏幕高宽
  55. */
  56. private DisplayMetrics displayMetrics;
  57. /**
  58. * {@link android.text.style.BackgroundColorSpan}用
  59. */
  60. private Paint textBgColorPaint = new Paint();
  61. /**
  62. * {@link android.text.style.BackgroundColorSpan}用
  63. */
  64. private Rect textBgColorRect = new Rect();
  65.  
  66. public MTextView(Context context)
  67. {
  68. super(context);
  69. this.context = context;
  70. paint.setAntiAlias(true);
  71. lineSpacing = dip2px(context, lineSpacingDP);
  72. minHeight = dip2px(context, );
  73.  
  74. displayMetrics = new DisplayMetrics();
  75. }
  76.  
  77. public MTextView(Context context,AttributeSet attrs)
  78. {
  79. super(context,attrs);
  80. this.context = context;
  81. paint.setAntiAlias(true);
  82. lineSpacing = dip2px(context, lineSpacingDP);
  83. minHeight = dip2px(context, );
  84.  
  85. displayMetrics = new DisplayMetrics();
  86. }
  87.  
  88. public static int px2sp(Context context, float pxValue)
  89. {
  90. final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
  91. return (int) (pxValue / fontScale + 0.5f);
  92. }
  93.  
  94. /**
  95. * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
  96. */
  97. public static int dip2px(Context context, float dpValue)
  98. {
  99. final float scale = context.getResources().getDisplayMetrics().density;
  100. return (int) (dpValue * scale + 0.5f);
  101. }
  102.  
  103. @Override
  104. public void setMaxWidth(int maxpixels)
  105. {
  106. super.setMaxWidth(maxpixels);
  107. maxWidth = maxpixels;
  108. }
  109.  
  110. @Override
  111. public void setMinHeight(int minHeight)
  112. {
  113. super.setMinHeight(minHeight);
  114. this.minHeight = minHeight;
  115. }
  116.  
  117. @Override
  118. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  119. {
  120. if (useDefault)
  121. {
  122. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  123. return;
  124. }
  125. int width = , height = ;
  126.  
  127. int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  128. int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  129. int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  130. int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  131.  
  132. switch (widthMode)
  133. {
  134. case MeasureSpec.EXACTLY:
  135. width = widthSize;
  136. break;
  137. case MeasureSpec.AT_MOST:
  138. width = widthSize;
  139. break;
  140. case MeasureSpec.UNSPECIFIED:
  141.  
  142. ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
  143. width = displayMetrics.widthPixels;
  144. break;
  145. default:
  146. break;
  147. }
  148. if (maxWidth > )
  149. width = Math.min(width, maxWidth);
  150.  
  151. paint.setTextSize(this.getTextSize());
  152. paint.setColor(textColor);
  153. int realHeight = measureContentHeight((int) width);
  154.  
  155. //如果实际行宽少于预定的宽度,减少行宽以使其内容横向居中
  156. int leftPadding = getCompoundPaddingLeft();
  157. int rightPadding = getCompoundPaddingRight();
  158. width = Math.min(width, (int) lineWidthMax + leftPadding + rightPadding);
  159.  
  160. if (oneLineWidth > -)
  161. {
  162. width = oneLineWidth;
  163. }
  164. switch (heightMode)
  165. {
  166. case MeasureSpec.EXACTLY:
  167. height = heightSize;
  168. break;
  169. case MeasureSpec.AT_MOST:
  170. height = realHeight;
  171. break;
  172. case MeasureSpec.UNSPECIFIED:
  173. height = realHeight;
  174. break;
  175. default:
  176. break;
  177. }
  178.  
  179. height += getCompoundPaddingTop() + getCompoundPaddingBottom();
  180.  
  181. height = Math.max(height, minHeight);
  182.  
  183. setMeasuredDimension(width, height);
  184. }
  185.  
  186. @Override
  187. protected void onDraw(Canvas canvas)
  188. {
  189. if (useDefault)
  190. {
  191. super.onDraw(canvas);
  192. return;
  193. }
  194. if (contentList.isEmpty())
  195. return;
  196. int width;
  197.  
  198. Object ob;
  199.  
  200. int leftPadding = getCompoundPaddingLeft();
  201. int topPadding = getCompoundPaddingTop();
  202.  
  203. float height = + topPadding + lineSpacing;
  204. //只有一行时
  205. if (oneLineWidth != -)
  206. {
  207. height = getMeasuredHeight() / - contentList.get().height / ;
  208. }
  209.  
  210. for (LINE aContentList : contentList)
  211. {
  212. //绘制一行
  213. float realDrawedWidth = leftPadding;
  214. for (int j = ; j < aContentList.line.size(); j++)
  215. {
  216. ob = aContentList.line.get(j);
  217. width = aContentList.widthList.get(j);
  218.  
  219. if (ob instanceof String)
  220. {
  221. canvas.drawText((String) ob, realDrawedWidth, height + aContentList.height - paint.getFontMetrics().descent, paint);
  222. realDrawedWidth += width;
  223. }
  224. else if (ob instanceof SpanObject)
  225. {
  226. Object span = ((SpanObject) ob).span;
  227. if(span instanceof ImageSpan)
  228. {
  229. ImageSpan is = (ImageSpan) span;
  230. Drawable d = is.getDrawable();
  231.  
  232. int left = (int) (realDrawedWidth);
  233. int top = (int) height;
  234. int right = (int) (realDrawedWidth + width);
  235. int bottom = (int) (height + aContentList.height);
  236. d.setBounds(left, top, right, bottom);
  237. d.draw(canvas);
  238. realDrawedWidth += width;
  239. }
  240. else if(span instanceof BackgroundColorSpan)
  241. {
  242.  
  243. textBgColorPaint.setColor(((BackgroundColorSpan) span).getBackgroundColor());
  244. textBgColorPaint.setStyle(Style.FILL);
  245. textBgColorRect.left = (int) realDrawedWidth;
  246. int textHeight = (int) getTextSize();
  247. textBgColorRect.top = (int) (height + aContentList.height - textHeight - paint.getFontMetrics().descent);
  248. textBgColorRect.right = textBgColorRect.left+width;
  249. textBgColorRect.bottom = (int) (height + aContentList.height + lineSpacing - paint.getFontMetrics().descent);
  250. canvas.drawRect(textBgColorRect, textBgColorPaint);
  251. canvas.drawText(((SpanObject) ob).source.toString(), realDrawedWidth, height + aContentList.height - paint.getFontMetrics().descent, paint);
  252. realDrawedWidth += width;
  253. }
  254. else//做字符串处理
  255. {
  256. canvas.drawText(((SpanObject) ob).source.toString(), realDrawedWidth, height + aContentList.height - paint.getFontMetrics().descent, paint);
  257. realDrawedWidth += width;
  258. }
  259. }
  260.  
  261. }
  262. height += aContentList.height + lineSpacing;
  263. }
  264.  
  265. }
  266.  
  267. @Override
  268. public void setTextColor(int color)
  269. {
  270. super.setTextColor(color);
  271. textColor = color;
  272. }
  273.  
  274. /**
  275. * 用于带ImageSpan的文本内容所占高度测量
  276. * @param width 预定的宽度
  277. * @return 所需的高度
  278. */
  279. private int measureContentHeight(int width)
  280. {
  281. int cachedHeight = getCachedData(text.toString(), width);
  282.  
  283. if (cachedHeight > )
  284. {
  285. return cachedHeight;
  286. }
  287.  
  288. // 已绘的宽度
  289. float obWidth = ;
  290. float obHeight = ;
  291.  
  292. float textSize = this.getTextSize();
  293. FontMetrics fontMetrics = paint.getFontMetrics();
  294. //行高
  295. float lineHeight = fontMetrics.bottom - fontMetrics.top;
  296. //计算出的所需高度
  297. float height = lineSpacing;
  298.  
  299. int leftPadding = getCompoundPaddingLeft();
  300. int rightPadding = getCompoundPaddingRight();
  301.  
  302. float drawedWidth = ;
  303.  
  304. boolean splitFlag = false;//BackgroundColorSpan拆分用
  305.  
  306. width = width - leftPadding - rightPadding;
  307.  
  308. oneLineWidth = -;
  309.  
  310. contentList.clear();
  311.  
  312. StringBuilder sb;
  313.  
  314. LINE line = new LINE();
  315.  
  316. for (int i = ; i < obList.size(); i++)
  317. {
  318. Object ob = obList.get(i);
  319.  
  320. if (ob instanceof String)
  321. {
  322.  
  323. obWidth = paint.measureText((String) ob);
  324. obHeight = textSize;
  325. }
  326. else if (ob instanceof SpanObject)
  327. {
  328. Object span = ((SpanObject) ob).span;
  329. if(span instanceof ImageSpan)
  330. {
  331. Rect r = ((ImageSpan)span).getDrawable().getBounds();
  332. obWidth = r.right - r.left;
  333. obHeight = r.bottom - r.top;
  334. if (obHeight > lineHeight)
  335. lineHeight = obHeight;
  336. }
  337. else if(span instanceof BackgroundColorSpan)
  338. {
  339. String str = ((SpanObject) ob).source.toString();
  340. obWidth = paint.measureText(str);
  341. obHeight = textSize;
  342.  
  343. //如果太长,拆分
  344. int k= str.length()-;
  345. while(width - drawedWidth < obWidth)
  346. {
  347. obWidth = paint.measureText(str.substring(,k--));
  348. }
  349. if(k < str.length()-)
  350. {
  351. splitFlag = true;
  352. SpanObject so1 = new SpanObject();
  353. so1.start = ((SpanObject) ob).start;
  354. so1.end = so1.start + k;
  355. so1.source = str.substring(,k+);
  356. so1.span = ((SpanObject) ob).span;
  357.  
  358. SpanObject so2 = new SpanObject();
  359. so2.start = so1.end;
  360. so2.end = ((SpanObject) ob).end;
  361. so2.source = str.substring(k+,str.length());
  362. so2.span = ((SpanObject) ob).span;
  363.  
  364. ob = so1;
  365. obList.set(i,so2);
  366. i--;
  367. }
  368. }//做字符串处理
  369. else
  370. {
  371. String str = ((SpanObject) ob).source.toString();
  372. obWidth = paint.measureText(str);
  373. obHeight = textSize;
  374. }
  375. }
  376.  
  377. //这一行满了,存入contentList,新起一行
  378. if (width - drawedWidth < obWidth || splitFlag)
  379. {
  380. splitFlag = false;
  381. contentList.add(line);
  382.  
  383. if (drawedWidth > lineWidthMax)
  384. {
  385. lineWidthMax = drawedWidth;
  386. }
  387. drawedWidth = ;
  388. height += line.height + lineSpacing;
  389.  
  390. lineHeight = obHeight;
  391.  
  392. line = new LINE();
  393. }
  394.  
  395. drawedWidth += obWidth;
  396.  
  397. if (ob instanceof String && line.line.size() > && (line.line.get(line.line.size() - ) instanceof String))
  398. {
  399. int size = line.line.size();
  400. sb = new StringBuilder();
  401. sb.append(line.line.get(size - ));
  402. sb.append(ob);
  403. ob = sb.toString();
  404. obWidth = obWidth + line.widthList.get(size - );
  405. line.line.set(size - , ob);
  406. line.widthList.set(size - , (int) obWidth);
  407. line.height = (int) lineHeight;
  408.  
  409. }
  410. else
  411. {
  412. line.line.add(ob);
  413. line.widthList.add((int) obWidth);
  414. line.height = (int) lineHeight;
  415. }
  416.  
  417. }
  418.  
  419. if (drawedWidth > lineWidthMax)
  420. {
  421. lineWidthMax = drawedWidth;
  422. }
  423.  
  424. if (line != null && line.line.size() > )
  425. {
  426. contentList.add(line);
  427. height += lineHeight + lineSpacing;
  428. }
  429. if (contentList.size() <= )
  430. {
  431. oneLineWidth = (int) drawedWidth + leftPadding + rightPadding;
  432. height = lineSpacing + lineHeight + lineSpacing;
  433. }
  434.  
  435. cacheData(width, (int) height);
  436. return (int) height;
  437. }
  438.  
  439. /**
  440. * 获取缓存的测量数据,避免多次重复测量
  441. * @param text
  442. * @param width
  443. * @return height
  444. */
  445. @SuppressWarnings("unchecked")
  446. private int getCachedData(String text, int width)
  447. {
  448. SoftReference<MeasuredData> cache = measuredData.get(text);
  449. if (cache == null)
  450. return -;
  451. MeasuredData md = cache.get();
  452. if (md != null && md.textSize == this.getTextSize() && width == md.width)
  453. {
  454. lineWidthMax = md.lineWidthMax;
  455. contentList = (ArrayList<LINE>) md.contentList.clone();
  456. oneLineWidth = md.oneLineWidth;
  457.  
  458. StringBuilder sb = new StringBuilder();
  459. for (int i = ; i < contentList.size(); i++)
  460. {
  461. LINE line = contentList.get(i);
  462. sb.append(line.toString());
  463. }
  464. return md.measuredHeight;
  465. }
  466. else
  467. return -;
  468. }
  469.  
  470. /**
  471. * 缓存已测量的数据
  472. * @param width
  473. * @param height
  474. */
  475. @SuppressWarnings("unchecked")
  476. private void cacheData(int width, int height)
  477. {
  478. MeasuredData md = new MeasuredData();
  479. md.contentList = (ArrayList<LINE>) contentList.clone();
  480. md.textSize = this.getTextSize();
  481. md.lineWidthMax = lineWidthMax;
  482. md.oneLineWidth = oneLineWidth;
  483. md.measuredHeight = height;
  484. md.width = width;
  485. md.hashIndex = ++hashIndex;
  486.  
  487. StringBuilder sb = new StringBuilder();
  488. for (int i = ; i < contentList.size(); i++)
  489. {
  490. LINE line = contentList.get(i);
  491. sb.append(line.toString());
  492. }
  493.  
  494. SoftReference<MeasuredData> cache = new SoftReference<MeasuredData>(md);
  495. measuredData.put(text.toString(), cache);
  496. }
  497.  
  498. /**
  499. * 用本函数代替{@link #setText(CharSequence)}
  500. * @param cs
  501. */
  502. public void setMText(CharSequence cs)
  503. {
  504. text = cs;
  505.  
  506. obList.clear();
  507.  
  508. ArrayList<SpanObject> isList = new ArrayList<MTextView.SpanObject>();
  509. useDefault = false;
  510. if (cs instanceof SpannableString)
  511. {
  512. SpannableString ss = (SpannableString) cs;
  513. CharacterStyle[] spans = ss.getSpans(, ss.length(), CharacterStyle.class);
  514. for (int i = ; i < spans.length; i++)
  515. {
  516.  
  517. int s = ss.getSpanStart(spans[i]);
  518. int e = ss.getSpanEnd(spans[i]);
  519. SpanObject iS = new SpanObject();
  520. iS.span = spans[i];
  521. iS.start = s;
  522. iS.end = e;
  523. iS.source = ss.subSequence(s, e);
  524. isList.add(iS);
  525. }
  526. }
  527.  
  528. //对span进行排序,以免不同种类的span位置错乱
  529. SpanObject[] spanArray = new SpanObject[isList.size()];
  530. isList.toArray(spanArray);
  531. Arrays.sort(spanArray,,spanArray.length,new SpanObjectComparator());
  532. isList.clear();
  533. for(int i=;i<spanArray.length;i++)
  534. {
  535. isList.add(spanArray[i]);
  536. }
  537.  
  538. String str = cs.toString();
  539.  
  540. for (int i = , j = ; i < cs.length(); )
  541. {
  542. if (j < isList.size())
  543. {
  544. SpanObject is = isList.get(j);
  545. if (i < is.start)
  546. {
  547. Integer cp = str.codePointAt(i);
  548. //支持增补字符
  549. if (Character.isSupplementaryCodePoint(cp))
  550. {
  551. i += ;
  552. }
  553. else
  554. {
  555. i++;
  556. }
  557.  
  558. obList.add(new String(Character.toChars(cp)));
  559.  
  560. }
  561. else if (i >= is.start)
  562. {
  563. obList.add(is);
  564. j++;
  565. i = is.end;
  566. }
  567. }
  568. else
  569. {
  570. Integer cp = str.codePointAt(i);
  571. if (Character.isSupplementaryCodePoint(cp))
  572. {
  573. i += ;
  574. }
  575. else
  576. {
  577. i++;
  578. }
  579.  
  580. obList.add(new String(Character.toChars(cp)));
  581. }
  582. }
  583.  
  584. requestLayout();
  585. }
  586.  
  587. public void setUseDefault(boolean useDefault)
  588. {
  589. this.useDefault = useDefault;
  590. if (useDefault)
  591. {
  592. this.setText(text);
  593. this.setTextColor(textColor);
  594. }
  595. }
  596. /**
  597. * 设置行距
  598. * @param lineSpacingDP 行距,单位dp
  599. */
  600. public void setLineSpacingDP(int lineSpacingDP)
  601. {
  602. this.lineSpacingDP = lineSpacingDP;
  603. lineSpacing = dip2px(context, lineSpacingDP);
  604. }
  605. /**
  606. * 获取行距
  607. * @return 行距,单位dp
  608. */
  609. public int getLineSpacingDP()
  610. {
  611. return lineSpacingDP;
  612. }
  613. /**
  614. * @author huangwei
  615. * @version SocialClient 1.2.0
  616. * @功能: 存储Span对象及相关信息
  617. * @2014年5月27日
  618. * @下午5:21:37
  619. */
  620. class SpanObject
  621. {
  622. public Object span;
  623. public int start;
  624. public int end;
  625. public CharSequence source;
  626. }
  627. /**
  628. * @功能: 对SpanObject进行排序
  629. * @author huangwei
  630. * @2014年6月4日
  631. * @下午5:21:30
  632. * @version SocialClient 1.2.0
  633. */
  634. class SpanObjectComparator implements Comparator<SpanObject>
  635. {
  636. @Override
  637. public int compare(SpanObject lhs, SpanObject rhs)
  638. {
  639.  
  640. return lhs.start - rhs.start;
  641. }
  642.  
  643. }
  644. /**
  645. * @author huangwei
  646. * @version SocialClient 1.2.0
  647. * @功能: 存储测量好的一行数据
  648. * @2014年5月27日
  649. * @下午5:22:12
  650. */
  651. class LINE
  652. {
  653. public ArrayList<Object> line = new ArrayList<Object>();
  654. public ArrayList<Integer> widthList = new ArrayList<Integer>();
  655. public int height;
  656.  
  657. @Override
  658. public String toString()
  659. {
  660. StringBuilder sb = new StringBuilder("height:" + height + " ");
  661. for (int i = ; i < line.size(); i++)
  662. {
  663. sb.append(line.get(i) + ":" + widthList.get(i));
  664. }
  665. return sb.toString();
  666. }
  667.  
  668. }
  669.  
  670. /**
  671. * @author huangwei
  672. * @version SocialClient 1.2.0
  673. * @功能: 缓存的数据
  674. * @2014年5月27日
  675. * @下午5:22:25
  676. */
  677. class MeasuredData
  678. {
  679. public int measuredHeight;
  680. public float textSize;
  681. public int width;
  682. public float lineWidthMax;
  683. public int oneLineWidth;
  684. public int hashIndex;
  685. ArrayList<LINE> contentList;
  686.  
  687. }

为方便在ListView中使用(ListView反复上下滑动会多次重新onMeasure),加了缓存,相同的情况下可以不用重复在测量一次。

对于SpannableString,只支持了ImageSpan,有其它需要者可自行扩展

Demo:http://download.csdn.net/detail/yellowcath/7421147 或:https://github.com/yellowcath/MTextView.git (2014/6/4 更新 添加对BackGroundColorSpan的支持,修复一个会导致最后一行最后一个图形显示不全的bug)

代码:这里

Android 自绘TextView解决提前换行问题,支持图文混排的更多相关文章

  1. Android TextView中图文混排设置行间距导致高度不一致问题解决

    最近项目中需要实现一个评论带表情的功能,刚开始一切顺利,非常easy,突然有一天发现文字跟表情混排的时候,TextView中图文高度不一致,excuse...什么鬼,之前明明测试过图文混排,不存在这个 ...

  2. Android中Textview显示Html,图文混排,支持图片点击放大

    本文首发于网易云社区 对于呈现Html文本来说,Android提供的Webview控件可以得到很好的效果,但使用Webview控件的弊端是效率相对比较低,对于呈现简单的html文本的话,杀鸡不必使用牛 ...

  3. android:怎样在TextView实现图文混排

    我们通常在TextView文本中设置文字.但是怎样设置图文混排呢? 我就在这里写一个样例 .我们须要用到一点简单的HTML知识 在TextView中预订了一些类似HTML的标签,通过标签能够使Text ...

  4. 使用android SpannableStringBuilder实现图文混排

    项目开发中需要实现这种效果 多余两行,两行最后是省略号,省略号后面是下拉更多 之前用过的是Html.fromHtml去处理图文混排的,仅仅是文字后图片或者文字颜色字体什么的, 但是这里需要在最后文字的 ...

  5. 使用android SpannableStringBuilder实现图文混排,看到许多其他

    项目开发需要达到这种效果 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmFuY3lsb3ZlamF2YQ==/font/5a6L5L2T/fontsiz ...

  6. TextView + Spanned实现图文混排以及图片点击交互

    最近要实现图文混排的需求,webview过大,所以想到了用SpannableStringBuilder来实现. 不过参考了大量国内文章,大多数是教你如何实现图文混排,并没有提及图片点击交互的.有翻阅了 ...

  7. Android自动解析html带图片,实现图文混排

    在android中,如何将html代码转换为text,然后显示在textview中呢,有一个简单直接的方法: textView.setText(Html.fromHtml(content)); 然而用 ...

  8. 仿小米便签图文混排 EditText解决尾部插入文字bug

    一直想实现像小米便签那样的图文混排效果,收集网上的办法无非三种: 1.自定义布局,每张图片是一个ImageView,插入图片后插入EditText,缺点是实现复杂,不能像小米便签那样同时选中图片和文字 ...

  9. Android图文混排-实现EditText图文混合插入上传

    前段时间做了一个Android会议管理系统,项目需求涉及到EditText的图文混排,如图: 在上图的"会议详情"中.须要支持文本和图片的混合插入,下图演示输入的演示样例: 当会议 ...

随机推荐

  1. nginx上传文件

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. VS2010远程调试

    1, A:调试机. B:远端被调试机. 2, 从A机的VS2010的安装目录里面,找到../Remote Debugger文件,复制到B机. 3, 启动B机上复制过来的目录下的msvsmon.exe ...

  3. linux make clean

    make clean仅仅是清除之前编译的可执行文件及配置文件. 而make distclean要清除所有生成的文件. Makefile 在符合GNU Makefiel惯例的Makefile中,包含了一 ...

  4. struts2全注解Action配置

  5. EasyUI –tree、combotree学习总结

    EasyUI –tree.combotree学习总结 一.   tree总结 (一).tree基本使用 tree控件是web页面中将数据分层一树形结构显示的. 使用$.fn.tree.defaults ...

  6. [Interview][CodingExam]

    這次去Interview, 其中有一個公司 把我列為 2/25的考慮對象, 在Final 的 1/2, 我被刷掉了. 因為第一輪的程式,我稍微google了一下,參考了既有的寫法. 即使第二輪我用完全 ...

  7. 做了codility网站上一题:CountBoundedSlices

    在写上一随笔之前,在Codility网站上还做了一个道题(非Demo题):CountBoundedSlices,得了60分(害臊呀).今天又重新做了一下这个算法,性能提高了不少,但由于此题不是Demo ...

  8. java 中的this关键字的几种用法

    转自:http://blog.csdn.net/anmei2010/article/details/4091227 1.     当成员变量和局部变量重名时,在方法中使用this时,表示的是该方法所在 ...

  9. ems lite 客户端远程连接mysql server

    在本地用ems客户端远程连接虚拟机上的mysql server,弹出客户端没有权限访问mysql server.使用下面方法进行设置:mysql> select host,user,passwo ...

  10. P1654: [Usaco2006 Jan]The Cow Prom 奶牛舞会

    裸的强连通 ; type node=record f,t:longint; end; var n,m,dgr,i,u,v,num,ans:longint; bfsdgr,low,head,f:arra ...