【问题标题】:Long Click on Clickable span not firing until click is released长点击可点击范围在点击被释放之前不会触发
【发布时间】:2014-11-08 02:34:08
【问题描述】:

我需要一个可点击的跨度来在我的应用程序中同时拥有普通点击和长点击两种方法,我在这里 (In Android - How can I register only long clicks using a ClickableSpan) 发现我可以扩展 LinkMovementMethod 类和 ClickableSpan 类以允许这样做,但目前长按和短按都可以工作,但是对于长按而不是在项目被按下足够长的时间时触发长按动作,它将等到您释放项目以触发。这是我的扩展类代码:

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);
}

实际实施

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);

【问题讨论】:

    标签: android onitemclicklistener onlongclicklistener


    【解决方案1】:

    我知道这个问题很老了,可能 OP 已经解决了这个问题,但我对原来的 LongClickLinkMovementMethod 进行了一些调整,以便在时间过去后触发,而不是在我们释放水龙头时触发:

    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;
    }
    

    所以基本上这使用 Handler 在 1000 毫秒后触发长点击事件

    【讨论】:

    • hmm 我得到了 longClick 并在 longclick 时单击
    【解决方案2】:

    简单的方法是使用View.setTag

    代码示例:

    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);
        }
    }
    

    【讨论】:

      【解决方案3】:

      我用略微最新的 Kotlin 语法编辑了 Pedro Oliveira solution

      class LongClickLinkMovementMethod : LinkMovementMethod() {
          private var longClickHandler: Handler? = null
          private var isLongPressed = false
          override fun onTouchEvent(
              widget: TextView, buffer: Spannable,
              event: MotionEvent
          ): Boolean {
              val action = event.action
              if (action == MotionEvent.ACTION_CANCEL) {
                  longClickHandler?.removeCallbacksAndMessages(null)
              }
              if (action == MotionEvent.ACTION_UP ||
                  action == MotionEvent.ACTION_DOWN
              ) {
                  var x = event.x.toInt()
                  var y = event.y.toInt()
                  x -= widget.totalPaddingLeft
                  y -= widget.totalPaddingTop
                  x += widget.scrollX
                  y += widget.scrollY
                  val layout = widget.layout
                  val line = layout.getLineForVertical(y)
                  val off = layout.getOffsetForHorizontal(line, x.toFloat())
                  val link = buffer.getSpans(
                      off, off,
                      LongClickableSpan::class.java
                  )
                  if (link.isNotEmpty()) {
                      if (action == MotionEvent.ACTION_UP) {
                          longClickHandler?.removeCallbacksAndMessages(null)
                          if (!isLongPressed) {
                              link[0].onClick(widget)
                          }
                          isLongPressed = false
                      } else {
                          Selection.setSelection(
                              buffer,
                              buffer.getSpanStart(link[0]),
                              buffer.getSpanEnd(link[0])
                          )
                          longClickHandler?.postDelayed({
                              link[0].onLongClick(widget)
                              isLongPressed = true
                          }, LONG_CLICK_TIME)
                      }
                      return true
                  }
              }
              return super.onTouchEvent(widget, buffer, event)
          }
      
          companion object {
              private const val LONG_CLICK_TIME = 500L
              val instance: MovementMethod?
                  get() {
                      if (sInstance == null) {
                          sInstance = LongClickLinkMovementMethod()
                          // Handler deprecated https://stackoverflow.com/a/62477706/4116924
                          sInstance!!.longClickHandler = Handler(Looper.getMainLooper())
                      }
                      return sInstance
                  }
      
              private var sInstance: LongClickLinkMovementMethod? = null
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2016-09-19
        • 1970-01-01
        • 2018-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多