首页 > 代码库 > android如果重写onDraw实现一个类似TextView可以显示表情和链接的控件(二)

android如果重写onDraw实现一个类似TextView可以显示表情和链接的控件(二)

下面来写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("\n", " ");
			
			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("\n")){
				y += fontHeight;
				curLen = 0;
			}
				
			strContent = strContent.replaceAll("\n", " ");
			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);
	}