Как сделать часть текста флажка интерактивной? - PullRequest
38 голосов
/ 18 ноября 2011

Я пытаюсь создать ссылку в смежном тексте моего текстового поля.Эта ссылка, однако, не является URL-адресом, но должна действовать как кнопка, чтобы я мог выполнить несколько задач в событии onItemClick.Я в основном подключаю это к представлению, которое показывает наше Лицензионное соглашение с конечным пользователем (жестко запрограммировано).

Как мне это сделать?

Заранее спасибо.

Ответы [ 8 ]

44 голосов
/ 11 февраля 2014

Следующий код работал для меня на 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());
32 голосов
/ 18 ноября 2011

На самом деле есть элегантное решение, использующее 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. Я сделал это, используя intent-filter в AndroidManifest.xml

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

Она ловит команду VIEW и фильтрует только URL, начиная с id.web.freelancer.example.TCActivity://

Вот пакет, чтобы вы могли его попробовать и , вот репозиторий github . Надеюсь, что это помогло

27 голосов
/ 07 ноября 2017

Возможно, вы захотите, чтобы только часть текста была интерактивной ссылкой, тогда как остальная часть флажка ведет себя как обычно, т.е. вы можете щелкнуть другой текст, чтобы переключить состояние.

Вы можете настроитьгалочка вот так:

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());
5 голосов
/ 17 июля 2013

У меня была та же проблема, и я хотел, чтобы в тексте флажка было более одной кликабельной ссылки без потери возможности щелкнуть в любом месте текста (где нет URL), чтобы установить / снять флажок.

Отличие от других ответов на этот вопрос заключается в том, что с этим решением вы можете иметь несколько кликабельных ссылок в тексте флажка, и эти ссылки не обязательно должны быть в конце текста.

Макет выглядит так же, как в 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>

Теперь я установил текст программно. Текст, который я хочу отобразить:

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

А вот и более сложная часть. До сих пор CheckBox не выбирается при нажатии TextView. Чтобы добиться этого, я добавил сенсорный обработчик в 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+, я использовал реализацию, которую нашел здесь: Определение слова, которое нажимается в текстовом представлении 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);
}
3 голосов
/ 18 ноября 2011

Создайте CheckBox без текста и добавьте два TextView рядом с ним. Первый - это не кликабельный вид с текстом типа «Я прочитал и согласен с». Вторым является интерактивный вид с текстом типа «УСЛОВИЯ И УСЛОВИЯ». Поместите TextView s бок о бок без каких-либо полей. Обратите внимание на дополнительное пространство в конце первого представления для естественного выравнивания текста. Таким образом, вы можете оформить оба текста так, как вам нравится.

Пример 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) {
        ...
    }  
}
0 голосов
/ 13 июня 2019

версия 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 голосов
/ 19 июня 2016

Мне не понравилось решение с checkBox + textView, так как ваш пользовательский вид расширяет ViewGroup, а не CheckBox, заставляя вас обернуть поведение CheckBox.

Для меня было важно, чтобы пользовательский CheckBox могиспользовать в xml точно так же, как обычный.

Для меня приемлемым было то, что этот CheckBox будет переключаться только тогда, когда вы нажимаете на его поле, а не на его текст.

Итак, я расширил 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();
}
}

он основан на том факте, что метод executeClick вызывается внутри механизма TextView, который расширяет CheckBox, ClickableSpan также вызывается механизмом TextView.так что получается, что когда вы касаетесь текста вашего CheckBox, он будет вызывать оба.

Итак, что я сделал, так это обнаружил, был ли щелчок в текстовой области, если так, мы отключим perfomClick, таким образом отключив переключатель,но кликабельный диапазон будет по-прежнему вызываться.

Использование:

Вам все еще нужно добавить кликабельный диапазон и setMovementMethod, как и раньше, как обычный TextView.

0 голосов
/ 05 февраля 2013

Если вы ищете решение с 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 = идентификатор ресурса с интерактивной строкой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...