android假设重写onDraw实现一个相似TextView能够显示表情和链接的控件

Posted phlsheji

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android假设重写onDraw实现一个相似TextView能够显示表情和链接的控件相关的知识,希望对你有一定的参考价值。

先看效果图:

技术分享

写一个超连接支持的对象:

/**作为超连接显示的对象*/
public class LinkInfo implements Comparable<LinkInfo>{
    private String content;
    private String type;
    private String id;
    private boolean bIsFace = false;
    private boolean bSelected = false;
    public static final String EMAIL = "Email";
    public static final String WEBURL = "WebUrl";
    public static final String PHONENUMBER = "PhoneNumber";
    private int startIndex;
    private int endIndex;
}

对于下面的字符串做这种解析:

		String s = "(#大笑)%$%$%3434343434343$%$%[email protected]$dfsfsfsdffds^15959224872)[email protected]&&fefrewafrewfjwio([email protected]()()()[email protected]";

	private static Pattern EMAIL_PATTERN = Patterns.EMAIL_ADDRESS;
	private static Pattern PHONE_PATTERN = Patterns.PHONE;
	private static Pattern WEBURL_PATTERN = Patterns.WEB_URL;	
public static ArrayList<LinkInfo> parseStr(String strLink) {
		if(TextUtils.isEmpty(strLink)){
    		return null;
    	}
		ArrayList<LinkInfo> resultList = new ArrayList<LinkInfo>();
    	ArrayList<LinkInfo> infoList = null;
    	try{
    		infoList = new ArrayList<LinkInfo>();
    	<span style="white-space:pre">	</span><strong>	Matcher matcher = EMAIL_PATTERN.matcher(strLink); //寻找字符串里的email的位置</strong>
    		int begin = 0;
    		while(matcher.find()) {
    			int start = matcher.start();
    			int end = matcher.end();
        		LinkInfo info = new LinkInfo();
        		info.setStartIndex(start);
        		info.setEndIndex(end);
            	info.setContent(matcher.group());
            	info.setType(LinkInfo.EMAIL);
        		infoList.add(info);
    		}
    		
    		<strong>Matcher matcher1 = PHONE_PATTERN.matcher(strLink);//寻找字符串里的号码的位置</strong>
    		while(matcher1.find()) {
    			int start = matcher1.start();
    			int end = matcher1.end();
        		LinkInfo info = new LinkInfo();
        		info.setStartIndex(start);
        		info.setEndIndex(end);
            	info.setContent(matcher1.group());
            	info.setType(LinkInfo.PHONENUMBER);
        		infoList.add(info);
    		}
    		
    		//(#大笑)
    		Pattern pattern = Pattern.compile("(\\(#\\S{1,2}\\))");
    	    Matcher matcher2 = pattern.matcher(strLink);
    		 while(matcher2.find()) {
    			int start = matcher2.start();
    			int end = matcher2.end();
    			System.out.println("====start="+start+"end="+end+"match group="+matcher2.group());
        		LinkInfo info = new LinkInfo();
        		info.setStartIndex(start);
        		info.setEndIndex(end);
            	info.setContent(matcher2.group());
            	info.setFace(true);
        		infoList.add(info);
    		}
    		
    		Collections.sort(infoList);
    		int last = 0;
    		for(int i=0;i<infoList.size();i++) {
    			LinkInfo info = infoList.get(i);
    			if(begin != info.getStartIndex()){
    				LinkInfo infoBefore = new LinkInfo();
        			infoBefore.setContent(strLink.substring(begin,info.getStartIndex()));
        			resultList.add(infoBefore);
    			}
    			resultList.add(info);
    			begin = info.getEndIndex();
    			last = info.getEndIndex();
    		}
    		if(last < strLink.length()) {
    			LinkInfo info = new LinkInfo();
        		info.setContent(strLink.substring(last,strLink.length()));
        		resultList.add(info);
    		}
    		
    		
    	}catch(Exception ex){
    		ex.printStackTrace();
    	}
    	return resultList;
	}
	

activity的Layout:

<?

xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/white" > <include android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:id="@+id/header" layout="@layout/news_header" /> <RelativeLayout android:id="@+id/title" android:layout_above="@id/header" android:layout_width="fill_parent" android:layout_height="match_parent" android:background="@drawable/white" android:gravity="center_horizontal" > <com.kaixin001.view.IntroView android:id="@+id/news_item_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="20dp" android:enabled="true" android:textColor="@drawable/black" android:textSize="16sp" android:textStyle="bold" /> </RelativeLayout> </RelativeLayout>



