web-dev-qa-db-ja.com

チェックボックスのテキストの一部をクリック可能にするにはどうすればよいですか?

テキストボックスの隣接するテキストにリンクを作成しようとしています。ただし、このリンクはURLではありませんが、onItemClickイベントでいくつかのタスクを実行できるようにボタンとして機能する必要があります。基本的に、これをエンドユーザーライセンス契約(ハードコード)を表示するビューに接続しています。

どうすればこれを達成できますか?

前もって感謝します。

40
PaulG

実際には、CheckBoxと単一のTextViewを使用したエレガントなソリューションがあります。 TextView.setClickable()、Intent Filter、およびTextView.setMovementMethod()の組み合わせとともに。

メインビューがあります(ここでは、(ClickableTextViewExampleと呼びました)):

package id.web.freelancer.example;

import Android.app.Activity;
import Android.os.Bundle;
import Android.text.Html;
import Android.text.method.LinkMovementMethod;
import Android.widget.CheckBox;
import Android.widget.TextView;

public class ClickableTextViewExampleActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        CheckBox checkbox = (CheckBox)findViewById(R.id.checkBox1);
        TextView textView = (TextView)findViewById(R.id.textView2);

        checkbox.setText("");
        textView.setText(Html.fromHtml("I have read and agree to the " +
                "<a href='id.web.freelancer.example.TCActivity://Kode'>TERMS AND CONDITIONS</a>"));
        textView.setClickable(true);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
    }
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="fill_parent"
    Android:layout_height="fill_parent"
    Android:orientation="vertical" >

    <LinearLayout
        Android:id="@+id/linearLayout1"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content" >

        <CheckBox
            Android:id="@+id/checkBox1"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="CheckBox" />

        <TextView
            Android:id="@+id/textView2"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="TextView"
            Android:clickable="true" />

    </LinearLayout>

</LinearLayout>

TCActivity.Java

package id.web.freelancer.example;

import Android.app.Activity;
import Android.os.Bundle;

public class TCActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tc);
    }

}

tc.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical" >

        <TextView
        Android:id="@+id/tcView"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Terms and conditions" />


</LinearLayout>

そして、すべてを接着する最後のコード、AndroidManifest.xml

<activity Android:name="TCActivity">
    <intent-filter>
        <category Android:name="Android.intent.category.DEFAULT" />
    <action Android:name="Android.intent.action.VIEW" />
    <data Android:scheme="id.web.freelancer.example.TCActivity" />  
    </intent-filter>            
</activity>

説明は次のとおりです。

textView.setText(Html.fromHtml("I have read and agree to the " +
                     "<a href='id.web.freelancer.example.TCActivity://Kode'>TERMS AND CONDITIONS</a>"));
textView.setClickable(true);
textView.setMovementMethod(LinkMovementMethod.getInstance());

setClickableをクリックすると、textViewをクリックできます。しかし、HREFリンクではありません。そのためには、setMovementMethod()を使用し、LinkMovementMethodに設定する必要があります。

その後、URLをキャッチする必要があります。 AndroidManifest.xmlintent-filterを使用してこれを行いました

<action Android:name="Android.intent.action.VIEW" />
<data Android:scheme="id.web.freelancer.example.TCActivity" />  

VIEWコマンドをキャッチし、id.web.freelancer.example.TCActivity://で始まるURLのみをフィルタリングします

試してみるためのパッケージはこちら および githubリポジトリはこちら これが助けたことを願っています

32
ariefbayu

次のコードはKitKatで機能しました。 Androidの以下のバージョンではまだテストしていません。

String checkBoxText = "I agree to all the <a href='http://www.redbus.in/mob/mTerms.aspx' > Terms and Conditions</a>";

checkBoxView.setText(Html.fromHtml(checkBoxText));
checkBoxView.setMovementMethod(LinkMovementMethod.getInstance());
46
Gopinath

テキストの一部のみをクリック可能なリンクにしたい場合がありますが、残りのチェックボックスは通常どおりに動作します。つまり、他のテキストをクリックして状態を切り替えることができます。

次のようにチェックボックスを設定できます。

CheckBox checkBox = (CheckBox) findViewById(R.id.my_check_box);

ClickableSpan clickableSpan = new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        // Prevent CheckBox state from being toggled when link is clicked
        widget.cancelPendingInputEvents();
        // Do action for link text...
    }
    @Override
    public void updateDrawState(TextPaint ds) {
        super.updateDrawState(ds);
        // Show links with underlines (optional)
        ds.setUnderlineText(true);
    }
};

