AndroidのTextViewのautolink=webが冗長になる


URLが含まれる可能性のあるTextViewにautolinkweb="web"を指定すると、自動でURLにあたる部分をクリックしたらブラウザで開くようにしてくれるのだが、 以下のようなテキストが入ると問題が出た。

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:autoLink="web"
            android:text="Hello Mr.Brown! I am Ken."
            android:textSize="20sp"
            />

上のXMLを描画すると

なんと Mr.Brown がリンクになってしまっている。クリックしてブラウザに飛ぶともちろんそんなサイトはないと怒られる。
さらにこの現象は Android5以上(API 21以上)でしか発生しないときた。

原因

調べてみると新しいAndroidAPIだと URLのパターンがhttp(s)がなくともxxx.yyyのようなテキストでもリンクを張ってくれるようになった模様。
どうやらTextViewはautolink属性はURLしか入らないような場合にしか使ってはいけない感じになってしまったようだ。

解決方法

ひとまず、どの機種も同様に表示されるように、httpで始まる部分だけリンクするようにしたかったので、TextViewを拡張したAutolinkTextViewを作成してURLにマッチする正規表現を独自に置き換えた。

参考URL http://stackoverflow.com/questions/30528825/android-autolink-is-too-aggressive

以下のclassを作成


public class AutoLinkTextView extends TextView {

    // URLの正規表現
    private final String regex = "(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";

    // Default constructor when inflating from XML file
    public AutoLinkTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    // Default constructor override
    public AutoLinkTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        String str = text.toString();

        SpannableString spannableString = new SpannableString(str);
        Matcher urlMatcher = Pattern.compile(regex).matcher(str);
        while (urlMatcher.find()) {
            String url = urlMatcher.group();
            int start = urlMatcher.start();
            int end = urlMatcher.end();
            spannableString.setSpan(new GoToURLSpan(url), start, end, 0);
        }

        super.setText(spannableString, type);
        super.setMovementMethod(new LinkMovementMethod());
    }

    /**
     * TextViewのテキスト内のリンクを表現するクラス.
     */
    public class GoToURLSpan extends ClickableSpan {

        private final String url;

        public GoToURLSpan(@NonNull String url) {
            this.url = url;
        }

                 // urlをクリックした時の処理
        public void onClick(View view) {
            // ブラウザを起動
            Uri webPage = Uri.parse(url)  ;
            Intent intent = new Intent(Intent.ACTION_VIEW, webPage);
            view.getContext().startActivity(intent);
        }
    }

}

XML (ちゃんとリンクが出るか確認するために適当なurl追加)

        <AutoLinkTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello Mr.Brown! I am Ken. URL: https://
google.com"
            android:textSize="20sp"
            />

無事 Mr.Brownにリンクがかからないようになった。
(XMLにautolink属性を書くと元の挙動に戻るので注意)

ちなみに上のコードだと、URLをクリックした時の処理を自由にかけるので、デフォルトのブラウザを起動する挙動を変えることも簡単にできる。