Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clickable links and copy/paste menu in EditView in android

I have an EditText view in my Android app. I need "inner links" in it, this means that I need some buttons or span inside EditText and with onClick to this button I can do some actions (not redirect to web page). I realized this buttons with ClickableSpan() like this

linkWord = "my link";
link = new SpannableString(linkWord);
cs = new ClickableSpan(){
private String w = linkWord;
    @Override
    public void onClick(View widget) {
    wrd.setText(w);
    }
};
link.setSpan(cs, 0, linkWord.length(), 0);
et.append(link);

For make this span clickable I used

et.setMovementMethod(LinkMovementMethod.getInstance());

"Inner links" works fine, but after using et.setMovementMethod() copy and paste items are disable on OnLongClick menu. And this is a problem, because I need "links" in EditText and copy text from this view in the same time.

I have idea to set in listener OnLongClickListener something like removeMovementMethod() for temporary disable "links" function and use menu with copy/paste and after coping text switch on setMovementMethod() method again. But I don't know how to realize this.

Can you help me? You may be there are some another ways...

Thank you!

like image 445
lubart Avatar asked May 13 '12 14:05

lubart


1 Answers

I don't think that having the user switch between link and copy mode will win you a usability prize. My solution allows you to select text and open the links at the same time. To achieve this I simply extend ArrowKeyMovementMethod, which allows to select text, and add the onTouchEvent() method from the LinkMovementMethod which handles the clicking/touching of links. There's but one line of code that needs to be changed, which is the one removing the selection from the TextView when no link could be found at the coordinates the screen was touched.

Here's the complete class:

public class MyMovementMethod extends ArrowKeyMovementMethod {

    private static MyMovementMethod sInstance;

    public static MovementMethod getInstance() {
        if (sInstance == null) {
            sInstance = new MyMovementMethod ();
        }
        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);

            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 {
                that's the line we need to remove
                Selection.removeSelection(buffer);
            }*/
        }

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

}

Doing this is pretty safe even if the documentation states:

This interface [MovementMethod] is intended for use by the framework; it should not be implemented directly by applications. http://developer.android.com/reference/android/text/method/MovementMethod.html

The code above extends a documented class rather than implement the interface. All it does is adding a check to see if a link was tapped and otherwise uses the super class methods.

like image 188
Emanuel Moecklin Avatar answered Nov 14 '22 21:11

Emanuel Moecklin