SpannableString linkText = new SpannableString("Link text");
linkText.setSpan(clickableSpan, 0, linkText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
CharSequence cs = TextUtils.expandTemplate(
    "CheckBox text with link: ^1 , and after link", linkText);

checkBox.setText(cs);
// Finally, make links clickable
checkBox.setMovementMethod(LinkMovementMethod.getInstance());
28
Daniel Schuler

私は同じ問題を抱えていて、チェックボックスを選択/選択解除するためにテキスト内の任意の場所(URLがない)をクリックする機能を失うことなく、チェックボックスのテキスト内に複数のクリック可能なリンクが必要でした。

この質問に対する他の回答との違いは、このソリューションでは、チェックボックスのテキストに複数のクリック可能なリンクを含めることができ、それらのリンクはテキストの最後にある必要がないことです。

レイアウトは ariefbay の答えに似ています:

_<RelativeLayout
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_marginBottom="16dp">

    <CheckBox
        Android:id="@+id/tosCheckBox"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_centerVertical="true"
        Android:checked="false" />

    <TextView
        Android:id="@+id/tosTextView"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_toRightOf="@id/tosCheckBox"
        Android:layout_centerVertical="true"
        Android:clickable="true" />

</RelativeLayout>
_

テキストをプログラムで設定しました。表示するテキストは次のとおりです。

_"I have read and accepted the <a href='https://www.anyurl.com/privacy'>privacy statement</a> and <a href='https://www.anyurl.com/tos'>terms of service.</a>"
_

HTMLが含まれているため、最初にSpannedに変換します。リンクをクリック可能にするために、TextViewの移動方法をLinkMovementMethodに追加で設定します。

_mTosTextView = (TextView) findViewById(R.id.tosTextView);
mTosTextView.setText(Html.fromHtml(getString(R.string.TOSInfo)));
mTosTextView.setMovementMethod(LinkMovementMethod.getInstance());
_

そして、ここからがよりトリッキーな部分です。これまでのところ、TextViewを押してもCheckBoxは選択されません。これを実現するために、TextViewにタッチハンドラーを追加しました。

_mTosCheckBox = (CheckBox) findViewById(R.id.tosCheckBox);
mTosTextView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        CharSequence text = mTosTextView.getText();

        // find out which character was touched
        int offset = getOffsetForPosition(mTosTextView, event.getX(), event.getY());

        // check if this character contains a URL
        URLSpan[] types = ((Spanned)text).getSpans(offset, offset, URLSpan.class);

        if (types.length > 0) {
            // a link was clicked, so don't handle the event
            Log.d("Some tag", "link clicked: " + types[0].getURL());
            return false;
        }

        // no link was touched, so handle the touch to change 
        // the pressed state of the CheckBox
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTosCheckBox.setPressed(true);
                break;

            case MotionEvent.ACTION_UP:
                mTosCheckBox.setChecked(!mTosCheckBox.isChecked());
                mTosCheckBox.setPressed(false);
                break;

            default:
                mTosCheckBox.setPressed(false);
                break;
        }
        return true;
    }
});
_

最後に、おそらくお気づきかもしれませんが、getOffsetForPosition(...)メソッドはまだありません。 APIレベル14+をターゲットにしている場合は、単に getOffsetForPosition()Dheeraj V.S.が指摘したように を使用できます。 APIレベル8+をターゲットにしたので、ここで見つけた実装を使用しました: どのWordがAndroid textview でクリックされたかを判断します。

_public int getOffsetForPosition(TextView textView, float x, float y) {
    if (textView.getLayout() == null) {
        return -1;
    }
    final int line = getLineAtCoordinate(textView, y);
    final int offset = getOffsetAtCoordinate(textView, line, x);
    return offset;
}

private int getOffsetAtCoordinate(TextView textView2, int line, float x) {
    x = convertToLocalHorizontalCoordinate(textView2, x);
    return textView2.getLayout().getOffsetForHorizontal(line, x);
}

private float convertToLocalHorizontalCoordinate(TextView textView2, float x) {
    x -= textView2.getTotalPaddingLeft();
    // Clamp the position to inside of the view.
    x = Math.max(0.0f, x);
    x = Math.min(textView2.getWidth() - textView2.getTotalPaddingRight() - 1, x);
    x += textView2.getScrollX();
    return x;
}

private int getLineAtCoordinate(TextView textView2, float y) {
    y -= textView2.getTotalPaddingTop();
    // Clamp the position to inside of the view.
    y = Math.max(0.0f, y);
    y = Math.min(textView2.getHeight() - textView2.getTotalPaddingBottom() - 1, y);
    y += textView2.getScrollY();
    return textView2.getLayout().getLineForVertical((int) y);
}
_
5
Flo

テキストなしでCheckBoxを作成し、その横に2つのTextViewsを追加します。 1つ目は、「non-clickable」ビューで、「私は読んで同意しました」などのテキストを表示します。 2番目は、「条件と条件」などのテキストを含むclickableビューです。 TextViewsをマージンなしで並べて配置します。自然なテキストの配置のために、最初のビューの最後に余分なスペースがあることに注意してください。これにより、両方のテキストを好きなようにスタイルできます。

