以下来写IntroView的onMeasureHeight:
private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); Paint paint = new Paint(); paint.setTextSize(getTextSize()); paint.setAntiAlias(true); paint.setStyle(Style.FILL); displayHeight = -paint.ascent() + paint.descent() + 2;//<span style="font-family: Arial, Helvetica, sans-serif;">-paint.ascent() 为字体高于baseLine的部分 paint.descent() 为低于baseline的部分</span> measureList(paint, titleList, 0); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { if (specMode == MeasureSpec.AT_MOST) { result = Math.min((int)displayHeight, specSize); } else{ result = (int)displayHeight; } } return result; } private float measureList(Paint paint, ArrayList<LinkInfo> infoList, float StartX){ if(infoList == null || infoList.size() == 0){ displayHeight = StartX; return StartX; } float x = StartX; int len = infoList.size(); //FontMetrics fontMetrics = paint.getFontMetrics(); float fontHeight = -paint.ascent() + paint.descent();//fontMetrics.bottom - fontMetrics.top; for(int i = 0; i < len; i++){ LinkInfo info = infoList.get(i); //这里開始计算每一个链接的点击区域 info.getRectFList().clear(); if(info.isFace()){//表情符排版 Bitmap faceBmp = null; if(mFaceType == MSG_FACE_TYPE) { faceBmp = MessageFaceModel.getInstance().getFaceIcon(info.getContent()); } if(faceBmp != null){ int xLen = faceBmp.getWidth() + 4;//表情图标左右各空两个像素 if(x + xLen >= displayWidth){ displayHeight += fontHeight; x = 0; } x += xLen; } continue; } String strContent = info.getContent(); strContent = strContent.replaceAll(" ", " "); float xLen = paint.measureText(strContent); //这里处理换行的情况 if(x + xLen >= displayWidth){ float startX = x; int lenStr = strContent.length(); for(int j = 0; j < lenStr; j++){ float width = paint.measureText(strContent, j, j + 1); if(x + width >= displayWidth){ RectF rectF = new RectF(); rectF.set(startX, displayHeight - fontHeight, x, displayHeight); info.addRectF(rectF); displayHeight += fontHeight; x = width;//下一行的X方向起始偏移量,width是一个字符的偏移量。所以这里rectF仅仅在换行的时候运行一次 startX = 0; }else{ x += width; } } RectF rectF = new RectF(); //假设换行则startX为0,x为第下一行的宽度 rectF.set(startX, displayHeight - fontHeight, x - startX, displayHeight); info.addRectF(rectF); } else{ RectF rectF = new RectF(); rectF.set(x, displayHeight - fontHeight, x + xLen, displayHeight); info.addRectF(rectF); x += xLen; } } return x; }
onDraw:
@Override protected void onDraw(Canvas canvas){ super.onDraw(canvas); try{ if(titleList == null || titleList.size() == 0){ return; } curLen = 0; Paint paint = new Paint(); paint.setTextSize(getTextSize()); paint.setAntiAlias(true); paint.setStyle(Style.FILL); float y = -paint.ascent();//(fontMetrics.bottom - fontMetrics.ascent); y = drawList(canvas, paint, titleList, y); }catch(Exception ex){ } } private float drawList(Canvas canvas, Paint paint, ArrayList<LinkInfo> infoList, float StartY){ if(infoList == null || infoList.size() == 0){ return StartY; } float y = StartY; float fontHeight = -paint.ascent() + paint.descent();//fontMetrics.bottom - fontMetrics.ascent; int len = infoList.size(); int color = getCurrentTextColor(); for(int i = 0; i < len; i++){ LinkInfo info = infoList.get(i); if(info.isFace()){//表情符排版 Bitmap faceBmp = null; if(mFaceType == MSG_FACE_TYPE) { faceBmp = MessageFaceModel.getInstance().getFaceIcon(info.getContent()); } if(faceBmp != null){ int xLen = faceBmp.getWidth() + 4;//表情图标左右各空两个像素 if(curLen + xLen >= displayWidth){ y += fontHeight; curLen = 0; } canvas.drawBitmap(faceBmp, curLen + 2, y - fontHeight + 4, paint); curLen += xLen; } continue; } String strContent = info.getContent(); if(mFaceType == MSG_FACE_TYPE && strContent.startsWith(" ")){ y += fontHeight; curLen = 0; } strContent = strContent.replaceAll(" ", " "); if((info.isEmail() || info.isPhoneNumber() || info.isWebUrl()) && info.isSelected()){ paintSelectRectF(canvas, paint, info); } float xLen = paint.measureText(strContent); int starLen = 0; if(info.isCommonString()){ paint.setColor(color); }else{ paint.setColor(getResources().getColor(R.drawable.blue1)); } if(curLen + xLen + starLen >= displayWidth){ int lenStr = strContent.length(); for(int j = 0; j < lenStr; j++){ float width = paint.measureText(strContent, j, j + 1); if(curLen + width >= displayWidth){ y += fontHeight; curLen = 0; } canvas.drawText(strContent, j, j + 1, curLen, y, paint); curLen += width; } }else{ canvas.drawText(strContent, curLen, y, paint); curLen += xLen; } } return y; }
onTouch事件,能够点击Text里面的链接(email,phone)
@Override public boolean onTouchEvent(MotionEvent event){ super.onTouchEvent(event); if(!this.isEnabled() || Listener == null){ return false; } if(event.getAction() == MotionEvent.ACTION_UP){ float x = event.getX(); float y = event.getY(); boolean flag = cancelCurInfo(x, y); if(flag){ return false;//up时的坐标已经不在down时记录的Link对象中,则直接返回 } if(clickLink(titleList, x, y, MotionEvent.ACTION_UP)){ //Log.i("curInfo", "ACTION_UP"); return true; } } else if(event.getAction() == MotionEvent.ACTION_DOWN){ float x = event.getX(); float y = event.getY(); boolean flag = clickLink(titleList, x, y, MotionEvent.ACTION_DOWN); if(flag){ //Log.i("curInfo", "ACTION_DOWN"); return true; } } else{ float x = event.getX(); float y = event.getY(); cancelCurInfo(x, y); } return false; } private void paintSelectRectF(Canvas canvas, Paint paint, LinkInfo info){ ArrayList<RectF> rectList = info.getRectFList(); int len = rectList.size(); for(int i = 0; i < len; i++){ RectF rectF = rectList.get(i); paint.setColor(this.getContext().getResources().getColor(R.drawable.gray3)); canvas.drawRect(rectF.left, rectF.top - 2, rectF.right, rectF.bottom - 2, paint); } } /** x,y所代表的点已经不在当前Link对象中,则清除掉当前Link对象的选中效果 */ private boolean cancelCurInfo(float x, float y){ if(curInfo == null){ return true; } if(!curInfo.contains(x, y)){ curInfo.setSelected(false); this.invalidate(); curInfo = null; return true; } return false; } private boolean clickLink(ArrayList<LinkInfo> infoList, float x, float y, int action){ if(infoList == null){ return false; } int len = infoList.size(); for(int i = 0; i < len; i++){ LinkInfo info = infoList.get(i); if(info.isCommonString()){ continue; } if(info.contains(x, y)){ if(action == MotionEvent.ACTION_DOWN){ info.setSelected(true); this.invalidate(); this.curInfo = info; } else if(action == MotionEvent.ACTION_UP){ this.curInfo = null; info.setSelected(false); this.invalidate(); Listener.onClick(info); } return true; } } return false; }
Listener:
private OnClickLinkListener Listener; public void setOnClickLinkListener(OnClickLinkListener listener){ this.Listener = listener; } public interface OnClickLinkListener{ /**点击了动态中的某个链接对象*/ public abstract void onClick(LinkInfo linkInfo); }