android span click event
Asked Answered
P

2

2

Ok. These are my problems.

  1. I need to user regular expressions to filet out everything except for letters and then I need to encase the found words within a $word tag. With this str = str.replaceAll(pattern, "$0");. right now I am filtering all of the right elements (punctuation, numbers etc) but its encasing every letter within each word in an a tag not the word. so how do I use the regular expression to group the letters to a word?

from "(a tag open)t(a close)(a tag open)h(a close)(a tag open)i(a close)(a tag open)s(a close) (a tag open)i(a close)(a tag open)s(a close) (a tag open)w(a close)(a tag open)r(a close)(a tag open)o(a close)(a tag open)n(a close)(a tag open)g(a close)";

to :

"(a tag open)This(a close) (a tag open)is(a close) (a tag open)right(a close)";

then I'm making them clickable and I need to catch the click event and get the position on screen on the clicked word as I want to use the clicked event to make a tool tip show up just below the clicked word. thank you for your help.

public class MainActivity extends Activity {

public String text = "This is just a sentence to test you. 23 this is another number23!g?";
public TextView tv;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    text = explode(text);

    tv = (TextView) findViewById(R.id.tv1);

    tv.setLinksClickable(true);
    tv.setMovementMethod(LinkMovementMethod.getInstance());

    Spanned article = Html.fromHtml(text, null, null);
    setHTML(article);
}


public void setHTML(Spanned html) {
      SpannableString message = new SpannableString(html.toString());
      Object[] spans = html.getSpans(0, html.length(), Object.class);
      for (Object span : spans) {
         int start = html.getSpanStart(span);
         int end = html.getSpanEnd(span);
         int flags = html.getSpanFlags(span);
         if (span instanceof URLSpan) {
            URLSpan urlSpan = (URLSpan) span;
            span = new CallbackSpan(urlSpan.getURL());
         }
         message.setSpan(span, start, end, flags);
      }
      tv.setText(message);
   }
    public String explode(String str){  
      String pattern = "([a-zA-Z])";
      str = str.replaceAll(pattern, "<a href=\"$0\">$0</a>");
      return str;
    }

   private final class CallbackSpan extends ClickableSpan {

      private String m_data;
      private String url_main;

      public CallbackSpan(String url) {
         m_data = url.substring(0);
         url_main = url;

      }

      public void onClick(View view) {

          TextView item = (TextView)findViewById(R.id.tv2);
          item.setText(url_main + " was clicked.");
          Log.d("item", url_main);
      }
   }

}

Pandowdy answered 29/3, 2013 at 8:1 Comment(7)
i can't understand clearly the question. You have string and you intent to click on the text in textview.Quicken
well firstly i have the string "This is a with numbers 238 66 AND ?? PUNC BL BLA"i want to convert that to "<span>This</span> <span>is</span> <span>a</span> <span>with</span> <span>numbers</span> 238 66 <span>AND</span> ?? <span>PUNC</span> <span>BL</span> <span>BLA</span>"Pandowdy
but im getting this "<span>T</span><span>h</span><span>i</span><span>s </span><span>i</span><span>s</span>..... ect" so how do i group the letters to a wordPandowdy
you want make individual worksin textview clickable?Quicken
yes but i have got that already with the code above. i can log each different word and all of the details i need from each clicked on item but it converts all the letters not wordsPandowdy
thanks you for your speedy responsePandowdy
pls edit your question. Instead of writing the whole story can you just point to the actual problem.Quicken
C
8

Latest code ,pls see link form github

message.setSpan(span, start, end, flags);

You need remove origin span before set new span. Please see below
The onClick() of ClickableSpan is not working for URLSpan?

EDIT
You can capture any span click event by extends LinkMovementMethod

    import android.os.Handler;
import android.os.Message;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.TextView;

import java.lang.ref.WeakReference;

public class LinkMovementMethodExt extends LinkMovementMethod {
    public static final int LinkMovementMethod_Down = 1001;
    public static final int LinkMovementMethod_Up = 2002;
    private static LinkMovementMethod sInstance;
    private Class mSpanClass = null;
    private WeakReference<Handler> mWeakReference = null;

