web-dev-qa-db-fra.com

Comment rendre une partie d'un texte de case à cocher cliquable?

J'essaie de créer un lien dans le texte adjacent de ma zone de texte. Ce lien n’est cependant pas une URL, mais doit servir de bouton pour pouvoir effectuer quelques tâches dans l’événement onItemClick. En gros, je connecte ceci à une vue qui montre notre contrat de licence utilisateur final (codé en dur).

Comment puis-je accomplir cela? 

Merci d'avance.

32
PaulG

Il existe une solution élégante, utilisant CheckBox et single TextView. Avec une combinaison de TextView.setClickable(), filtre d'intention et TextView.setMovementMethod().

Vous avez la vue principale (ici, je l'ai appelée 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>

et le dernier morceau de code qui colle tout, le 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>

Voici les explications:

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 vous permettra de cliquer sur textView. Mais pas le lien HREF. Pour ce faire, vous devrez utiliser setMovementMethod() et le définir sur LinkMovementMethod.

Après cela, vous devez attraper l'URL. Je l'ai fait en utilisant intent-filter dans AndroidManifest.xml

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

Il intercepte la commande VIEW et ne filtre que les URL commençant par id.web.freelancer.example.TCActivity://

Voici le paquet pour que vous puissiez l’essayer et voici le référentiel github . J'espère que cela a aidé 

31
ariefbayu

Le code suivant a fonctionné pour moi sur KitKat. Je n'ai pas encore testé les versions ci-dessous d'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());
37
Gopinath

Vous pouvez souhaiter que seule une partie du texte soit un lien cliquable, alors que le reste de la case à cocher se comporte comme d'habitude, c'est-à-dire que vous pouvez cliquer sur l'autre texte pour basculer l'état.

Vous pouvez configurer votre case à cocher comme suit:

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());
20
Daniel Schuler

J'ai eu le même problème et je voulais avoir plus d'un lien cliquable dans le texte d'une case à cocher sans perdre la possibilité de cliquer n'importe où dans le texte (sans URL) pour sélectionner/désélectionner la case à cocher.

La différence par rapport aux autres réponses à cette question est qu'avec cette solution, vous pouvez avoir plusieurs liens cliquables dans le texte de la case à cocher et que ces liens ne doivent pas nécessairement se trouver à la fin du texte.

La disposition ressemble à celle de la réponse de ariefbayu :

<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>

Je place maintenant le texte par programme. Le texte que je veux afficher est: 

"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>"

Comme il contient du HTML, je le convertis d’abord en Spanned. Pour rendre les liens cliquables, j'ai également défini la méthode de déplacement de TextView sur LinkMovementMethod:

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

Et voici la partie la plus délicate. Jusqu'à présent, la case à cocher n'est pas sélectionnée lorsque vous appuyez sur le TextView. Pour y parvenir, j'ai ajouté un gestionnaire de touches au 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;
    }
});

Enfin, comme vous l'avez probablement remarqué, il n'y a pas encore de méthode getOffsetForPosition(...). Si vous ciblez une API de niveau 14 ou supérieur, vous pouvez simplement utiliser getOffsetForPosition() , comme l'a souligné Dheeraj V.S. . En tant que cible de l'API de niveau 8+, j'ai utilisé une implémentation que j'ai trouvée ici: Détermination du mot sur lequel l'utilisateur a cliqué dans une vue de texte Android .

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

Créez une CheckBox sans texte et ajoutez deux TextViews à côté. La première est une vue non-cliquable avec un texte du type "J'ai lu et j'accepte les". La seconde est une vue cliquable avec un texte du type "TERMES ET CONDITIONS". Placez les TextViews côte à côte sans aucune marge. Notez l'espace supplémentaire à la fin de la première vue pour un alignement naturel du texte. De cette façon, vous pouvez styler les deux textes comme vous le souhaitez.

Exemple de code 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>


Ajoutez ensuite un gestionnaire onClick() dans le code. Voila.

public class SignUpActivity extends Activity {

    public void onClick(View v) {
        ...
    }  
}
3
Jarno Argillander

Je n’aimais pas la solution avec checkBox + textView car votre vue personnalisée étendrait un ViewGroup et non pas CheckBox, ce qui vous obligeait à envelopper le comportement de CheckBox.

Il était important pour moi que la CheckBox personnalisée puisse être utilisée en XML exactement comme une autre.

Le comportement acceptable pour moi était que cette case à cocher ne sera basculée que lorsque vous appuierez sur sa case et non sur son texte. 

J'ai donc étendu CheckBox et, pour obtenir ce comportement, j'ai joué avec tout le mécanisme tactile, le code complet est présenté ci-dessous, ainsi qu'une explication juste après, pour ceux qui aiment savoir comment cela fonctionne.

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();
}
}

il rappelle que la méthode performClick est appelée en interne par le mécanisme TextView étendu par CheckBox; ClickableSpan est également appelé par le mécanisme TextView . de sorte que lorsque vous touchez le texte de votre CheckBox, il appelle les deux.

Donc, ce que j’ai fait, c’est de détecter si le clic était dans la zone de texte. Si tel est le cas, nous désactiverons perfomClick, ce qui désactivera la bascule. mais la durée cliquable sera encore appelée.

Usage:

Vous devez toujours ajouter une plage cliquable et définirMovementMethod comme avant, comme un TextView ordinaire.

0
ndori

Si vous recherchez une solution avec l'URL, je vous suggère d'utiliser la solution suivante. Avec CheckBox et 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"; et R.string.terms_and_conds = id à la ressource avec la chaîne cliquable.

0
validcat