30

レイアウトに TextView があります。とてもシンプルです。レイアウトに OnClickListener を配置し、TextView の一部を ClickableSpan に設定しました。ClickableSpan がクリックされたときに onClick 関数で何かを実行し、TextView の他の部分がクリックされたときに、レイアウトの OnClickListener の onClick 関数で何かを実行する必要があります。これが私のコードです。

    RelativeLayout l = (RelativeLayout)findViewById(R.id.contentLayout);
    l.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "whole layout", Toast.LENGTH_SHORT).show();
        }
    });

    TextView textView = (TextView)findViewById(R.id.t1);
    textView.setMovementMethod(LinkMovementMethod.getInstance());

    SpannableString spannableString = new SpannableString(textView.getText().toString());
    ClickableSpan span = new ClickableSpan() {
        @Override
        public void onClick(View widget) {
            Toast.makeText(MainActivity.this, "just word", Toast.LENGTH_SHORT).show();
        }
    };
    spannableString.setSpan(span, 0, 5, Spannable.SPAN_INCLUSIVE_INCLUSIVE);        
    textView.setText(spannableString);
4

5 に答える 5

30

私もこの問題に遭遇しました.@KMDevが言及したソースコードのおかげで、私はよりクリーンなアプローチを思いつきました.

まず、TextView部分的にクリック可能にする しかないため、実際には、キーの押下、スクロールなどを処理する機能を追加するほとんどの機能LinkMovementMethod(およびそのスーパークラス) は必要ありません。ScrollingMovementMethod

代わりに、次のコードMovementMethodを使用するカスタムを作成します。OnTouch()LinkMovementMethod

ClickableMovementMethod.java

package com.example.yourapplication;

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

/**
 * A movement method that traverses links in the text buffer and fires clicks. Unlike
 * {@link LinkMovementMethod}, this will not consume touch events outside {@link ClickableSpan}s.
 */
public class ClickableMovementMethod extends BaseMovementMethod {

    private static ClickableMovementMethod sInstance;

    public static ClickableMovementMethod getInstance() {
        if (sInstance == null) {
            sInstance = new ClickableMovementMethod();
        }
        return sInstance;
    }

    @Override
    public boolean canSelectArbitrarily() {
        return false;
    }

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

        int action = event.getActionMasked();
        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) {
                    link[0].onClick(widget);
                } else {
                    Selection.setSelection(buffer, buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0]));
                }
                return true;
            } else {
                Selection.removeSelection(buffer);
            }
        }

        return false;
    }

    @Override
    public void initialize(TextView widget, Spannable text) {
        Selection.removeSelection(text);
    }
}

これを使用するClickableMovementMethodと、移動メソッドによってタッチイベントが消費されなくなります。ただし、TextView.setMovementMethod()どの呼び出しTextView.fixFocusableAndClickableSettings()により、クリック可能、ロングクリック可能、およびフォーカス可能が true に設定されView.onTouchEvent()、タッチ イベントが消費されます。これを修正するには、3 つの属性をリセットするだけです。

したがって、 に付随する最後のユーティリティ メソッドClickableMovementMethodは次のとおりです。

public static void setTextViewLinkClickable(TextView textView) {
    textView.setMovementMethod(ClickableMovementMethod.getInstance());
    // Reset for TextView.fixFocusableAndClickableSettings(). We don't want View.onTouchEvent()
    // to consume touch events.
    textView.setClickable(false);
    textView.setLongClickable(false);
}

これは私にとって魅力のように機能します。

のクリック イベントClickableSpanが発生し、それらの外側のクリックは親レイアウト リスナーに渡されます。

あなたがTextView選択可能にしている場合、私はそのケースをテストしていないことに注意してください。おそらく、自分でソースを掘り下げる必要があります:P

于 2015-12-10T11:31:02.767 に答える
5

これが簡単な解決策であり、私にとってはうまくいきました

これは、Textview クラスの getSelectionStart() および getSelectionEnd() 関数で回避策を使用して実現できます。

tv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ClassroomLog.log(TAG, "Textview Click listener ");
        if (tv.getSelectionStart() == -1 && tv.getSelectionEnd() == -1) {
            //This condition will satisfy only when it is not an autolinked text
            //Fired only when you touch the part of the text that is not hyperlinked 
        }
    }
});
于 2016-02-29T10:06:44.607 に答える
0

booleanグローバル変数を宣言します。

boolean wordClicked = false;

次のように宣言して初期化lfinalます。

final RelativeLayout l = (RelativeLayout)findViewById(R.id.contentLayout);

OnClickListenerにを追加textView:

textView.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {
        if (!wordClicked) {
            // Let the click be handled by `l's` OnClickListener
            l.performClick();   
        }
    }
});

変更span:

ClickableSpan span = new ClickableSpan() {

    @Override
    public void onClick(View widget) {
        wordClicked = true;
        Toast.makeText(Trial.this, "just word", Toast.LENGTH_SHORT).show();

        // A 100 millisecond delay to let the click event propagate to `textView's` 
        // OnClickListener and to let the check `if (!wordClicked)` fail
        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                wordClicked = false;                        
            }
        }, 100L);
    }
};

編集:

ユーザー KMDev の回答を視野に入れておくと、次のコードが仕様を満たします。spannableString.setSpan(.., 0, 5, ..);指定された長さのスパンと残りのスパンの 2 つのスパンを作成しますspannableString.setSpan(.., 6, spannableString.legth(), ..);。2 番目ClickableSpan(span2) は をクリックしますRelativeLayout。さらに、 をオーバーライドupdateDrawState(TextPaint)することで、2 番目のスパンに目立たない (スタイルのない) 外観を与えることができます。一方、最初のスパンにはリンクの色があり、下線が引かれています。

final RelativeLayout l = (RelativeLayout)findViewById(R.id.contentLayout);
    l.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v) {
            Toast.makeText(Trial.this, "whole layout", Toast.LENGTH_SHORT).show();
        }
    });

TextView textView = (TextView)findViewById(R.id.t1);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.TRANSPARENT);

SpannableString spannableString = new SpannableString(textView.getText().toString());

ClickableSpan span = new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        Toast.makeText(Trial.this, "just word", Toast.LENGTH_SHORT).show();
    }
};

spannableString.setSpan(span, 0, 5, Spannable.SPAN_INCLUSIVE_INCLUSIVE);     

ClickableSpan span2 = new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        l.performClick();
    }

    @Override
    public void updateDrawState(TextPaint tp) {
        tp.bgColor = getResources().getColor(android.R.color.transparent);
        tp.setUnderlineText(false);
    }
};

spannableString.setSpan(span2, 6, spannableString.length(), 
                                      Spannable.SPAN_INCLUSIVE_INCLUSIVE);  

textView.setText(spannableString);

私の元の回答の問題に気づいてくれたユーザー KMDev に感謝します。ブール変数を使用して (誤った) チェックを実行する必要OnclickListenerはありません。また、TextView に を設定する必要はありません。

于 2013-09-02T12:58:49.757 に答える