zoukankan      html  css  js  c++  java
  • Textview加入Intent、表情,点击跳转Activity

     做过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方法:

    Java代码  收藏代码
    1. public void onClick(View widget) {  
    2.         Uri uri = Uri.parse(getURL());  
    3.         Context context = widget.getContext();  
    4.         Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
    5.         intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());  
    6.         context.startActivity(intent);  
    7.     }  

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

    Java代码  收藏代码
    1. public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {  
    2.   
    3.     /** 
    4.      * Performs the click action associated with this span. 
    5.      */  
    6.     public abstract void onClick(View widget);  
    7.      
    8.     /** 
    9.      * Makes the text underlined and in the link color. 
    10.      */  
    11.     @Override  
    12.     public void updateDrawState(TextPaint ds) {  
    13.         ds.setColor(ds.linkColor);  
    14.         ds.setUnderlineText(true);  
    15.     }  
    16. }  

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

    Java代码  收藏代码
    1. import android.content.Context;  
    2. import android.content.Intent;  
    3. import android.text.TextPaint;  
    4. import android.text.style.ClickableSpan;  
    5. import android.view.View;  
    6.   
    7. /** 
    8.  * If an object of this type is attached to the text of a TextView with a 
    9.  * movement method of LinkMovementMethod, the affected spans of text can be 
    10.  * selected. If clicked, the {@link #onClick} method will be called. 
    11.  *  
    12.  * @author 张宁 
    13.  */  
    14. public class MyClickableSpan extends ClickableSpan {  
    15.   
    16.     int color = -1;  
    17.     private Context context;  
    18.     private Intent intent;  
    19.   
    20.     public MyClickableSpan(Context context, Intent intent) {  
    21.         this(-1, context, intent);  
    22.     }  
    23.   
    24.     /** 
    25.      * constructor 
    26.      * @param color the link color 
    27.      * @param context 
    28.      * @param intent 
    29.      */  
    30.     public MyClickableSpan(int color, Context context, Intent intent) {  
    31.         if (color!=-1) {  
    32.             this.color = color;  
    33.         }  
    34.         this.context = context;  
    35.         this.intent = intent;  
    36.     }  
    37.   
    38.     /** 
    39.      * Performs the click action associated with this span. 
    40.      */  
    41.     public void onClick(View widget){  
    42.         context.startActivity(intent);  
    43.     };  
    44.   
    45.     /** 
    46.      * Makes the text without underline. 
    47.      */  
    48.     @Override  
    49.     public void updateDrawState(TextPaint ds) {  
    50.         if (color == -1) {  
    51.             ds.setColor(ds.linkColor);  
    52.         } else {  
    53.             ds.setColor(color);  
    54.         }  
    55.         ds.setUnderlineText(false);  
    56.     }  
    57. }  

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

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

    Java代码  收藏代码
    1. /** 
    2.     * Retrieves images for HTML &lt;img&gt; tags. 
    3.     */  
    4.    public static interface ImageGetter {  
    5.        /** 
    6.         * This methos is called when the HTML parser encounters an 
    7.         * &lt;img&gt; tag.  The <code>source</code> argument is the 
    8.         * string from the "src" attribute; the return value should be 
    9.         * a Drawable representation of the image or <code>null</code> 
    10.         * for a generic replacement image.  Make sure you call 
    11.         * setBounds() on your Drawable if it doesn't already have 
    12.         * its bounds set. 
    13.         */  
    14.        public Drawable getDrawable(String source);  
    15.    }  

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

    ImageSpan呢?OK,上代码:

    Java代码  收藏代码
    1. import java.util.Map;  
    2. import java.util.Set;  
    3.   
    4. import android.content.Context;  
    5. import android.graphics.drawable.Drawable;  
    6. import android.text.Html;  
    7. import android.text.Spanned;  
    8. import android.text.Html.ImageGetter;  
    9.   
    10. /** 
    11.  * this is a class which defining a spanned with image 
    12.  * @author 张宁 
    13.  * 
    14.  */  
    15. public class ImageSpan {  
    16.       
    17.     /** 
    18.      * the map of face. 
    19.      */  
    20.     private Map<String, String> faceMap;  
    21.     private Context context;  
    22.       
    23.     public ImageSpan(Context context, Map<String, String> faceMap){  
    24.         this.context = context;  
    25.         this.faceMap = faceMap;  
    26.     }   
    27.   
    28.     /** 
    29.      * get the image by the given key 
    30.      */  
    31.     private ImageGetter imageGetter = new Html.ImageGetter() {  
    32.         @Override  
    33.         public Drawable getDrawable(String source) {  
    34.             Drawable drawable = null;  
    35.             String sourceName = context.getPackageName() + ":drawable/"  
    36.                     + source;  
    37.             int id = context.getResources().getIdentifier(sourceName, nullnull);  
    38.             if (id != 0) {  
    39.                 drawable = context.getResources().getDrawable(id);  
    40.                 if (drawable != null) {  
    41.                     drawable.setBounds(00, drawable.getIntrinsicWidth(),  
    42.                             drawable.getIntrinsicHeight());  
    43.                 }  
    44.             }  
    45.             return drawable;  
    46.         }  
    47.     };  
    48.       
    49.     /** 
    50.      * return a {@link Spanned} with image 
    51.      * @param text 
    52.      * @return 
    53.      */  
    54.     public Spanned getImageSpan(CharSequence text){  
    55.         String cs = text.toString();  
    56.         if (faceMap != null) {  
    57.             Set<String> keys = faceMap.keySet();  
    58.             for (String key : keys) {  
    59.                 if (cs.contains(key)) {  
    60.                     cs = cs.replace(key, "<img src='" + faceMap.get(key) + "'>");  
    61.                 }  
    62.             }  
    63.         }  
    64.         return Html.fromHtml(cs, imageGetter, null);  
    65.     }  
    66.   
    67. }  

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

    Java代码  收藏代码
    1. import java.util.HashMap;  
    2. import java.util.List;  
    3. import java.util.Map;  
    4.   
    5. import android.content.Context;  
    6. import android.content.Intent;  
    7. import android.text.SpannableStringBuilder;  
    8. import android.text.Spanned;  
    9. import android.text.TextUtils;  
    10. import android.text.method.LinkMovementMethod;  
    11. import android.widget.EditText;  
    12. import android.widget.TextView;  
    13.   
    14. /** 
    15.  * TextView with intent that can redirect to a new activity 
    16.  *  
    17.  * @author 张宁 
    18.  *  
    19.  */  
    20. public class CustomTextView {  
    21.   
    22.     private static Map<String, String> faceMap;  
    23.   
    24.     static {  
    25.         faceMap = new HashMap<String, String>();  
    26.         faceMap.put("[哭]""face_1");  
    27.         faceMap.put("[怒]""face_2");  
    28.     }  
    29.   
    30.     /** 
    31.      * make textview a clickable textview<br> 
    32.      * Note: make true the order of textList and intentList are mapped 
    33.      *  
    34.      * @param context 
    35.      * @param textView 
    36.      * @param textList 
    37.      *            the text should be set to this textview,not null 
    38.      * @param intentList 
    39.      *            the intent map to the text, if the text have no intent mapped 
    40.      *            to, please set a null value.Or it will happen some unknown 
    41.      *            error.<br> 
    42.      *            not null 
    43.      */  
    44.     public static void setClickableTextView(Context context, TextView textView,  
    45.             List<String> textList, List<Intent> intentList) {  
    46.         if (textList == null || intentList == null) {  
    47.             return;  
    48.         }  
    49.         SpannableStringBuilder builder = new SpannableStringBuilder();  
    50.         int end = -1, length = -1;  
    51.         int size = textList.size();  
    52.         Intent intent;  
    53.         for (int i = 0; i < size; i++) {  
    54.             String text = textList.get(i);  
    55.             if (TextUtils.isEmpty(text)) {  
    56.                 continue;  
    57.             }  
    58.             builder.append(textList.get(i));  
    59.             if ((intent = intentList.get(i)) != null) {  
    60.                 end = builder.length();  
    61.                 length = textList.get(i).length();  
    62.                 builder.setSpan(getClickableSpan(context, intent),  
    63.                         end - length, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  
    64.             }  
    65.             builder.append(" ");  
    66.         }  
    67.         textView.setText(builder);  
    68.         textView.setFocusable(true);  
    69.         textView.setMovementMethod(LinkMovementMethod.getInstance());  
    70.     }  
    71.       
    72.     /** 
    73.      *  make textview a clickable textview<br> 
    74.      *  Note: make true the order of textList and intentList are mapped 
    75.      * @param context 
    76.      * @param textView 
    77.      * @param text 
    78.      * @param intent 
    79.      */  
    80.     public static void setClickableTextView(Context context, TextView textView,  
    81.             String text, Intent intent) {  
    82.         SpannableStringBuilder builder = new SpannableStringBuilder(text);  
    83.         builder.setSpan(getClickableSpan(context, intent), 0, text.length(),   
    84.                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  
    85.         textView.setText(builder);  
    86.         textView.setMovementMethod(LinkMovementMethod.getInstance());  
    87.     }  
    88.   
    89.     /** 
    90.      * make TextView a View with image at any index   
    91.      * @param context 
    92.      * @param textView 
    93.      * @param textList 
    94.      */  
    95.     public static void setImgTextView(Context context, TextView textView,  
    96.             List<String> textList) {  
    97.         StringBuilder builder = new StringBuilder();  
    98.         for (int i = 0; i < textList.size(); i++) {  
    99.             builder.append(textList.get(i)).append(" ");  
    100.         }  
    101.         setImgTextView(context, textView, builder.toString());  
    102.   
    103.     }  
    104.   
    105.     /** 
    106.      * make TextView a View with image at any index   
    107.      * @param context 
    108.      * @param textView 
    109.      * @param text 
    110.      */  
    111.     public static void setImgTextView(Context context, TextView textView,  
    112.             String text) {  
    113.         ImageSpan imageSpan = new ImageSpan(context, faceMap);  
    114.         Spanned spanned = imageSpan.getImageSpan(text);  
    115.         textView.setText(spanned);  
    116.     }  
    117.       
    118.     /** 
    119.      * make EditText a View with image at any index   
    120.      * @param context 
    121.      * @param EditText 
    122.      * @param text 
    123.      */  
    124.     public static void setImgTextView(Context context, EditText editText,  
    125.             String text) {  
    126.         ImageSpan imageSpan = new ImageSpan(context, faceMap);  
    127.         Spanned spanned = imageSpan.getImageSpan(text);  
    128.         editText.setText(spanned);  
    129.     }  
    130.   
    131.     /** 
    132.      * return a custom ClickableSpan 
    133.      *  
    134.      * @param context 
    135.      * @param intent 
    136.      * @return 
    137.      */  
    138.     public static MyClickableSpan getClickableSpan(Context context,  
    139.             Intent intent) {  
    140.         return new MyClickableSpan(context, intent);  
    141.     }  
    142.   
    143.     /** 
    144.      * make textview a clickable textview with image<br> 
    145.      * Note: make true the order of textList and intentList are mapped 
    146.      *  
    147.      * @param context 
    148.      *            not null 
    149.      * @param haveImg 
    150.      *            whether this is image in the text,not null 
    151.      * @param textView 
    152.      *            not null 
    153.      * @param textList 
    154.      *            the text should be set to this textview,not null 
    155.      * @param intentList 
    156.      *            the intent map to the text, if the text have no intent mapped 
    157.      *            to, please set a null value.Or it will happen some unknown 
    158.      *            error.<br> 
    159.      *            allow null 
    160.      */  
    161.     public static void setCustomText(Context context, Boolean haveImg,  
    162.             TextView textView, List<String> textList, List<Intent> intentList) {  
    163.         SpannableStringBuilder builder = new SpannableStringBuilder();  
    164.         int end = -1, length = -1;  
    165.         if (intentList != null) {  
    166.             int size = textList.size();  
    167.             Intent intent;  
    168.             for (int i = 0; i < size; i++) {  
    169.                 String text = textList.get(i);  
    170.                 if (TextUtils.isEmpty(text)) {  
    171.                     continue;  
    172.                 }  
    173.                 builder.append(textList.get(i));  
    174.                 if ((intent = intentList.get(i)) != null) {  
    175.                     end = builder.length();  
    176.                     length = textList.get(i).length();  
    177.                     builder.setSpan(getClickableSpan(context, intent), end  
    178.                             - length, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  
    179.                 }  
    180.                 builder.append(" ");  
    181.             }  
    182.         } else {  
    183.             for (String text : textList) {  
    184.                 builder.append(text).append(" ");  
    185.             }  
    186.         }  
    187.         if (haveImg) {  
    188.             ImageSpan imageSpan = new ImageSpan(context, faceMap);  
    189.             Spanned spanned = imageSpan.getImageSpan(builder);  
    190.             textView.setText(spanned);  
    191.         } else {  
    192.             textView.setText(builder);  
    193.         }  
    194.         textView.setMovementMethod(LinkMovementMethod.getInstance());  
    195.   
    196.     }  
    197.   
    198. }  

        有了这个类,我们就可以方便的实现在TextView中插入Intent和表情了,甚至不用管底层是怎样实现的,也降低了代码的耦合度。

  • 相关阅读:
    IOS8修改状态栏颜色
    iOS文件存储路径规定
    iOS+HTML5
    调用电话/获取通讯录
    iOS高级必备
    CoreData
    IOS 中的CoreImage框架
    CoreText
    CoreGpaphics
    iOS多线程 NSThread/GCD/NSOperationQueue
  • 原文地址:https://www.cnblogs.com/xingmeng/p/2629959.html
Copyright © 2011-2022 走看看