サンプルxmlコード:

_<RelativeLayout
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content">
    <CheckBox
        Android:id="@+id/terms_check"
        Android:text=""
        Android:layout_marginLeft="10dp"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"/>
    <TextView
        Android:id="@+id/terms_text"
        Android:layout_toRightOf="@id/terms_check"
        Android:text="I have read and agree to the "
        Android:layout_marginLeft="20dp"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"/>
    <TextView
        Android:id="@+id/terms_link"
        Android:layout_toRightOf="@id/terms_text"
        Android:text="TERMS AND CONDITIONS"
        Android:textColor="#00f"
        Android:onClick="onClick"
        Android:clickable="true"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"/>
</RelativeLayout>
_


次に、コードにonClick()ハンドラーを追加します。ボイラ。

_public class SignUpActivity extends Activity {

    public void onClick(View v) {
        ...
    }  
}
_
3

カスタムビューはCheckBoxではなくViewGroupを拡張するため、CheckBox + textViewを使用したソリューションは気に入らなかったため、CheckBoxの動作をラップする必要があります。

カスタムCheckBoxを通常のXMLとまったく同じようにxmlで使用できることが重要でした。

私にとって受け入れ可能な動作は、このチェックボックスは、テキストではなくボックスを押したときにのみ切り替えられるということでした。

そこで、私はCheckBoxを拡張しました。この動作を実現するために、タッチメカニズム全体でプレイしました。完全なコードを以下に示し、その動作を知りたい人のためにその直後に説明します。

public class CheckBoxWithLinks extends CheckBox {


public CheckBoxWithLinks(Context context) {
    super(context);
}

public CheckBoxWithLinks(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CheckBoxWithLinks(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
public boolean performClick() {
    if ( !onTextClick)
        return super.performClick();
    return false;
}

private boolean onTextClick = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
    onTextClick = !isLeftDrawableClick(event) && !isRightDrawableClick(event);
    return super.onTouchEvent(event);
}

private boolean isRightDrawableClick(MotionEvent event) {
    return event.getX() >= getRight() - getTotalPaddingRight();
}

private boolean isLeftDrawableClick(MotionEvent event) {
    return event.getX() <= getTotalPaddingLeft();
}
}

これは、performClickメソッドがCheckBoxが拡張するTextViewメカニズムによって内部的に呼び出されるという事実を中継します。ClickableSpanはTextViewメカニズムによっても呼び出されます。そのため、CheckBoxのテキストをタッチすると、両方が呼び出されます。

そのため、クリックがテキスト領域にあるかどうかを検出し、そうであれば、perfomClickを無効にしてトグルを無効にします。ただし、クリック可能なスパンは引き続き呼び出されます。

使用法:

通常のTextViewと同様に、以前と同様にクリック可能なスパンとsetMovementMethodを追加する必要があります。

0
ndori

URLを使用したソリューションを探す場合は、フォローソリューションを使用することをお勧めします。 CheckBoxおよびTextViewを使用します。

    final TextView tvTerms = (TextView)findViewById(R.id.tvTerms);

    Pattern pattern = Pattern.compile(getString(R.string.terms_and_conds));
    TransformFilter transFilter = new TransformFilter() {
    @Override
    public String transformUrl(Matcher match, String url) {
        return "";
    }}; 
    Linkify.addLinks(tvTerms, pattern, Constants.URL_TERMS_AND_CONDS, null, transFilter);

どこ URL_TERMS_AND_CONDS = "yourUrl.com";およびR.string.terms_and_conds =クリック可能な文字列を持つリソースのID。

0
validcat

Daniel Schulerの答えの( extension を介して)Kotlinバージョン:

fun CheckBox.addClickableLink(fullText: String, linkText: SpannableString, callback: () -> Unit) {
    val clickableSpan = object : ClickableSpan() {
        override fun onClick(widget: View) {
            widget.cancelPendingInputEvents() // Prevent CheckBox state from being toggled when link is clicked
            callback.invoke()
        }

        override fun updateDrawState(ds: TextPaint) {
            super.updateDrawState(ds)
            ds.isUnderlineText = true // Show links with underlines
        }
    }
    linkText.setSpan(clickableSpan, 0, linkText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
    val fullTextWithTemplate = fullText.replace(linkText.toString(), "^1", false)
    val cs = TextUtils.expandTemplate(fullTextWithTemplate, linkText)

    text = cs
    movementMethod = LinkMovementMethod.getInstance() // Make link clickable
}

使用法:

yourCheckBox.addClickableLink(
    fullText = "This link must be clickable",
    linkText = SpannableString("This link")
) {
    // Do whatever you want when onClick()
}
0
Phil