Android自定义可点击的ImageSpan并在TextView中内置View

有的时候可能想在TextView中添加一些图片,比如下图,发短信输入联系人时,要把联系人号码换成一个图片,但这个图片无法用固定的某张图,而是根据内容进行定制的,这更像一个view。

当然,如果你不是view而是固定的图片,比如发信息时用表情图片替代特殊符号,那么实现起来会更加简单。又或许,你希望这个图片是可点击的。这里,笔者要介绍的就是怎么用一个自定义的ImageSpan来实现在文本里插入可点击的图片或View。

在此之前,如果你还不了解SpannableString.setSpan(),不了解LinkMovementMethod是什么,建议先看下笔者的解析TextView中的URL等指定特殊字符串与点击事件

首先,因为ImageSpan没有继承ClickableSpan,因此没有 onClick()方法。所以我写了个ClickableImageSpan 。

public abstract class ClickableImageSpan extends ImageSpan {
 public ClickableImageSpan(Drawable b) {
  super(b);
 }

 public abstract void onClick(View view);
}

同时,我们发现google提供的LinkMovementMethod只会执行ClickableSpan的onClick()方法.下面是LinkMovementMethod的onTouchEvent()的源码。这个方法是在我们点击Spanned的时候响应。

public boolean onTouchEvent(TextView widget, Spannable buffer,
        MotionEvent event) {
  int action = event.getAction();

  if (action == MotionEvent.ACTION_UP ||
   action == MotionEvent.ACTION_DOWN) {
   int x = (int) event.getX();
   int y = (int) event.getY();

   x -= widget.getTotalPaddingLeft();
   y -= widget.getTotalPaddingTop();

   x += widget.getScrollX();
   y += widget.getScrollY();

   Layout layout = widget.getLayout();
   int line = layout.getLineForVertical(y);
   int off = layout.getOffsetForHorizontal(line, x);

   ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

   if (link.length != 0) {
    if (action == MotionEvent.ACTION_UP) {
     link[0].onClick(widget);
    } else if (action == MotionEvent.ACTION_DOWN) {
     Selection.setSelection(buffer,
           buffer.getSpanStart(link[0]),
           buffer.getSpanEnd(link[0]));
    }

    return true;
   } else {
    Selection.removeSelection(buffer);
   }
  }

  return super.onTouchEvent(widget, buffer, event);
 }

发现这个方法其实就是通过坐标找到相应的Span。然后,当link数组不为空时,将会得到span并执行他的onClick()方法。这里我们注意到了这一句代码

ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); 

这说明该方法只获得了ClickableSpan,因为如果我们直接使用系统的LinkMovementMethod类,是无法让ImageSpan响应点击事件的。。因为我们知道,ImageSpan没有继承ClickableSpan。所以,笔者写了一个ClickableMovementMethod

public class ClickableMovementMethod extends LinkMovementMethod {

 private static ClickableMovementMethod sInstance;

 public static ClickableMovementMethod getInstance() {
  if (sInstance == null) {
   sInstance = new ClickableMovementMethod();
  }
  return sInstance;
 }

 public boolean onTouchEvent(TextView widget, Spannable buffer,
        MotionEvent event) {
  int action = event.getAction();

  if (action == MotionEvent.ACTION_UP ||
    action == MotionEvent.ACTION_DOWN) {
   int x = (int) event.getX();
   int y = (int) event.getY();

   x -= widget.getTotalPaddingLeft();
   y -= widget.getTotalPaddingTop();

   x += widget.getScrollX();
   y += widget.getScrollY();

   Layout layout = widget.getLayout();
   int line = layout.getLineForVertical(y);
   int off = layout.getOffsetForHorizontal(line, x);

   ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
   ClickableImageSpan[] imageSpans = buffer.getSpans(off, off, ClickableImageSpan.class);

   if (link.length != 0) {
    if (action == MotionEvent.ACTION_UP) {
     link[0].onClick(widget);
    } else if (action == MotionEvent.ACTION_DOWN) {
     Selection.setSelection(buffer,
       buffer.getSpanStart(link[0]),
       buffer.getSpanEnd(link[0]));
    }

    return true;
   } else if (imageSpans.length != 0) {
    if (action == MotionEvent.ACTION_UP) {
     imageSpans[0].onClick(widget);
    } else if (action == MotionEvent.ACTION_DOWN) {
     Selection.setSelection(buffer,
       buffer.getSpanStart(imageSpans[0]),
       buffer.getSpanEnd(imageSpans[0]));
    }

    return true;
   } else {
    Selection.removeSelection(buffer);
   }
  }

  return false;
 }
}

