I need a clickable span to have both a normal click and a long click methods in my app and i found out here (In Android - How can I register only long clicks using a ClickableSpan) that i could extend the LinkMovementMethod class and the ClickableSpan class to allow to do that but currently the long and short clicks both work but for a long click instead of firing the long click action when the item has been pressed long enough it will wait until you release the item to fire. Here is my code for the extended classes:
LinkMovementClass
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.MotionEvent;
import android.widget.TextView;
public class LongClickLinkMovementMethod extends LinkMovementMethod {
private Long lastClickTime = 0l;
private int lastX = 0;
private int lastY = 0;
@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();
lastX = x;
lastY = y;
int deltaX = Math.abs(x-lastX);
int deltaY = Math.abs(y-lastY);
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);
LongClickableSpan[] link = buffer.getSpans(off, off, LongClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
if (System.currentTimeMillis() - lastClickTime < 1000)
link[0].onClick(widget);
else if (deltaX < 10 && deltaY < 10)
link[0].onLongClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
lastClickTime = System.currentTimeMillis();
}
return true;
}
}
return super.onTouchEvent(widget, buffer, event);
}
public static MovementMethod getInstance() {
if (sInstance == null)
sInstance = new LongClickLinkMovementMethod();
return sInstance;
}
private static LongClickLinkMovementMethod sInstance;
}
LongClickableSpanClass
import android.text.style.ClickableSpan;
import android.view.View;
public abstract class LongClickableSpan extends ClickableSpan {
abstract public void onLongClick(View view);
}
Actual implementation
LongClickableSpan eruptionText = new LongClickableSpan() {
@Override
public void onClick(View tvEruptions) {
LinkFunctions.link_eruption_detail(getApplicationContext(),PostErupionID);
}
@Override
public void onLongClick(View tvEruptions) {
if(SignedInUserID != 0) {
DialogFragment newFragment = new Dialogs.QuickActionsDialogFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putLong("eruptionID", PostErupionID);
newFragment.setArguments(args);
newFragment.show(getFragmentManager(), "QuickActions");
}
}
};
ss.setSpan(eruptionText, startpos[(int) j], endpos[(int) j], Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
I know this question is old and probably the OP has already solved the problem but I made some tweaks on the original LongClickLinkMovementMethod to fire after the time has passed instead of when we release the tap:
import android.os.Handler;
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.MotionEvent;
import android.widget.TextView;
public class LongClickLinkMovementMethod extends LinkMovementMethod {
private Handler mLongClickHandler;
private static int LONG_CLICK_TIME = 1000;
private boolean mIsLongPressed = false;
@Override
public boolean onTouchEvent(final TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
if(action == MotionEvent.ACTION_CANCEL){
if(mLongClickHandler!=null){
mLongClickHandler.removeCallbacksAndMessages(null);
}
}
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);
final LongClickableSpan[] link = buffer.getSpans(off, off, LongClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
if(mLongClickHandler!=null){
mLongClickHandler.removeCallbacksAndMessages(null);
}
if(!mIsLongPressed) {
link[0].onClick(widget);
}
mIsLongPressed = false;
} else {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
mLongClickHandler.postDelayed(new Runnable() {
@Override
public void run() {
link[0].onLongClick(widget);
mIsLongPressed = true;
}
},LONG_CLICK_TIME);
}
return true;
}
}
return super.onTouchEvent(widget, buffer, event);
}
public static MovementMethod getInstance() {
if (sInstance == null) {
sInstance = new LongClickLinkMovementMethod();
sInstance.mLongClickHandler = new Handler();
}
return sInstance;
}
private static LongClickLinkMovementMethod sInstance;
}
So basicaly this uses an Handler to fire the long click event after 1000ms
The simple way is to use View.setTag.
Code example:
tv.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(getApplicationContext(), "onLongClick", Toast.LENGTH_SHORT).show();
v.setTag("Test");
return true;
}
});
private static class NonLongClickableUrlSpan extends URLSpan {
public NonLongClickableUrlSpan(String url) {
super(url);
}
@Override
public void onClick(View widget) {
if (widget.getTag() != null) {
widget.setTag(null);
return;
}
super.onClick(widget);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With