Edit: I have been able to track the problem down to the use of EditText rather than TextView. The repeated calls only happen when a field is an EditText and the system behaves itself when the field is a TextView. I can find nothing in the documentation or online that indicates that LineBackgroundSpan will not work with EditText.
I have updated the MCVE to show how things work with TextView (it does) and with EditText (it doesn't - at least not well). My updated question is how to get LineBackgroundSpan working with EditText.
I have implemented a simple class to add a rounded background to text in an EditText using LineBackgroundSpan. Everything works OK but while debugging I noticed that the drawBackground method of my class is called repeatedly and, seemingly, without end for each span in the string even though no changes are being made. It is not apparent on the display, but is readily apparent if a breakpoint is set in the drawBackground method.
In trying to track down the issue, I was able to reduce the code down to an MCVE.The following code will simply highlight an entire line of text. The top line is an EditText and the bottom line is a TextView. (This is not what I am really trying to do, but it serves the purpose.)
This MCVE exhibits the problem for me on emulators running API 17 and API 24 as well as an actual phone running API 24. Setting the disableDraw argument to true for the constructor of RoundedBackgroudSpan() will disable background drawing action in drawBackground(). I am seeing the problem on the EditText even with background drawing disabled.
What is going on here? Am I misunderstanding how to work with spans? Will spans not work with EditText? Any help will be greatly appreciated.
MainActivity.java
package com.example.bgspanmcve;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.SpannableString;
import android.text.style.LineBackgroundSpan;
import android.util.Log;
import android.widget.EditText;
import android.widget.TextView;
import static android.text.Spanned.SPAN_INCLUSIVE_INCLUSIVE;
public class MainActivity extends AppCompatActivity {
final String dispString = "XAB CD EF";
private static int count = 0; // times drawBackground is called
#Override
protected void onCreate(Bundle savedInstanceState) {
EditText editText;
TextView textView;
RoundedBackgroundSpan bg;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set up the EditText field with a span.
// RoundedBackgroundSpan#drawBackground will be called forever for this EditText.
editText = ((EditText) findViewById(R.id.editText));
SpannableString ssEditText = new SpannableString(dispString);
bg = new RoundedBackgroundSpan(INHIBIT_DRAWING, false);
ssEditText.setSpan(bg, 0, ssEditText.length(), SPAN_INCLUSIVE_INCLUSIVE);
editText.setText(ssEditText);
// Set up the TextView field with a span.
// RoundedBackgroundSpan#drawBackground will be called once for this TextView.
textView = ((TextView) findViewById(R.id.textView));
SpannableString ssTextView = new SpannableString(dispString);
bg = new RoundedBackgroundSpan(INHIBIT_DRAWING, true);
ssTextView.setSpan(bg, 0, ssTextView.length(), SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(ssTextView, TextView.BufferType.EDITABLE);
}
private static class RoundedBackgroundSpan implements LineBackgroundSpan {
private boolean mDisableDraw;
private boolean mIsTextView;
RoundedBackgroundSpan(boolean disableDraw, boolean isTextView) {
super();
mDisableDraw = disableDraw;
mIsTextView = isTextView;
}
#Override
public void drawBackground(
Canvas canvas, Paint paint, int left, int right, int top,
int baseline, int bottom, CharSequence text, int start, int end, int lnum) {
count++;
if (mIsTextView) {
Log.d(TAG, "<<<<drawBackground (TextView) #" + count);
} else {
Log.d(TAG, "<<<<drawBackground (EditText) #" + count);
}
if (mDisableDraw) return;
Paint localPaint = new Paint();
RectF rect = new RectF(left, top, right, bottom);
localPaint.setColor(BG_COLOR);
canvas.drawRoundRect(rect, RADIUS_X, RADIUS_Y, localPaint);
}
private final String TAG = RoundedBackgroundSpan.class.getSimpleName();
private final int BG_COLOR = 0xfF00FF00;
private final int RADIUS_X = 20;
private final int RADIUS_Y = 20;
}
private final static String TAG = MainActivity.class.getSimpleName();
private final boolean INHIBIT_DRAWING = true;
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.bgspanmcve.MainActivity">
<EditText
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginStart="0dp"
android:inputType="text"
android:paddingEnd="0dp"
android:paddingStart="0dp"
android:text="EditText"
android:textSize="20sp" />
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="#id/editText"
android:layout_below="#id/editText"
android:layout_marginStart="0dp"
android:layout_marginTop="16dp"
android:paddingEnd="0dp"
android:paddingStart="0dp"
android:text="TextView"
android:textSize="20sp"
android:textStyle="bold" />
</RelativeLayout>
The call to drawBackground() is timed to the rate of the flashing cursor which is about 500 ms as suggested by #Suragch. I am now convinced that the call to drawBackground() is made as part of the cursor implementation.
As a quick, but not definitive test, I have set the EditText field to not show the cursor but to still be editable (android:cursorVisible="false"). When this attribute set to false, the repeated calls to drawBackground() cease.
I have a problem with my android app. I'm using a simple Textview with a vertical scrollBar to display lyrics from a song. The problem is that in my activity I set a Onclick event on this same Textview. So when I scroll the lyrics in the textview, the activity registers a click event when I release my finger from the screen. I don't want the onClick event to happen after I scroll.
Here is what I have done so far but it does not work really well since im using a onLongClick event wich is not precise enough:
public class NowPlayingActivity extends Activity implements ckListener,OnLongClickListener
{
private TextView lyrics;
private static final String TAG_LYRICS = "LYRICS";
#Override
protected void onCreate(Bundle savedInstanceState)
{
this.lyrics = (TextView) this.findViewById(R.id.now_playing_Lyrics);
this.lyrics.setOnClickListener(this);
this.lyrics.setMovementMethod(new ScrollingMovementMethod());
this.lyrics.setOnLongClickListener(this);
}
public void onClick(View v)
{
String tag = (String) v.getTag();
if (tag.equals(NowPlayingActivity.TAG_LYRICS))
{
if (this.scrolled) //this way, the click action doesnt occur after a scroll
{
this.scrolled = false;
}
else
{
this.scrolled = false;
this.artwork.setVisibility(View.VISIBLE);
this.lyrics.setVisibility(View.GONE);
}
}
public boolean onLongClick(View arg0)
{
this.scrolled = true;
return this.scrolled;
}
what can I do to make it more "accurate" (so I dont have to make a longClick for it to work)
thanks!
Put your textview inside scrollview.
<ScrollView
android:id="#+id/content_scroll"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_margin="7dip"
android:scrollbars="none" >
<TextView
android:id="#+id/fileContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dip" />
</ScrollView>
Then it should work properly. Hope it helps
I need help understanding how to accomplish math between different EditText views. I am not asking someone to write me the code but maybe explain what is involved to get this done.
I wanted to post a picture of this but as a new user I can not. Basicly I have a EditText for the following: Width, Length, Eave Height, Pitch.
I have ID's for all the TextViews I just dont know how to program the behind the scenes math involved to make them work. I do have the equations needed to perform the math just not sure where and how to put them in java.
Basicly I need the user to enter a number in each of the top 4 boxes. I need to use an equation to generate the answer that will be displayed in the "SQFT" box. The user will also input a number in a cost box which will generat a "Total" that needs to be displayed in a separate TextView.
Any help would be appreciated, even if it is to point me in a direction of a tutorial to get me started. Thanks for your help.
Just to show what type of math I need to use, below is the equation I use for excel to calulate.
(length+width)*(Eave+1)*2 + (((width/2)/12*Pitch)*(width/2)*2)
I'm not sure if you don't know how to extract the numbers entered in the EditTexts, how to actually do the math calculation, how to let the user initiate the calculate or how to present it.
I created a small demo that has 2 EditTexts, and a TextView that displays the sum of the numbers entered. The user does not need to press any buttons to perform the calculation, it is performed automatically every time the user updates the text (I assumed this is what you wanted).
Please note this code is not good code, it uses lots of internal anonymous classes etc but it supposed to demonstrate the mechanics of how to do this.
This is the main.xml layout file:
<?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="horizontal" >
<EditText
android:id="#+id/a"
android:hint="input a"
android:inputType="number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:minWidth="60dp"/>
<EditText
android:id="#+id/b"
android:hint="input b"
android:inputType="number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:minWidth="60dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="a+b = " />
<TextView
android:id="#+id/total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp" />
</LinearLayout>
And this is the sample Activity:
package com.example;
import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;
import android.widget.TextView;
public class SumActivity extends Activity
{
private int a;
private int b;
private TextView totalOutput;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
EditText inputA = (EditText) findViewById(R.id.a);
EditText inputB = (EditText) findViewById(R.id.b);
totalOutput = (TextView) findViewById(R.id.total);
inputA.addTextChangedListener(new TextChangedListener()
{
#Override
public void numberEntered(int number)
{
a = number;
updateTotal();
}
});
inputB.addTextChangedListener(new TextChangedListener()
{
#Override
public void numberEntered(int number)
{
b = number;
updateTotal();
}
});
}
private void updateTotal()
{
int total = a + b; // This is where you apply your function
totalOutput.setText("" + total); // need to do that otherwise int will
// be treated as res id.
}
private abstract class TextChangedListener implements TextWatcher
{
public abstract void numberEntered(int number);
#Override
public void afterTextChanged(Editable s)
{
String text = s.toString();
try
{
int parsedInt = Integer.parseInt(text);
numberEntered(parsedInt);
} catch (NumberFormatException e)
{
Log.w(getPackageName(), "Could not parse '" + text + "' as a number", e);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
}
}
I'm trying to create a link in my textbox's adjacent text. This link however is not a URL, but should act as a button so that I can perform a few tasks in the onItemClick event. I'm basically connecting this to a view that shows our End User License Agreement (hard coded).
How can I accomplish this?
Thanks in advance.
You may want only part of the text to be a clickable link, while the rest of the checkbox behaves as usual, i.e. you can click the other text to toggle the state.
You can set up your checkbox like so:
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());
The following code worked for me on KitKat. I am yet to test on below versions of 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());
There actually is an elegant solution, using CheckBox and single TextView. Along with a combinations of TextView.setClickable(), Intent Filter, and TextView.setMovementMethod().
You have main view (here, I called it 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>
and the final piece of codes that glue it all, the 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>
Here comes, the explanations:
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 will allow you to click on textView. But not the HREF link. To do that, you will have to use setMovementMethod() and set it to LinkMovementMethod.
After that, you need to catch the URL. I did this using intent-filter in AndroidManifest.xml
<action android:name="android.intent.action.VIEW" />
<data android:scheme="id.web.freelancer.example.TCActivity" />
It catch VIEW command and it only filter URL starting with id.web.freelancer.example.TCActivity://
Here's the package for you to try it out and here's the github repository. Hope this helped
Kotlin version (through an extension) of Daniel Schuler's answer :
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
}
Usage :
yourCheckBox.addClickableLink(
fullText = "This link must be clickable",
linkText = SpannableString("This link")
) {
// Do whatever you want when onClick()
}
I had the same problem and wanted to have more than one clickable links in the text of a checkbox without loosing the ability to click anywhere in the text (where there is no URL) to select/deselect the checkbox.
The difference to the other answers to this question is that with this solution you can have multiple clickable links in the checkbox text and those links don't have to be at the end of the text.
The layout looks similar to the one in ariefbayu's answer:
<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 now set the text programmatically. The text I want to display is:
"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>"
As it contains HTML, I first convert it to a Spanned. To make the links clickable, I additionally set the movement method of the TextView to LinkMovementMethod:
mTosTextView = (TextView) findViewById(R.id.tosTextView);
mTosTextView.setText(Html.fromHtml(getString(R.string.TOSInfo)));
mTosTextView.setMovementMethod(LinkMovementMethod.getInstance());
And here comes the more tricky part. So far, the CheckBox does not get selected when pressing the TextView. To achive this, I added a touch handler to the 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;
}
});
Finally, as you probably noticed, there is no method getOffsetForPosition(...) yet. If you're targeting API level 14+, you can simply use getOffsetForPosition(), as pointed out by Dheeraj V.S.. As I target API level 8+, I used an implementation that I found here: Determining which word is clicked in an 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);
}
Requirements:
only part of the text to be a clickable link, while the rest of the Checkbox behaves as usual:
Prevent CheckBox state from being toggled when link is clicked
Remove ripple effect from CheckBox
Here is the Kotlin version:
interface HtmlAnchorClickListener {
fun onHyperLinkClicked(name: String)
}
fun addClickableSpan(linkableTextView: TextView?, htmlString: String, listener: HtmlAnchorClickListener) {
linkableTextView?.let {
val sequence = HtmlCompat.fromHtml(htmlString, HtmlCompat.FROM_HTML_MODE_LEGACY)
Log.d("addClickableSpan", "sequence = $sequence")
val spannableString = SpannableStringBuilder(sequence)
val urls = spannableString.getSpans(0, sequence.length, URLSpan::class.java)
urls.forEach { span ->
with(spannableString) {
val start = getSpanStart(span)
val end = getSpanEnd(span)
val flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
val linkColor = linkableTextView.context.getColor(R.color.light_blue)
val clickable = object : ClickableSpan() {
override fun onClick(view: View) {
// Prevent CheckBox state from being toggled when link is clicked
linkableTextView.cancelPendingInputEvents()
removeRippleEffectFromCheckBox(linkableTextView)
listener.onHyperLinkClicked(span.url)
}
override fun updateDrawState(textPaint: TextPaint) {
textPaint.color = linkColor
textPaint.isUnderlineText = true
}
}
setSpan(clickable, start, end, flags)
setSpan(ForegroundColorSpan(linkColor), start, end, flags)
removeSpan(span)
}
with(it) {
text = spannableString
linksClickable = true
movementMethod = LinkMovementMethod.getInstance()
}
}
}
}
fun removeRippleEffectFromCheckBox(textView: TextView) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
var drawable = textView.background
if (drawable is RippleDrawable) {
drawable = drawable.findDrawableByLayerId(0)
textView.background = drawable
}
}
}
Usage:
private fun setUpTermsOfUseHyperLink() {
val checkBoxText =
"I agree to all the <a href='http://www.redbus.in/mob/mTerms.aspx' > Terms and Conditions</a>"
addClickableSpan(cbAccept, checkBoxText, object : HtmlAnchorClickListener {
override fun onHyperLinkClicked(name: String) {
Toast.makeText(context!!, name, Toast.LENGTH_LONG).show()
}
})
}
Create a CheckBox with no text and add two TextViews next to it. The first is a non-clickable view with text like "I have read and agree to the ". The second is a clickable view with text like "TERMS AND CONDITIONS". Place the TextViews side by side without any margin. Notice the extra space in the end of the first view for natural text alignment. This way you could style both texts as you like.
Sample xml code:
<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>
Then add an onClick() handler in the code. Voilá.
public class SignUpActivity extends Activity {
public void onClick(View v) {
...
}
}
I didn't like the solution with the checkBox + textView as your custom view will extend a ViewGroup and not CheckBox thus forcing you to wrap CheckBox behavior.
It was important to me that the custom CheckBox can be used in xml exactly like a regular one.
The acceptable behavior for me was that this CheckBox will only be toggled when you press on it's box and not on it's text.
So I've extended CheckBox, and in order to achieve this behavior I've played with the whole touch mechanism, the full code is below, and an explanation right after it for anyone who like to know how it works.
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();
}
}
it relays on the fact that performClick method is call internally by the TextView mechanism that CheckBox extends, the ClickableSpan is also called by the TextView Mechanism.
so what happens is that when you touch your CheckBox's text it will call both.
So What I've done is detect if the click was in the text area, if so we will disable the perfomClick thus disabling the toggle. but the clickable span will still be called.
Usage:
You still need to add a clickable span and setMovementMethod as before, just like a regular TextView.
If you look for a solution with the URL, i suggest you to use follow solution. With CheckBox and 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);
where
URL_TERMS_AND_CONDS = "yourUrl.com"; and R.string.terms_and_conds = id to the resource with the clickable string.
Here is a simple code snippet to make checkbox spannable string clickable in kotlin:
val myText = ... // your string.
val spannableStr = SpannableString(myText)
val clickableText1 = object : ClickableSpan() {
override fun onClick(widget: View) {
widget.cancelPendingInputEvents()
doMyWorkHere()
}
override fun updateDrawState(text: TextPaint) {
super.updateDrawState(text)
text.color = Color.RED
text.isUnderlineText = true
}
}
val clickableText2 = object : ClickableSpan() {
override fun onClick(widget: View) {
widget.cancelPendingInputEvents()
doMySecondWork()
}
override fun updateDrawState(textPaint: TextPaint) {
super.updateDrawState(textPaint)
textPaint.color = COLOR.BLUE
textPaint.isUnderlineText = false
}
}
spannableStr1.setSpan(clickableText1, 10, 20, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannableStr2.setSpan(clickableText2, 30, 40, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
myCheckBox.text = spannableStr
myCheckBox.movementMethod = LinkMovementMethod.getInstance()
Happy Coding :)
Please try this one
String checkBoxText = "I agree to the Yunolearning <a href='https://blog.google/intl/en-in/' > Blogs</a> and <a href='https://about.google/stories/' > Stories</a>";
MaterialCheckBox singleCheckbox = new MaterialCheckBox(this);
singleCheckbox.setTag(formField.getName());
singleCheckbox.setText(Html.fromHtml(checkBoxText));
singleCheckbox.setMovementMethod(LinkMovementMethod.getInstance());
I want to show all my validation error's of EdiText fields in a popup as shown in below image:
As far as I know Android has drawables:
1) popup_inline_error.9.png
2) popup_inline_error_above.9.png
3) indicator_input_error.png
I am able to display the red error indicator inside the right side of the EditText by using:
Drawable err_indiactor = getResources().getDrawable(R.drawable.indicator_input_error);
mEdiText.setCompoundDrawablesWithIntrinsicBounds(null, null, err_indiactor, null);
Now also i want to display the error message as shown is the first image but it seems I am not getting any idea about this, though I think it should be a Custom Toast.
As the earlier answer is solution for my problem but I have tried a different approach to use a custom Drawable image instead of default indicator_input_error image.
Default Drawable
Custom Drawable
So, I have just created two EditText in my layout xml file and then implemented some Listener in Java code on that EditText.
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:padding="20dip"
android:background="#222222">
<EditText android:layout_width="match_parent"
android:layout_height="wrap_content" android:hint="Username"
android:id="#+id/etUsername" android:singleLine="true"
android:imeActionLabel="Next"></EditText>
<EditText android:layout_width="match_parent"
android:inputType="textPassword"
android:layout_height="wrap_content" android:hint="Password"
android:id="#+id/etPassword" android:singleLine="true"
android:imeActionLabel="Next"></EditText>
</LinearLayout>
EditTextValidator.java
import java.util.regex.Pattern;
import android.app.Activity;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
public class EditTextValidator extends Activity {
private EditText mUsername, mPassword;
private Drawable error_indicator;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Setting custom drawable instead of red error indicator,
error_indicator = getResources().getDrawable(R.drawable.emo_im_yelling);
int left = 0;
int top = 0;
int right = error_indicator.getIntrinsicHeight();
int bottom = error_indicator.getIntrinsicWidth();
error_indicator.setBounds(new Rect(left, top, right, bottom));
mUsername = (EditText) findViewById(R.id.etUsername);
mPassword = (EditText) findViewById(R.id.etPassword);
// Called when user type in EditText
mUsername.addTextChangedListener(new InputValidator(mUsername));
mPassword.addTextChangedListener(new InputValidator(mPassword));
// Called when an action is performed on the EditText
mUsername.setOnEditorActionListener(new EmptyTextListener(mUsername));
mPassword.setOnEditorActionListener(new EmptyTextListener(mPassword));
}
private class InputValidator implements TextWatcher {
private EditText et;
private InputValidator(EditText editText) {
this.et = editText;
}
#Override
public void afterTextChanged(Editable s) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if (s.length() != 0) {
switch (et.getId()) {
case R.id.etUsername: {
if (!Pattern.matches("^[a-z]{1,16}$", s)) {
et.setError("Oops! Username must have only a-z");
}
}
break;
case R.id.etPassword: {
if (!Pattern.matches("^[a-zA-Z]{1,16}$", s)) {
et.setError("Oops! Password must have only a-z and A-Z");
}
}
break;
}
}
}
}
private class EmptyTextListener implements OnEditorActionListener {
private EditText et;
public EmptyTextListener(EditText editText) {
this.et = editText;
}
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_NEXT) {
// Called when user press Next button on the soft keyboard
if (et.getText().toString().equals(""))
et.setError("Oops! empty.", error_indicator);
}
return false;
}
}
}
Now I have tested it like:
For empty EditText validations :
Suppose user click on the Username field then Softkeybord opens and if user press Next key then the user will be focused to the Password field and Username field remains empty then the error will be shown like as given in below images:
For wrong input validations :
1) I type the text vikaS in Username field then error will be like as given in below image :
2) I type the text Password1 in password field then error will be like as given in below image :
Note:
Here I have used custom drawable only in case of when user left the EditText field blank and press Next key on key board but you can use it in any case. Only you need to supply Drawable object in setError() method.
try this..
final EditText editText=(EditText) findViewById(R.id.edit);
editText.setImeActionLabel("",EditorInfo.IME_ACTION_NEXT);
editText.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(actionId==EditorInfo.IME_ACTION_NEXT){
if( editText.getText().toString().trim().equalsIgnoreCase(""))
editText.setError("Please enter some thing!!!");
else
Toast.makeText(getApplicationContext(),"Notnull",Toast.LENGTH_SHORT).show();
}
return false;
}
});
I know answer has been accepted by the asker, but none of the above worked for me.
I was able to reproduce this on my Nexus S running Android 4.0.3.
Here's how I made it work.
Create a theme with:
<style name="MyApp.Theme.Light.NoTitleBar" parent="#android:style/Theme.Light.NoTitleBar">
<item name="android:textColorPrimaryInverse">#android:color/primary_text_light
</item>
</style>
Apply MyApp.Theme.Light.NoTitleBar theme to my application / activity from manifest.
<application
android:name=".MyApp"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/MyApp.Theme.Light.NoTitleBar"
>