只是做了很小的改动,这样,这个类既可以支持ClickableSpan也可以支持我们自己写的ClickableImageSpan。

到此为止,一个可点击的ImageSpan就完成了。剩下的步骤就跟实现文字样式的方式一样,首先new一个SpannableString传入文本,然后找到你需要放置ImageSpan的位置(一般使用正则表达式),接着new一个ClickableImageSpan传入图片,通过SpannableString的setSpan()方法传入ClickableImageSpan对象。最后别忘了TextView调用setMovementMethod时,传入的是我们的ClickableMovementMethod.getInstance()方法。具体代码实现参照文字样式那边的,稍作修改即可。具体的笔者不再贴这部分的代码了。

那么,如果我们不是传一个简单的图片,而是需要显示一个定制的View,应该怎么做呢。其实只要把View转化成Drawable就好,下面是主要的实现代码:

 private BitmapDrawable createDrawble(Context ctx, String content) {
 View view = LayoutInflater.from(ctx).inflate(R.layout.viewt, null);
 ((TextView) view.findViewById(R.id.tv_content)).setText(content);
 int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
 view.measure(spec, spec);
 view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
 Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
 Canvas c = new Canvas(b);
 c.translate(-view.getScrollX(), -view.getScrollY());
 view.draw(c);
 view.setDrawingCacheEnabled(true);
 Bitmap cacheBmp = view.getDrawingCache();
 Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
 view.destroyDrawingCache();
 return new BitmapDrawable(ctx.getResources(), viewBmp);
 }

 public void filter(Spannable sp) {
 /**
  .....此处省略.
 **/
 BitmapDrawable bd = createDrawble(tv.getContext(), sp.toString); bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());
 MyClickableImageSpan span = new MyClickableImageSpan(bd,text);
 sp.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 }

