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!
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.
I solved this problem and may be this will be interesting for someone...
For clickable links inside EditText I used
et.setMovementMethod(LinkMovementMethod.getInstance());
in this case in longClick menu there are not copy/paste items.
For activate them I need back to normal EditText state, I can do it with:
et.setMovementMethod(ArrowKeyMovementMethod.getInstance());
After this method links will not work but appear normal longClick menu.
Therefore I added new item to the context menu and switched between this two options:
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if(et.getSelectionStart() == -1){ // in case of setMovementMethod(LinkMovementMethod.getInstance())
menu.add(0, 1, 0, "Enable copy");
}
else{
menu.add(0, 2, 0, "Enable links");
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 1:
et.setMovementMethod(ArrowKeyMovementMethod.getInstance());
et.setSelection(0, 0);
//re-register EditText for context menu:
unregisterForContextMenu(et);
registerForContextMenu(et);
break;
case 2:
et.setMovementMethod(LinkMovementMethod.getInstance());
break;
}
return true;
}
Also I registered EditText for context menu:
registerForContextMenu(et);
Have a hope that this will help someone!