    public static MovementMethod getInstance(Handler handler, Class spanClass) {
        if (sInstance == null) {
            sInstance = new LinkMovementMethodExt();
            ((LinkMovementMethodExt) sInstance).mWeakReference = new WeakReference<>(handler);
            ((LinkMovementMethodExt) sInstance).mSpanClass = spanClass;
        }
        return sInstance;
    }

    @Override
    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);
            /**
             * get you interest span
             */
            Object[] spans = buffer.getSpans(off, off, mSpanClass);
            if (spans.length != 0) {
                if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer, buffer.getSpanStart(spans[0]), buffer.getSpanEnd(spans[0]));
                    MessageSpan obj = new MessageSpan();
                    obj.setObj(spans);
                    obj.setView(widget);
                    Handler handler = mWeakReference.get();
                    if (handler != null) {
                        Message message = handler.obtainMessage();
                        message.obj = obj;
                        message.what = LinkMovementMethod_Down;
                        message.sendToTarget();
                        return true;
                    }
                    return false;
                } else if (action == MotionEvent.ACTION_UP) {
                    Handler handler = mWeakReference.get();
                    if (handler != null) {
                        MessageSpan obj = new MessageSpan();
                        obj.setView(widget);
                        Message message = handler.obtainMessage();
                        message.obj = obj;
                        message.what = LinkMovementMethod_Up;
                        message.sendToTarget();
                        return true;
                    }
                    return false;
                }
            }
        }

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

    public boolean canSelectArbitrarily() {
        return true;
    }

    public boolean onKeyUp(TextView widget, Spannable buffer, int keyCode,
                           KeyEvent event) {
        return false;
    }

textView.setMovementMethod(LinkMovementMethodExt.getInstance());


Edit for "android developer"
It is better way to add Handler property for LinkMovementMethodExt class.
You can capture all Spanned which are delivered as Message object.

Snip code in onTouchEvent method:

Message message = Handler.obtainMessage();
message.obj = buffer.getSpans(off, off, Spanned.class);//get all spanned
message.what = 111;//which value ,it is up to you
message.sendToTarget(); //send message to you target handler

You can handler expected spanned in you handler class. May be it is flexible way to handle .
Hope to help you.


enter image description here


Above textview text is <a href='/a'>aaaa</a>123456<a href='/b'>bbbb</b>7890
I understand you requirement :
Click 'aaaa',you want get its href value '/a', click 'bbbb',get its href '/b'; Do not trigger default action which is opened in web browser.
If my understand is right, you can do like this:

  • Set LinkMovementMethod for textview, etc:textview.setMovementMethod(LinkMovementMethodExt.getInstance(handler, URLSpan.class));
  • Get interest span, here is URLSpan.
    In you handler handleMessage method, you can do like this:
private Handler handler = new Handler() {
      public void handleMessage(Message msg) {
          int what = msg.what;
          if (what == 100) {
              Object[] spans = (Object[])msg.obj;
              for (Object span : spans) {
                  if (span instanceof URLSpan) {
                      System.out.println(((URLSpan) span).getURL());
                  }
              }
          }
      };
  };

Download demo code

  • MainActivity has color property which you can assign which color value as you like.

How to do?

  • Step1, get current click span.

  • Step2, set BackgroundColorSpan for current click span

