TextViewのリンクに確認ダイアログをつけてみる。

今回はTextView!
半日悩んで解決したのでメモです。

条件

  1. TextViewに複数のURLが含まれる可能性がある。
  2. そのテキストは自分以外のだれかが入力したもので、HTMLフォーマット化されていない。
  3. そのURLに関して正しいかどうかを判断するロジックはGoodleさんに任せる。
  4. それぞれのURLごとに正しいURLに飛ぶこと。
  5. それぞれのURLをタップしたときに確認ダイアログをつける。

とりあえず簡単なやつから。
まずGoogleさんに判断させる部分だけど、

textView.setAutoLinkMask(Linkify.WEB_URLS);
この一行でOK。あとはGoogleさんがよきに計らってくれる。
複数URLがあっても問題なし。HTMLフォーマットも必要なし。
(ただし、複数あるが、連続してしまうとさすがに判別不能。まぁそこは無視。スプリットでもなんでもすればいい。)

大半の条件はこれで満たせるのだけど、これだけでは確認ダイアログが付けられない。
タップするとアプリから勝手にブラウザに飛んで行ってしまう。

それでは条件満たせないので、それぞれのリンクに確認ダイアログをつけることにする。
そしてここから泥沼へ。

それぞれのリンクはURLSpanというオブジェクトとして保管されている。(それすら今回初めて知りました)
このURLSpanのonClickメソッドを継承し、onClickメソッド内でダイアログ表示→OKならブラウザへで完成するはず。

しかし、残念ながら、TextViewのsetTextメソッドはfinalメソッドのため、継承できず、autoLinkの動きをかえることはできません!何か方法があれば教えてください。

ということで苦肉の策。。。

CharSequence text2 = textView.getText();
if( text2 instanceof Spannable ) {
Spannable spannable = (Spannable) text2;
URLSpan[] spans = spannable.getSpans(0, text2.length(), URLSpan.class);
for (URLSpan urlSpan : spans) {
int start = spannable.getSpanStart(urlSpan);
int end = spannable.getSpanEnd(urlSpan);
spannable.removeSpan(urlSpan);
spannable.setSpan(new MyURLSpan(urlSpan.getURL()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}

やってること

  1. textView.getText()の中身がSpannableであったら、URLSpanを取得する。
  2. それぞれのURLSpanのスタートとエンドのポジションを取得する。
  3. そのURLSpanを削除する。
  4. 同じ場所にMyURLSpanをぶち込む!

全体
MainActivity.java

package com.example.textviewsample;

import android.app.Activity;
import android.os.Bundle;
import android.text.Spannable;
import android.text.Spanned;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

private static final String text = "やふる:http://yahoo.co.jp\nぐぐる:http://www.google.co.jp\nびんぐる:http://www.bing.com/";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

TextView textView = (TextView)this.findViewById(R.id.textview);
textView.setAutoLinkMask(Linkify.WEB_URLS);
textView.setText(text);

CharSequence text2 = textView.getText();
if( text2 instanceof Spannable ) {
Spannable spannable = (Spannable) text2;
URLSpan[] spans = spannable.getSpans(0, text2.length(), URLSpan.class);
for (URLSpan urlSpan : spans) {
int start = spannable.getSpanStart(urlSpan);
int end = spannable.getSpanEnd(urlSpan);
spannable.removeSpan(urlSpan);
spannable.setSpan(new MyURLSpan(urlSpan.getURL()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}

MyURLSpan.java

package com.example.textviewsample;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Parcel;
import android.text.style.URLSpan;
import android.view.View;

public class MyURLSpan extends URLSpan {

public MyURLSpan(Parcel src) {
super(src);
}

public MyURLSpan(String url) {
super(url);
}

@Override
public void onClick(final View widget) {
AlertDialog.Builder builder = new AlertDialog.Builder(widget.getContext());
builder.setTitle("確認");
builder.setMessage(getURL()+"へ飛びますか?");
builder.setPositiveButton("おっけー", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onClickSuper(widget);
}
});
builder.setNegativeButton("やだ", null);
builder.show();
}

private void onClickSuper(View widget) {
super.onClick(widget);
}
}

activity_main.xml


実行結果

確認ダイアログ

何が驚いたって、textView.getText()がString以外のもの返すこと。
CharSequenceのことを全く気にせずStringしか使ってこなかった自分が恥ずかしい。

MyURLSpan込みのTextViewを作成してみたい!がんばる。
今日はここまで。