`
zhangning290
  • 浏览: 11195 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Textview加入Intent、表情,点击跳转Activity

阅读更多

      (第二届 Google 暑期大学生博客分享大赛 - 2011 Android 成长篇

      做过web开发的人应该都知道,在HTML里支持<a>标签在文本里插入一个链接,点击后跳转;并且有<img>标签可以插入图片。Android开发是否也支持呢?带着这个疑问,我们去APIDemos探索一下。OK,在com.example.android.apis.text.link这个类里,官方演示了TextView支持的一些链接,上个图:


 

      看来TextView是支持链接跳转的,不过做Android开发的应该都知道,android的View载体是Activity,能不能支持activity跳转呢,很遗憾,不支持。

      不过无所谓,Android很有爱,开源的,理解了原理后我们自己去做,这也是我写本篇文章的主要目的,"授之以鱼,不如授之以渔",希望大家在遇到相似问题时能像我这样去分析源码,然后找出解决办法(或者大家可以提出更好的方法),另外,文中如有不妥的地方,也欢迎大家批评指正。先上效果图:点击左边的链接后跳转到右边。



  

 

    现在我们开始开发吧!第一步,研究相关的源代码吧。通过跟踪TextView的源码,我们发现TextView支持的链接是由android.text.style.URLSpan这个类实现的,它重写了一个onClick方法:

 

public void onClick(View widget) {
        Uri uri = Uri.parse(getURL());
        Context context = widget.getContext();
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
        context.startActivity(intent);
    }

      大家看到了吧startActivity,多么熟悉的方法。既然它能实现,为什么我们不能呢,答案是可以的。我们接着跟踪代码,可以看到URLSpan其实继承的是android.text.style.ClickableSpan,我们来看一下他的源码:

 

public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {

    /**
     * Performs the click action associated with this span.
     */
    public abstract void onClick(View widget);
   
    /**
     * Makes the text underlined and in the link color.
     */
    @Override
    public void updateDrawState(TextPaint ds) {
        ds.setColor(ds.linkColor);
        ds.setUnderlineText(true);
    }
}

 是不是有点眉目了,我们直接继承这个类,重写他的方法不就可以了吗?大胆假设,小心求证,我们新建一个类:

 

import android.content.Context;
import android.content.Intent;
import android.text.TextPaint;
import android.text.style.ClickableSpan;
import android.view.View;

/**
 * If an object of this type is attached to the text of a TextView with a
 * movement method of LinkMovementMethod, the affected spans of text can be
 * selected. If clicked, the {@link #onClick} method will be called.
 * 
 * @author 张宁
 */
public class MyClickableSpan extends ClickableSpan {

	int color = -1;
	private Context context;
	private Intent intent;

	public MyClickableSpan(Context context, Intent intent) {
		this(-1, context, intent);
	}

	/**
	 * constructor
	 * @param color the link color
	 * @param context
	 * @param intent
	 */
	public MyClickableSpan(int color, Context context, Intent intent) {
		if (color!=-1) {
			this.color = color;
		}
		this.context = context;
		this.intent = intent;
	}

	/**
	 * Performs the click action associated with this span.
	 */
	public void onClick(View widget){
		context.startActivity(intent);
	};

	/**
	 * Makes the text without underline.
	 */
	@Override
	public void updateDrawState(TextPaint ds) {
		if (color == -1) {
			ds.setColor(ds.linkColor);
		} else {
			ds.setColor(color);
		}
		ds.setUnderlineText(false);
	}
}

 

      在这个类里,我们重写了onClick事件,实现了Activity的跳转,并且去掉了下划线。Ok,第一个目的就达到了,下面我们来看一下如何在TextView里加入表情。

      这个就比较复杂了,因为TextView只能在其上下左右方向加入图片,是由Drawables这个类实现的,而我们想要的效果是在中间也可以插入,看来这次TextView插入图片源码帮不了我们了。不过我们可以去android.text这个包里去找别的类,大家可以看到在这个包里有一个Html类,做过web开发的应该可以想到什么吧?在文章开头已经提到了Html的<img>标签可以插入图片,那这个类是否提供这个功能呢?带着这个疑问我们可以进去看看,其中有个接口:

 

 /**
     * Retrieves images for HTML &lt;img&gt; tags.
     */
    public static interface ImageGetter {
        /**
         * This methos is called when the HTML parser encounters an
         * &lt;img&gt; tag.  The <code>source</code> argument is the
         * string from the "src" attribute; the return value should be
         * a Drawable representation of the image or <code>null</code>
         * for a generic replacement image.  Make sure you call
         * setBounds() on your Drawable if it doesn't already have
         * its bounds set.
         */
        public Drawable getDrawable(String source);
    }

 

     看到<code>source</code>这个没,熟悉吧,结合URLSpan的用法,我们是否可以配合Spanned实现一个

ImageSpan呢?OK,上代码:

 

import java.util.Map;
import java.util.Set;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.text.Spanned;
import android.text.Html.ImageGetter;

/**
 * this is a class which defining a spanned with image
 * @author 张宁
 *
 */
public class ImageSpan {
	
	/**
	 * the map of face.
	 */
	private Map<String, String> faceMap;
	private Context context;
	
	public ImageSpan(Context context, Map<String, String> faceMap){
		this.context = context;
		this.faceMap = faceMap;
	} 

	/**
	 * get the image by the given key
	 */
	private ImageGetter imageGetter = new Html.ImageGetter() {
		@Override
		public Drawable getDrawable(String source) {
			Drawable drawable = null;
			String sourceName = context.getPackageName() + ":drawable/"
					+ source;
			int id = context.getResources().getIdentifier(sourceName, null, null);
			if (id != 0) {
				drawable = context.getResources().getDrawable(id);
				if (drawable != null) {
					drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
							drawable.getIntrinsicHeight());
				}
			}
			return drawable;
		}
	};
	
	/**
	 * return a {@link Spanned} with image
	 * @param text
	 * @return
	 */
	public Spanned getImageSpan(CharSequence text){
		String cs = text.toString();
		if (faceMap != null) {
			Set<String> keys = faceMap.keySet();
			for (String key : keys) {
				if (cs.contains(key)) {
					cs = cs.replace(key, "<img src='" + faceMap.get(key) + "'>");
				}
			}
		}
		return Html.fromHtml(cs, imageGetter, null);
	}

}

 

      到目前为止可以说关键代码都已经实现了,但是会有人问,我该如何使用这两个类呢?下面,我们在实现一个工具类来封装这两个类的方法,以方便调用:

 

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.content.Context;
import android.content.Intent;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.widget.EditText;
import android.widget.TextView;

/**
 * TextView with intent that can redirect to a new activity
 * 
 * @author 张宁
 * 
 */
public class CustomTextView {

	private static Map<String, String> faceMap;

	static {
		faceMap = new HashMap<String, String>();
		faceMap.put("[哭]", "face_1");
		faceMap.put("[怒]", "face_2");
	}

	/**
	 * make textview a clickable textview<br>
	 * Note: make true the order of textList and intentList are mapped
	 * 
	 * @param context
	 * @param textView
	 * @param textList
	 *            the text should be set to this textview,not null
	 * @param intentList
	 *            the intent map to the text, if the text have no intent mapped
	 *            to, please set a null value.Or it will happen some unknown
	 *            error.<br>
	 *            not null
	 */
	public static void setClickableTextView(Context context, TextView textView,
			List<String> textList, List<Intent> intentList) {
		if (textList == null || intentList == null) {
			return;
		}
		SpannableStringBuilder builder = new SpannableStringBuilder();
		int end = -1, length = -1;
		int size = textList.size();
		Intent intent;
		for (int i = 0; i < size; i++) {
			String text = textList.get(i);
			if (TextUtils.isEmpty(text)) {
				continue;
			}
			builder.append(textList.get(i));
			if ((intent = intentList.get(i)) != null) {
				end = builder.length();
				length = textList.get(i).length();
				builder.setSpan(getClickableSpan(context, intent),
						end - length, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
			}
			builder.append(" ");
		}
		textView.setText(builder);
		textView.setFocusable(true);
		textView.setMovementMethod(LinkMovementMethod.getInstance());
	}
	
	/**
	 *  make textview a clickable textview<br>
	 *  Note: make true the order of textList and intentList are mapped
	 * @param context
	 * @param textView
	 * @param text
	 * @param intent
	 */
	public static void setClickableTextView(Context context, TextView textView,
			String text, Intent intent) {
		SpannableStringBuilder builder = new SpannableStringBuilder(text);
		builder.setSpan(getClickableSpan(context, intent), 0, text.length(), 
				Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
		textView.setText(builder);
		textView.setMovementMethod(LinkMovementMethod.getInstance());
	}

	/**
	 * make TextView a View with image at any index  
	 * @param context
	 * @param textView
	 * @param textList
	 */
	public static void setImgTextView(Context context, TextView textView,
			List<String> textList) {
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < textList.size(); i++) {
			builder.append(textList.get(i)).append(" ");
		}
		setImgTextView(context, textView, builder.toString());

	}

	/**
	 * make TextView a View with image at any index  
	 * @param context
	 * @param textView
	 * @param text
	 */
	public static void setImgTextView(Context context, TextView textView,
			String text) {
		ImageSpan imageSpan = new ImageSpan(context, faceMap);
		Spanned spanned = imageSpan.getImageSpan(text);
		textView.setText(spanned);
	}
	
	/**
	 * make EditText a View with image at any index  
	 * @param context
	 * @param EditText
	 * @param text
	 */
	public static void setImgTextView(Context context, EditText editText,
			String text) {
		ImageSpan imageSpan = new ImageSpan(context, faceMap);
		Spanned spanned = imageSpan.getImageSpan(text);
		editText.setText(spanned);
	}

	/**
	 * return a custom ClickableSpan
	 * 
	 * @param context
	 * @param intent
	 * @return
	 */
	public static MyClickableSpan getClickableSpan(Context context,
			Intent intent) {
		return new MyClickableSpan(context, intent);
	}

	/**
	 * make textview a clickable textview with image<br>
	 * Note: make true the order of textList and intentList are mapped
	 * 
	 * @param context
	 *            not null
	 * @param haveImg
	 *            whether this is image in the text,not null
	 * @param textView
	 *            not null
	 * @param textList
	 *            the text should be set to this textview,not null
	 * @param intentList
	 *            the intent map to the text, if the text have no intent mapped
	 *            to, please set a null value.Or it will happen some unknown
	 *            error.<br>
	 *            allow null
	 */
	public static void setCustomText(Context context, Boolean haveImg,
			TextView textView, List<String> textList, List<Intent> intentList) {
		SpannableStringBuilder builder = new SpannableStringBuilder();
		int end = -1, length = -1;
		if (intentList != null) {
			int size = textList.size();
			Intent intent;
			for (int i = 0; i < size; i++) {
				String text = textList.get(i);
				if (TextUtils.isEmpty(text)) {
					continue;
				}
				builder.append(textList.get(i));
				if ((intent = intentList.get(i)) != null) {
					end = builder.length();
					length = textList.get(i).length();
					builder.setSpan(getClickableSpan(context, intent), end
							- length, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
				}
				builder.append(" ");
			}
		} else {
			for (String text : textList) {
				builder.append(text).append(" ");
			}
		}
		if (haveImg) {
			ImageSpan imageSpan = new ImageSpan(context, faceMap);
			Spanned spanned = imageSpan.getImageSpan(builder);
			textView.setText(spanned);
		} else {
			textView.setText(builder);
		}
		textView.setMovementMethod(LinkMovementMethod.getInstance());

	}

}

 

    有了这个类,我们就可以方便的实现在TextView中插入Intent和表情了,甚至不用管底层是怎样实现的,也降低了代码的耦合度。但是又回到我写这篇文章的目的:希望大家能得到“渔”而不仅仅是“鱼”。

    Ok,任务完成。源码奉上。

注:原创作品,转载请标明出处:

 

 http://zhangning290.iteye.com/blog/1134286

 

 

  • 大小: 17.6 KB
  • 大小: 15.9 KB
  • 大小: 34.9 KB
分享到:
评论
3 楼 Evilover3 2014-03-12  
确实学习到不少
2 楼 hgl718075399 2012-07-06  
相当不错哦!!!!!
1 楼 lenomon 2012-04-07  

相关推荐

    android textview 部分文本变色,点击跳转

    在Android开发中,`TextView`是用于展示文本的常用组件,但有时我们希望部分文本具有不同的样式,比如颜色不同,甚至能响应用户的点击事件,跳转到特定的链接。这通常涉及到`SpannableString`、`ClickableSpan`等...

    Android如何通过TextView实现超链接的跳转

    当需要在TextView中嵌入超链接并实现点击跳转时,开发者通常会利用Android的`TextView`特性和`Linkify`类来完成这一功能。下面我们将详细介绍如何通过TextView实现超链接的跳转,并结合提供的`HyperLinkDemo`示例...

    仿微博获取到TextView中的url地址并且用“网址链接”替换,点击跳转

    本教程将深入探讨如何在TextView中检测并替换URL,使其成为可点击的链接,点击后能够跳转到相应的网页。这个过程涉及到Java语言、Android SDK以及TextView的相关知识。 首先,我们需要理解TextView的基本用法。...

    TextView单击链接Activity

    而当我们需要在`TextView`中的文本中嵌入可点击的链接,以触发特定的行为,如跳转到另一个`Activity`,这时就需要用到特殊的技术。本篇文章将详细讲解如何实现`TextView`点击链接启动新`Activity`的功能。 首先,...

    android 从activity用intent跳转到listactivity并在其中用simpleadapter在listview中显示数据列

    通过以上步骤,我们就实现了从一个Activity用Intent跳转到ListActivity,并在ListActivity中使用SimpleAdapter显示数据列。这在Android应用开发中是非常常见的场景,尤其当需要展示大量结构化数据时。理解并熟练掌握...

    android 运算器 不同activity间的跳转

    当需要从一个Activity跳转到另一个Activity时,我们创建一个Intent实例。例如,以下代码展示了如何从当前Activity启动新的Activity: ```java Intent intent = new Intent(this, SecondActivity.class); start...

    Android 单击实现页面跳转

    Intent构造函数的第一个参数是当前Activity(`MainActivity.this`),第二个参数是要跳转的目标Activity(`NextActivity.class`)。`startActivity`方法用于启动目标Activity,从而完成页面跳转。 除了直接通过...

    RecyclerView item 点击跳转事件

    本篇文章将深入探讨如何在RecyclerView中实现item的点击跳转事件,以便用户点击不同的列表项时可以跳转到不同的Activity。 首先,我们需要在布局文件中为RecyclerView创建一个适配器(Adapter)。适配器是连接数据...

    TextView超链接启动Activity,无下划线,可自定义颜色

    在许多应用中,我们可能需要将TextView中的部分文字设置为超链接,以便用户点击后可以跳转到其他Activity或网页。本教程将详细介绍如何实现一个没有下划线且可以自定义颜色的TextView超链接,并通过Intent启动新的...

    Android应用源码之(Activity跳转与操作)-IT计算机-毕业设计.zip

    Intent是Android中用于启动或与另一个组件交互的机制,通常用于Activity之间的跳转。通过构造Intent对象,可以指定目标Activity的类名或者用Intent的ACTION_*常量表示通用操作。源码可能会展示如何使用startActivity...

    最简单intent使用Activity切换实例

    本教程将详细讲解如何通过Intent实现最简单的Activity切换实例,即点击Button后从一个Activity(Act1)跳转到另一个Activity(Act2)。 首先,我们需要了解Activity的基本概念。Activity是Android中的一个核心组件...

    实验IntentActivity

    IntentActivity 实验指南 Intent 和 Activity 是 Android 应用程序开发中两个非常重要的概念。Intent 是一种异步的消息机制,用于在应用程序组件之间传递信息,而 Activity 则是 Android 的一个基本组件,负责处理...

    Android开发之TextView使用intent传递信息,实现注册界面功能示例

    在本示例中,我们关注的是如何通过Intent在两个Activity之间传递信息,特别是从注册界面(MainActivity)到验证或后续处理界面(In)。下面我们将详细探讨这个过程。 1. **Intent的创建与使用**: 在`MainActivity...

    Android编程中Intent实现页面跳转功能详解

    startActivity(intent)方法是实现页面跳转功能的最基本方法,它可以将当前Activity跳转到目标Activity中。例如: ```java Intent intent = new Intent(mContext, SActivity.class); startActivity(intent); ``` 2. ...

    Android使用Intent实现页面跳转

    在 Android 中,Intent 可以被用来实现页面之间的跳转,包括 startActivity 和 startActivityForResult 两种方式。 1. startActivity 方式 startActivity 方式是使用 Intent 实现页面跳转的一种常见方式。例如,在...

    Intent实现多个活动的跳转的一个简单实例

    在Android应用开发中,Intent是连接各个组件(如Activity、Service等)的桥梁,它用于在组件间传递消息,实现应用程序内部的导航。本实例主要讲解如何使用Intent实现从一个Activity(登录页面)跳转到另一个Activity...

    Android之Activity的生命周期和Activity间的跳转和数据传递)

    Activity之间的跳转通常通过`Intent`对象实现。Intent可以携带数据并在Activity之间传递,实现页面跳转。 - **显式Intent**:明确指定目标Activity的名称。 - **隐式Intent**:通过定义Action和Category来指定目标...

Global site tag (gtag.js) - Google Analytics