17

I'm not sure how to go about doing this, but I was wondering if there was a way to get the position (index) in a String in a TextView when a person clicks somewhere on the TextView. So, if someone clicks on the following TextView:

Hello I'm Vinay Hiremath.

on the first 'H', the onClick event will get the integer 0. Or, if someone clicks on the 'V', it will get 10, and so on.

Any help, like always, is greatly appreciated. Feel free to call me an idiot and throw rocks if this is blatantly on Android Developers.

Vinay
  • 6,204
  • 6
  • 38
  • 55
  • 1
    Looks like you're asking http://stackoverflow.com/questions/2302867/android-how-to-determine-character-index-of-a-touch-events-position-in-textview – James Moore Jul 01 '11 at 21:01

5 Answers5

29

So I ended up solving this via the link in James Moore's comment here. I ended up using the Tony Blues's solution in that link. That solution is this:

1.) Set an onTouchListener to the TextView.

2.) Get the layout of the TextView.

3.) Get the x,y coordinates of the touch via the event.

4.) Find the line and offset (index) based on that event.

The following code example is how one would Log (in verbose) the index of a touch given a TextView named manip:

manip.setOnTouchListener(new View.OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        Layout layout = ((TextView) v).getLayout();
        int x = (int)event.getX();
        int y = (int)event.getY();
        if (layout!=null){
            int line = layout.getLineForVertical(y);
            int offset = layout.getOffsetForHorizontal(line, x);
            Log.v("index", ""+offset);
        }
        return true;
    }
});

EDIT: Rahul Mandaliya has brought it to my attention that this code will fail if there is padding on the text view. Unfortunately, it has been a very long time since I've written any Android code, so I'm not sure how to update this answer to account for that. Intuitively, it seems like you could do some sort of check against the click event if you know the padding value. Sorry about not being able to be more helpful on this edge case. :-(

Community
  • 1
  • 1
Vinay
  • 6,204
  • 6
  • 38
  • 55
12

If you're following the accepted answer, you may notice that the onTouch event is raised more than once every time the user touches the screen. Use event.getAction() to find out what action is being handled. This depends on the user's behaviour that raised the event. For instance, for a simple touch on the screen, the onTouch event will fire twice with the actions ACTION_DOWN and ACTION_UP. For a touch and move, you will get different actions depending on the move (within the control, from outside to inside, or vice versa). For more details check the MotionEvent.

The following code is a modified version of the accepted answer that only handles the ACTION_DOWN action:

manip.setOnTouchListener(new View.OnTouchListener() {
  public boolean onTouch(View v, MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
      Layout layout = ((TextView) v).getLayout();
      int x = (int)event.getX();
      int y = (int)event.getY();
      if (layout!=null){
          int line = layout.getLineForVertical(y);
          int offset = layout.getOffsetForHorizontal(line, x);
          Log.v("index", ""+offset);
          }
    }
    return true;
  }
});
Racil Hilan
  • 24,690
  • 13
  • 50
  • 55
  • Upvote from me. At this point, it's been a while since I've touch anything Android-related, but this seems to make sense. :-) – Vinay Feb 24 '14 at 22:00
4

1) Take a List.

2)split this string into separate character.

3)take setOnItemClickListener(new OnItemClickListener() ) Event.

4) show Toast for each character by the get args of charcter.

It will help you.

Siten
  • 4,515
  • 9
  • 39
  • 64
  • If Octavian's answer works for me, then I'm going to use that (it fits my needs a bit better), but +1 for a creative/different way to approach this problem. ;-) – Vinay Jun 10 '11 at 02:50
3

Take this with a pinch of salt as I had no chance to test it.

Set the android:textIsSelectable attribute to true for the TextView and then use the getSelectionStart() and getSelectionEnd() methods.

Again I'm really just making an educated guess.

Octavian Helm
  • 39,405
  • 19
  • 98
  • 102
  • Thanks a ton for a great direction Octavian! This would actually be very helpful for future functionality I would like in my app as well, if it works. However, whenever I put "android:textIsSelectable="true"" into my XML layout, it tells me that "No resource identifier found for attribute 'textIsSelectable' in package 'android'". Do you have any idea what this could mean? It looks like someone was facing the same problem here: http://androidforums.com/application-development/330162-how-do-i-make-textview-text-selectable.html but got no answers. Any help is appreciated. – Vinay Jun 10 '11 at 02:47
  • As a side note, I understand that this issue is regarding something entirely different, so, once I figure out the whole "textIsSelectable" problem, whether you can help me or not, if your answer works, I will give you credit and mark it as the answer to this question. I always give credit where it's due. – Vinay Jun 10 '11 at 02:49
  • Oh. It seems that this issue is due to me not telling you that the method `isTextSelectable()` is from API level `11` (Honeycomb). I'm not sure if it works without. I'll have to try it myself. – Octavian Helm Jun 10 '11 at 10:01
1

Following the correct answer above, i add some more features. this way it will also show you the letter that you have clicked, plus it stop the error in case you click outside the boundaries, if you dont put it on try catch, the app will crash.

manip.setOnTouchListener(new View.OnTouchListener() {
  public boolean onTouch(View v, MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
      Layout layout = ((TextView) v).getLayout();
      int x = (int)event.getX();
      int y = (int)event.getY();
      if (layout!=null){
          int line = layout.getLineForVertical(y);
          int offset = layout.getOffsetForHorizontal(line, x);
           try{
              char texts = textView.getText().charAt(offset);
              Toast.makeText(MainActivity.this,"the offset are "+offset+"letter are "+texts,Toast.LENGTH_SHORT).show();
            }catch (Exception e){
              Toast.makeText(MainActivity.this,"the error are "+e,Toast.LENGTH_SHORT).show();
            }
          }
    }
    return true;
  }
});