Combinative answered 24/4, 2013 at 2:42 Comment(25)
this is a really good solution. can i have a customized action being done when clicking on the link, yet still have the same clicking effect on the link (i use ClickableSpan instead) ?Patina
i don't understand. maybe i will explain better: suppose i have a textView that i set a text from the strings.xml , which has multiple links inside it. i wish to capture the clicking on each of them, so that i will handle what happens next myself, instead of opening an intent for the web browser. how would i do that?Patina
where will you put the handler and make it overtake the default behavior? currently all i can see is preparing the hanlder, but there is no connection to where to put it and make it work...Patina
I have edit MovementMethod getInstance() method which has handler parameter.Combinative
@android developer, I write demo project for this. How to send you?Combinative
thanks. you can use any website you wish. even pasteBin. btw, i think you've accidentally set "" to the wrong variables. the "" is usually used for fields and not parameters.Patina
downloading from this website requires registration and knowledge in reading chinese . :(Patina
@androiddeveloper, I have not find online store file system. If you like ,please send mail to [email protected]; I will sent to you tomorrow.Combinative
@androiddeveloper, I have not find online store file system. If you like ,please send mail to [email protected]; I will sent to you tomorrow.Combinative
is it that long that you need to post the whole project? just post the sample. you can use pasteBin . just paste the relevant code: pastebin.comPatina
i've already tried it, and it doesn't work well, since when you click on the link, it stays marked as if you still touch it. in the default behavior, once you click on the link, it returns to be normal again. btw, you don't have to use a handler. you can use a listener instead.Patina
let us continue this discussion in chatCombinative
i don't think you understand. the code handles the link fine, but the effect of touching the link doesn't work well. it shows something like this, even though i don't touch the link anymore : imageshack.us/photo/my-images/89/13ms.pngPatina
@androiddeveloper, I have not understand you mean before. Now i have modify the github code, please download again. When you click link which correspond background color will change to green. img824.imageshack.us/img824/5177/kee9.pngCombinative
it still has the exact same bug, only with a different color : when i touch it and leave my finger from the device, the link stays with the same effect as if i'm still touching it.Patina
@androiddeveloper, Do you want this:during touch period, change the link background. Restore init status once you finish touch. If this, please download again.Combinative
seems to work fine now. maybe i should have posted a question instead of writing everything here. what did you do in order to make it work? also , why is the color now green?Patina
is it possible to use the default color? also, what was wrong before that now works fine?Patina
Yes, you can set any color. Before just system.out.println href value, May be you have not notice this. In order to show clear, change to Toast style.Combinative
i don't understand the last sentence .Patina
i don't care about the toast. i meant the url color . i asked if i can use the default color for this. i think it's usually some sort of blue, but on old devices it might be green.Patina
@androiddeveloper, you can set any color as you like. I have modify color to blue.Combinative
i know i can. i ask how to use the default one, as it could be different per device/version/ROM...Patina
@androiddeveloper, I push latest code. change link background color to it`s default color (view.getLinkTextColors().getDefaultColor())Combinative
it seems the code you've written for the color of links when they are clicked is wrong, as it puts a totally opaque color of blue.Patina
Q
0

Assuming You want click individual words on the textview?. I din't understand the converts all letters to words in your comment.

The below can be used to click on individual works and it is displayed in a toast.

    public class MainActivity extends Activity {

    TextView _tv;
    String[] each;
    SpannableString ss1;
    Button b;
    EditText et;
    String s;
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    b= (Button) findViewById(R.id.button1);
    et = (EditText) findViewById(R.id.ed);
    _tv = (TextView) findViewById( R.id.tv );
    b.setOnClickListener(new OnClickListener()
    {

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        s=et.getText().toString();
        _tv.setText("");
        for(int i=0;i<s.length();i++)
        {
            each = s.split("\\s+");
        }
        for(int i=0;i<each.length;i++)
        {
            System.out.println("................"+each[i]);
            ss1=  new SpannableString(each[i]);
            //StyleSpan boldSpan = new StyleSpan( Typeface.BOLD );
            //spannable.setSpan( boldSpan, 41, 52, Spannable.SPAN_INCLUSIVE_INCLUSIVE );
            ss1.setSpan(new MyClickableSpan(each[i]), 0, ss1.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            _tv.append(ss1); 
            _tv.append(" "); 

        }
        _tv.setMovementMethod(LinkMovementMethod.getInstance());
    }
       });
      }
      class MyClickableSpan extends ClickableSpan{ 
      String clicked;
          public MyClickableSpan(String string) {
    // TODO Auto-generated constructor stub
       clicked =string;
      }
      //clickable span
      public void onClick(View textView) {
      //do something


      Toast.makeText(MainActivity.this,clicked ,
        Toast.LENGTH_SHORT).show();
     }
     @Override
     public void updateDrawState(TextPaint ds) {
     ds.setColor(Color.BLACK);//set text color 
     ds.setUnderlineText(false); // set to false to remove underline
     }
    }
    }
Quicken answered 29/3, 2013 at 8:36 Comment(2)
@Pandowdy is this waht you were looking for?Quicken
not quite as this still brings makes all elements like number and things clickable. i only ned the words.and that's why i used the regular expressions to insert the a tag around the words. thank you for your helpPandowdy

© 2022 - 2024 — McMap. All rights reserved.