createDrawble()方法是通过View的getDrawingCache()方法将一个View转化成BItmap,然后在获得BitmapDrawable 后别忘了调用setBounds(),这个方法是决定图片的大小,如果不设置,那么图片长宽都为0! 当然,你如果嫌显示的效果太大或太小,也可以通过这个方法调整图片大小。其他步骤相信大家看过笔者的 解析TextView中的URL等指定特殊字符串与点击事件 ,实现起来应该是没有困难的。因此笔者不再赘述了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android RichText 让Textview轻松的支持富文本(图像ImageSpan、点击效果等等类似QQ微信聊天)

    AndroidRichText帮助实现像QQ,微信一样的,一个TextView里既有文字又有表情又有图片的效果,采用插件化的框架,代码简单,可拓展性强. 基础框架包只有四个java文件, RichTextWrapper :TextView的包裹类,实现支持富文本,通过new RichTextWrapper(TextView v)来构造. RTMovementMethod: 继承自Android原生的LinkMovementMethod,重写onTouchEvent方法,优化了ClickSpan(

  • Android自定义可点击的ImageSpan并在TextView中内置View

    有的时候可能想在TextView中添加一些图片,比如下图,发短信输入联系人时,要把联系人号码换成一个图片,但这个图片无法用固定的某张图,而是根据内容进行定制的,这更像一个view. 当然,如果你不是view而是固定的图片,比如发信息时用表情图片替代特殊符号,那么实现起来会更加简单.又或许,你希望这个图片是可点击的.这里,笔者要介绍的就是怎么用一个自定义的ImageSpan来实现在文本里插入可点击的图片或View. 在此之前,如果你还不了解SpannableString.setSpan(),不了解

  • Android自定义button点击效果的两种方式

    我们在界面上经常会用到button按钮,但通常button点击后看不到点击的效果,如果用户连续点击了两次,就会报NAR错误,这样交互性就比较差了.如果我们自定义了button点击效果,比如我们点击了button能让我们看到我们确实点击了button按钮,这样就会有效的避免重复点击了. 自定义点击效果有两种方式,一种是在xml中定义,另一种是在代码中定义. 首先看一下如何在xml中定义: 在drawable下新建selector.xml文件: <?xml version="1.0"

  • Android 自定义按钮点击事件和长按事件对比

     Android 自定义按钮点击事件和长按事件对比 一个按钮同时实现点击和长按事件,有时候会有冲突,我们针对这一现象来自定义按钮来区分点击和长按事件 1.xml中 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="mat

  • Android实现可点击展开的TextView

    概述 Android开发过程中,经常遇到 Textview 展示不完全的情况. 遇到此情况,通常的处理是: 方案一 Textview 添加 android:ellipsize 属性,让展示不完的部分使用省略号代替. 方案二 Textview 采用走马灯效果,使其滚动展示全部文本内容. 对于方案一,如果想查看被省略后的内容,如何实现?通常情况下是在 TextView 文本后面或下边添加一个可点击的图标,来实现 TextView 的展开与收缩.如下图: 收缩状态 展开状态 实现原理 对于以上效果,大

  • Ubuntu中为Android系统实现内置Java应用程序测试Application Frameworks层的硬件服务

    我们在Android系统增加硬件服务的目的是为了让应用层的APP能够通过Java接口来访问硬件服务.那么, APP如何通过Java接口来访问Application Frameworks层提供的硬件服务呢?在这一篇文章中,我们将在Android系统的应用层增加一个内置的应用程序,这个内置的应用程序通过ServiceManager接口获取指定的服务,然后通过这个服务来获得硬件服务.        一. 参照在Ubuntu Android实现Application Frameworks层增加硬件访问服

  • Android 自定义view模板并实现点击事件的回调

    Android 自定义view模板并实现点击事件的回调 主要的目的就是仿老版QQ的一个界面做一个模板.然后实现点击事件的回调.先看效果图: 步骤如下: 1.在res/values/目录下新建一个atts.xml文件 内容如下: <resources> <declare-styleable name="topbar"> <attr name="title" format="string"/> <attr n

  • Android自定义带增长动画和点击弹窗提示效果的柱状图DEMO

    项目中最近用到各种图表,本来打算用第三方的,例如MPAndroid,这是一个十分强大的图表库,应用起来十分方便,但是最终发现和设计不太一样,没办法,只能自己写了.今天将写好的柱状图的demo贴在这,该柱状图可根据数据的功能有一下几点: 1. 根据数据的多少,动态的绘制柱状图柱子的条数: 2. 柱状图每条柱子的绘制都有动态的动画效果: 3. 每条柱子有点击事件,点击时弹出提示框,显示相关信息,规定时间后,弹窗自动消失. 好了,先上演示图: 下边贴出相关代码: 自定义柱状图类: package co

  • Android 自定义Button控件实现按钮点击变色

    效果图如下所示: 一.shape 样式:(在drawable新建-->new-->Drawable resource file 在父级标签selector添加Item ) <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item and

  • Android自定义View简易折线图控件(二)

    继续练习自定义View,这次带来的是简易折线图,支持坐标点点击监听,效果如下: 画坐标轴.画刻度.画点.连线..x.y轴的数据范围是写死的 1 <= x <= 7 ,1 <= y <= 70 ..写活的话涉及到坐标轴刻度的动态计算.坐标点的坐标修改,想想就头大,这里只练习自定义View. 1.在res/values文件夹下新建attrs.xml文件,编写自定义属性: <?xml version="1.0" encoding="utf-8"

  • Android 自定义九宫格手势锁

    预览效果图如下: 主要的方法是重写View.onTouchEvent( MotionEvent event ) , 常用的三个操作:ACTION_DOWN 手指触摸屏幕 ; ACTION_UP 手指离开屏幕; ACTION_MOVE手指在屏幕滑动. 如果该方法返回true ,表示该事件已经被View处理,不再向上层的View或Activity传递 : 如果返回false, 表示事件未处理,继续传递. 具体代码如下: package com.ninegrid; import android.con

随机推荐