読者です 読者をやめる 読者になる 読者になる

TextViewのリンク以外をクリックした時はListViewのクリックに行く方法

AndroidでListViewの中にTextViewを使っていて、そこにリンクを表示させようとすると
autolinkやsetMovementMethodを使うが
リンク外をクリックしたときにListViewのクリックにいけなくて困っていた

かなり困ったが回避できたので記事にしておく
他にスマートな方法があったら教えてください





1.MutableLinkMovementMethod.javaを作成

TextViewに埋め込んだUrlリンクをタップした時の動作を置き換える
http://qiita.com/le_skamba/items/cca74696095cbbb65cc3

の戻り値だけ書き換えたもの

package *;//パッケージは適宜書き換え

import android.net.Uri;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.view.MotionEvent;
import android.widget.TextView;

/**
 * Htmlテキストのリンククリックアクションをオーバーライドするためのクラス。<br>
 *
 * original source is android.text.method.LinkMovementMethod.java
 *
 * @author S.Kamba
 *
 */
public class MutableLinkMovementMethod extends LinkMovementMethod {

  /**
   * Urlのリンクをタップした時のイベントを受け取るリスナー
   *
   */
  public interface OnUrlClickListener{
    public abstract void onUrlClick(TextView widget, Uri uri);
  }

  /** Urlクリックリスナー */
  OnUrlClickListener listener = null;

  /*
   * Urlクリックリスナーを登録
   */
  public void setOnUrlClickListener(OnUrlClickListener l){
    listener = l;
  }

  @Override
  public boolean onTouchEvent(TextView widget, Spannable buffer,
                              MotionEvent event) {

    // LinkMovementMethod#onTouchEventそのまんま

    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) {
          // リスナーがあればそちらを呼び出し
          if(link[0] instanceof URLSpan && listener!=null){
            Uri uri = Uri.parse( ((URLSpan)link[0]).getURL() );
            listener.onUrlClick(widget, uri);
          }else{
            link[0].onClick(widget);
          }
        } else if (action == MotionEvent.ACTION_DOWN) {
          Selection.setSelection(buffer,
                  buffer.getSpanStart(link[0]),
                  buffer.getSpanEnd(link[0]));
        }

        return true;
      } else {
        Selection.removeSelection(buffer);
      }
    }

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

2. TextViewにOnTouchListenerをセットする

textView.setOnTouchListener(new ViewGroup.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent event) {
              TextView textView = (TextView) view;
              //LinkMovementMethodを継承したもの 下記参照
              MutableLinkMovementMethod m = new MutableLinkMovementMethod();
              //MovementMethod m=LinkMovementMethod.getInstance();
              //リンクのチェックを行うため一時的にsetする
              textView.setMovementMethod(m);
              boolean mt = m.onTouchEvent(textView, (Spannable) textView.getText(), event);
              //チェックが終わったので解除する しないと親view(listview)に行けない
              textView.setMovementMethod(null);
              //setMovementMethodを呼ぶとフォーカスがtrueになるのでfalseにする
              textView.setFocusable(false);
              //戻り値がtrueの場合は今のviewで処理、falseの場合は親viewで処理
              return mt;
            }
}


これでリンクをクリックしたときはブラウザが開き(引用元通り、別処理も可能)、それ以外のときはListViewのクリックに行くことができる