IntroView有这样一个方法来载入字符串:
<pre name="code" class="java">public class IntroView extends TextView {

	private ArrayList<LinkInfo> titleList;
	private int displayWidth = 0;
	private float displayHeight = 0;
	private float curLen = 0;
	private Bitmap starBmp;
	private Bitmap selectedBmp;
	private float posX = 0;
	private float posY = 0;
	private LinkInfo curInfo;//当前点击的Link对象
	private OnClickLinkListener Listener;
	
	private String mFaceType = MSG_FACE_TYPE;
	public static final String MSG_FACE_TYPE = "msgtype";
	public static final String STATUS_FACE_TYPE = "statustype";
	
	public IntroView(Context context) {
		super(context);
	}

	public IntroView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public IntroView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}
	
	
	public void setTitleList(ArrayList<LinkInfo> titleList){
		this.titleList = titleList;
		displayHeight = 0;
        requestLayout();
	}
}<span style="font-family: Arial, Helvetica, sans-serif;">                                    </span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family:Arial, Helvetica, sans-serif;">activity里这样来调用:</span>
<span style="font-family:Arial, Helvetica, sans-serif;"></span>
IntroView news_item_text = (IntroView)findViewById(R.id.news_item_text);
//不支持字符串里有\n
String s = "(#大笑)%$%$%3434343434343$%$%[email protected]$dfsfsfsdffds^15959224872)[email protected]&&fefrewafrewfjwio([email protected]()()()[email protected]";
news_item_text.setTitleList(ParseNewsInfoUtil.parseStr(s));
news_item_text.setOnClickLinkListener(this);
<span style="font-family:Arial, Helvetica, sans-serif;">
</span>
</pre><pre code_snippet_id="447070" snippet_file_name="blog_20140810_12_4943450" name="code" class="java"><span style="font-family: Arial, Helvetica, sans-serif;">IntroView的主题思想是在onMeasure里的measureWidth和measureHeight时来获取ArrayList<LinkInfo> titleList每一个LinkInfo的位置信息。并获取这个IntroView的高度和宽度,</span>
然后onDraw的时候通过循环来绘制titleList的每一个LinkInfo
</pre><pre code_snippet_id="447070" snippet_file_name="blog_20140810_15_8956611" name="code" class="java"><strong><span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
<span style="white-space:pre">		</span>super.onMeasure(widthMeasureSpec, heightMeasureSpec);
<span style="white-space:pre">		</span>try{
<span style="white-space:pre">			</span>int width = measureWidth(widthMeasureSpec);
<span style="white-space:pre">			</span>int height = measureHeight(heightMeasureSpec);
<span style="white-space:pre">			</span>setMeasuredDimension(width, height);
<span style="white-space:pre">		</span>}catch(Exception ex){
<span style="white-space:pre">			</span>ex.printStackTrace();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}</strong>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;"> <span style="white-space:pre">			</span></span><pre name="code" class="java"> private int measureWidth(int measureSpec) {
        int result = 0;
        
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int initialWidth = getPaddingLeft() + getPaddingRight();
        int width = initialWidth;
        int maxWidth = 0;
        
        TextPaint tempPaint = null;

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            if (tempPaint == null) {
                tempPaint = new TextPaint();
                tempPaint.setStyle(Style.FILL);
                tempPaint.setAntiAlias(true);
                tempPaint.setTextSize(getTextSize());
            }
            
            if (titleList != null && titleList.size() > 0) {
                maxWidth = specSize;
                
                int size = titleList.size();
forLable:                
                for (int i = 0; i < size; i++) {
                    LinkInfo info = titleList.get(i);
                    
                    if (info.isFace()) {
                    	Bitmap faceBmp = null;
        				if(mFaceType == MSG_FACE_TYPE) {
        					faceBmp = MessageFaceModel.getInstance().getFaceIcon(info.getContent());
        				}
                        if (faceBmp != null) {
                            int wSize = faceBmp.getWidth() + 4;
                        <strong>    if (width + wSize >= maxWidth)</strong> { //这里表示已经计算的宽度大于控件的宽度,那就返回maxWidth就能够了
                                width = maxWidth;
                                break forLable;
                            }
                            width += wSize;
                        }
                        continue;
                    }
                    
                    String text = info.getContent();
                    text = text.replaceAll("\n", " "); //由于该控件不支持\n,所以把这个换成空格
                    if (!TextUtils.isEmpty(text)) {
                        float wSize = tempPaint.measureText(text);
                        if (width + wSize >= maxWidth) {
                            width = maxWidth;
                            break forLable;
                        }
                        width += wSize;
                    }
                    
                }
            }
            
            result = width;
        }

        displayWidth = result;
        return result;
    }


<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">                                                                                </span>












以上是关于android假设重写onDraw实现一个相似TextView能够显示表情和链接的控件的主要内容,如果未能解决你的问题,请参考以下文章

Android面试自定义View

android 自己定义控件

《Android开发艺术探索》之View的事件体系和工作原理学习笔记

Android View的绘制

Android 自定义View

Android 自己定义控件开发入门