Copy text from TextView on Android - android

I have a ListView where each item is a TextView.
I want to enable the long press behaviour similar to an EditText that displays the default context menu with items like "Select all", "Cut all", "Copy all", etc.
Is there an easy way to enable this for a TextView?

I think I have a solution.
Just call
registerForContextMenu(yourTextView);
and your TextView will be registered for receiving context menu events.
Then override onCreateContextMenu in your Activity:
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
//user has long pressed your TextView
menu.add(0, v.getId(), 0, "text that you want to show in the context menu - I use simply Copy");
//cast the received View to TextView so that you can get its text
TextView yourTextView = (TextView) v;
//place your TextView's text in clipboard
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(yourTextView.getText());
}
Hope this helps you and anyone else looking for a way to copy text from a TextView

Actually, you do not have to develop this feature by yourself. You just need to use EditText instead TextView, while you set the android:editable of EditText to false. My code is here:
R.layout.edittext.xml
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="40dip"
android:editable="false"
android:background="#null"
android:textColor="#FFFFFF"/>
ListItemCopyTextActivity.java
public class ListItemCopyTextActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout ll = new LinearLayout(this);
ListView lv = new ListView(this);
String[] values = new String[15];
for (int i = 0; i < 15; i++) {
values[i] = "ListItem NO." + i;
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.edittext, values);
lv.setAdapter(adapter);
ll.addView(lv, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
setContentView(ll);
}
}
You can just long click the item, and choose the select text, copy, cut, past etc.

To allow users to copy some or all of the TextView's value and paste it somewhere else,
set the XML attribute {#link android.R.styleable#TextView_textIsSelectable android:textIsSelectable} to "true"
or
call {#link #setTextIsSelectable setTextIsSelectable(true)}.

You might want to register an onItemLongClickListener on your ListView and then based on the selected item, provide the user with whatever options you choose.

I have a solution, but I'm not exactly to useful.
just use this method :
txtDescDetail.setCursorVisible(true);
i hope to do it.

Here is the solution
<TextView
android:id="#+id/textID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clickable="true"
android:focusable="true"
android:text="Terms and Conditions"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
override setOnLongClickListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textID.setTextIsSelectable(true)
textID. setOnLongClickListener {
val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Copied String", textID.text)
clipboardManager.setPrimaryClip(clip)
true // Or false if not consumed
}
}
the expected behavior will be like the image below

Related

linkify on textview with domain in text not working, links not clickable

I'm not able to linkfy a string with a href when the string says the domain name with it. so string. let me show you.
Here is the string:
<string name="go_to_settings">Please go to "Settings" in mywebsite.com to change this status</string>
And it would say this to the user: "Please go to "Settings" in mywebsite.com to change this status. "
here is the textview I'm using:
<TextView
android:id="#+id/tv_instructions"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_marginTop="73dp"
android:lineSpacingExtra="3sp"
android:textAlignment="center"
android:textColor="#5c5c5c"
android:textColorLink="#color/action_blue"
android:linksClickable="true"
android:text="#string/go_to_settings"/>
and here is how i am trying to make the link clickable:
Linkify.addLinks(tv_instructions, Linkify.ALL)
tv_instructions.movementMethod=LinkMovementMethod.getInstance()
But i am seeing happen on api 24 and 27 device and emulator is that the link is going to www.mywebsite.com instead of www.mywebsite/settings
but here is the strange thing, if i change the text the user sees and remove .com from that then it works fine. so if the user see this instead it works:
and it would say this to the user: "Please go to "Settings" in mywebsite to change this status. "
notice there is no .com being mentioned. How can i get this displayed. i also tried this way:
tv_instructions.text=(utils.fromHTML(resources.getString(R.string.go_to_settings),null))
tv_instructions.movementMethod=LinkMovementMethod.getInstance()
and textview like this:
<TextView
android:id="#+id/tv_instructions"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_marginTop="73dp"
android:lineSpacingExtra="3sp"
android:textAlignment="center"
android:textColor="#5c5c5c"
android:text="#string/go_to_settings"
/>
you guys have any idea what I'm doing wrong ?
Figured this out. for some reason i needed to wrap my text as character data using tag cData in my strings file. this works:
<string name="go_to_settings"><![CDATA[Please go to "Settings" in mywebsite.com to change this status :]]></string>
then the textview looks like this:
<TextView
android:id="#+id/tv_instructions"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_marginTop="73dp"
android:lineSpacingExtra="3sp"
android:text="#string/go_to_settings"
android:textAlignment="center"
android:textColor="#5c5c5c"
android:textColorLink="#color/action_blue"/>
Java Code
tv_instructions.text=(utils.fromHTML(resources.getString(R.string.go_to_settings),null))
tv_instructions.movementMethod=LinkMovementMethod.getInstance()
linkify on textview with domain in text not working, links not clickable
You can also use ClickableSpan
If an object of this type is attached to the text of a TextView with a movement method of LinkMovementMethod, the affected spans of text can be selected. If selected and clicked, the onClick(View) method will be called.
Check below SAMPLE CODE
Try this
public class MainActivity extends AppCompatActivity {
TextView myTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = (TextView) findViewById(R.id.myTextview);
setSpan();
}
private void setSpan() {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Please go to \"Settings\" in");
SpannableString spannableString = new SpannableString(" mywebsite.com ");
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View textView) {
Toast.makeText(MainActivity.this, "Click", Toast.LENGTH_SHORT).show();
// you can open your link here as per your requiremnt
}
};
spannableString.setSpan(clickableSpan, 0, spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new UnderlineSpan(), 0, spannableString.length(), 0);
spannableStringBuilder.append(spannableString);
spannableStringBuilder.append("to change this status");
myTextView.setText(spannableStringBuilder);
myTextView.setMovementMethod(LinkMovementMethod.getInstance());
}
}

GridView: Using RecyclerView or Simply TextView with clickable words

So, Does anybody know how to create following grid? I need to set clickable event to every word:
If four words are not fit to a line then should be shown three in a line:
if it will be exceed to a line n words then should be shown n words in a line.
Is there anybody knows how to implement this?
You can use SpannableString and ClickableSpan. this Activity, for example, creates TextView with your text and manages clicks on each word:
public class MainActivity extends AppCompatActivity {
Activity activity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activity = this;
TextView textView = (TextView) findViewById(R.id.textView);
String text = "up down Antidisestablishment over took dropped lighten with from throught fell on up down Antidisestablishment over took dropped lighten with from throught fell on";
String[] textArray = text.split(" ");
SpannableString ss = new SpannableString(text);
int start = 0;
int end = 0;
for(final String item : textArray){
end = start + item.length();
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View textView) {
Toast.makeText(activity, "Say " + item+ "!", Toast.LENGTH_SHORT).show();
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
}
};
ss.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
start += item.length()+1;
}
textView.setText(ss);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.TRANSPARENT);
}
}
and here is activity_main.xml:
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity"
android:padding="5dp">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="#000000"
android:textColorLink="#000000"/>
</LinearLayout>
if you click on any word you get popup with this word
EDIT: to justify the text use the library android-justifiedtextview
But not the library from gradle, there are the old version which does not support SpannableString. I recommend just copy the class JustifyTextView from git to your project. Then you can use this view in your .xml like:
<com.yourdomain.yourproject.JustifyTextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="#000000"
android:textColorLink="#000000"/>
here is what I got with this library:
you also can modify the library to keep last line unjustified. Every word in the text is still clickable.
If the amount of items you have to add is little, consider using a FlowLayout. It extends a LinearLayout, so just wrap it in a ScrollView, add your views to it dynamically, and you should be good to go.

How to add default text in spinner?

I am new to android,I know access of spinner but I want to add default text in spinner and want set lay out like below image,can any one help me with this,
final String[] items = new String[] {"One", "Two", "Three"};
final ArrayAdapter<String> adapter123 = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_dropdown_item, items);
sp3.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View w) {
new AlertDialog.Builder(RegistrationForm.this)
.setTitle("the prompt")
.setAdapter(adapter123, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).create().show();
}
});
My suggestion for your case is use Button initially set text and set gravity (not layout_gravity) to left|center_vertical instead of opening an AlertDialog open a PopupWindow set that Button as anchor of that PopUpWindow. In that PopUpWindow place a ListView and in OnItemClick change text with selected value in that Button using setText(java.lang.CharSequence)
code snippet
XML for that Button
<Button
android:id="#+id/propertyBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/dp_4"
android:background="#drawable/property_btn_large"
android:ellipsize="marquee"
android:gravity="left|center_vertical"
android:marqueeRepeatLimit="marquee_forever"
android:minHeight="0dp"
android:minWidth="0dp"
android:onClick="showPropertyPopUp"
android:paddingLeft="42dp"
android:paddingRight="22dp"
android:shadowColor="#android:color/white"
android:shadowDx="1"
android:shadowDy="1"
android:shadowRadius="1"
android:text="#string/select_property" />
Java code for opening PopUpWindow in that Button click
//don't forget to initialize that button in onCreate(...)
public void showPropertyPopUp(View v) {
propertyList = dbHelper.getAllProperties();
dbHelper.closeDB();
if(propertyList != null && propertyList.size() > 0) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View popUpView = inflater.inflate(R.layout.pop_up, null, false);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.select_dropdown);
popupWindowProperty = new PopupWindow(popUpView, propertyBtn.getWidth(),
300, true);
popupWindowProperty.setContentView(popUpView);
popupWindowProperty.setBackgroundDrawable(new BitmapDrawable(getResources(),
bitmap));
popupWindowProperty.setOutsideTouchable(true);
popupWindowProperty.setFocusable(true);
popupWindowProperty.showAsDropDown(propertyBtn, 0, 0);
ListView dropdownListView = (ListView) popUpView.
findViewById(R.id.dropdownListView);
PropertyDropdownAdapter adapter = new PropertyDropdownAdapter(
AddIncomeActivity.this,
R.layout.row_pop_up_list, propertyList);
dropdownListView.setAdapter(adapter);
dropdownListView.setOnItemClickListener(this);
}
}
code for setting text on that Button in OnItemClick
PropertyInfo addPropertyInfo = propertyList.get(position);
String propertyName = addPropertyInfo.getPropertyName();
propertyBtn.setText(propertyName);
popupWindowProperty.dismiss();
pop_up layout
<?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" >
<ListView
android:id="#+id/dropdownListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#null"
android:fadeScrollbars="false" >
</ListView>
</LinearLayout>
Screenshot on clicking on that Button
Screenshot after item click of ListView
Add one more element in array which you are passing in spinner and if you want to add validation you can check it by runtime using -
if (spnType.getSelectedItemPosition() == 0) {
View view = spnType.getSelectedView();
SpinnerView adapter = (SpinnerView) spnType.getAdapter();
adapter.setError(view, getString(R.string.err_leadtype));
return false;
} else {
strType = arllistType.get(spnType.getSelectedItemPosition()).get(
WebServiceResponseParameter.LIST_VALUE);
}
As Nitish explained, You need to add default value on top of your array R.array.day_birthdate. Suppose you your array is day_birthdate then add day in top where you define
<string-array name="day_birthdate">
<item name="0">day</item>
<item name="1">1</item>
...
</string-array>
Add validaton on Spinner, if option first is selected then,
if (mSpinnerBirthDate.getSelectedItemPosition() == 0) { //where mSpinnerBirthDate is Spinner for Birthdate
//show invalid selection message
}else{
//get selected value from spinner
}
Add one more element in array which you are passing in spinner and then write this code in java file.
String array[]=getResources().getStringArray(R.array.name_Of_array);
ArrayAdapter<String> ad=new ArrayAdapter<String>(this,layoutid, array);
spinner.setAdapter(ad);
You can not do that, for that you have to create custom spinner or you have to show alertdialog as a action when text view is clicked.
Edited :
Create a array
String a [] = new String [4] ;
a[0] = "ram";
a[1] = "shyam";
a[2] = "mohan";
a[3] = "krishna";
use this array as a source of listview data, now as a action listview will be displayed and set a listener for listview which will provide you a position of clicked item in the listview,
use that position for array[position], lets position = 2, then clicked item text will be mohan set this text as a item of textview .
Edited 2:
Create custom Dialog with listview inside it.
set onItemClickListener in listview.
onCreate
{
dayTextView.setText("day");
}
onItemClickListener ()
{
dayTextView.setText("Sunday");
}
Edited 3 :
Follow this tutorial for custom dilaog : http://rajeshvijayakumar.blogspot.in/2013/04/alert-dialog-dialog-with-item-list.html

How do I make a portion of a Checkbox's text clickable?

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

Select + copy text in a TextView?

Is there a way to allow the user to select / copy text in a TextView? I need the same functionality of EditText where you can long-press the control and get the popup options of select all / copy, but I need the control to look like a TextView.
Tried a few things like making an EditText use the editable="none" option or inputType="none", but those still retain the framed background of an EditText, which I don't want,
Thanks
------- Update ----------------------
This is 99% there, all I'd want is for the selection hilight to be visible (the orange stuff). Other than that it's good, could live with this though:
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:editable="false"
style="?android:attr/textViewStyle"
android:textColor="#color/white"
android:textAppearance="#android:style/TextAppearance.Medium"
android:cursorVisible="false"
android:background="#null" />
I guess it's being caused because of cursorVisible="false" but without that the cursor is present even without any selection being made.
android:textIsSelectable works (at least in ICS - I haven't yet checked in earlier versions)
<TextView
android:id="#+id/deviceIdTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:text="" />
Text View needs to be enabled, focusable, longClickable and textIsSelectable
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:id="#+id/pwTextView"
android:enabled="true"
android:textIsSelectable="true"
android:focusable="true"
android:longClickable="true" />
I think I have a better solution.
Just call
registerForContextMenu(yourTextView);
and your TextView will be registered for receiving context menu events.
Then override onCreateContextMenu in your Activity
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
//user has long pressed your TextView
menu.add(0, v.getId(), 0, "text that you want to show in the context menu - I use simply Copy");
//cast the received View to TextView so that you can get its text
TextView yourTextView = (TextView) v;
//place your TextView's text in clipboard
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(yourTextView.getText());
}
Hope this helps you and anyone else looking for a way to copy text from a TextView
textview1.setTextIsSelectable(true);
This will enable user to select and copy text on long clicking or as we do usually
Using Kotlin Programmatically (Manual Copy)
button.setTextIsSelectable(true)
Or, add a Kotlin property extension
var TextView.selectable
get() = isTextSelectable
set(value) = setTextIsSelectable(value)
Then call
textview.selectable = true
// or
if (textview.selectable) { ...
Using Kotlin Programmatically (Auto-Copy)
If you want to auto-copy when user long-presses you view, this is the base code required:
myView.setOnLongClickListener {
val clipboardManager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Copied String", myString)
clipboardManager.setPrimaryClip(clip)
true // Or false if not consumed
}
You may want to add a Toast to confirm it happened
Or, add a Kotlin extension function
myView.copyOnHold() // pass custom string to not use view contents
fun TextView.copyOnHold(customText: String? = null) {
setOnLongClickListener {
val clipboardManager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Copied String", customText ?: text)
clipboardManager.setPrimaryClip(clip)
true // Or false if not consumed
}
}
Using Xml (Manual Copy)
Add this to your <TextView>
android:textIsSelectable="true"
NOTE: All of these require android:enabled="true" and android:focusable="true", which are the default values for a TextView.
I'm trying to implement the same, and your question helped me to set my editext layout correctly. So Thanks! :)
Then I realized, that the highlight will actually be visible if the cursor is on.
But I just like you do not want to see a cursor before long clicking on the text, so I hide the cursor in the layout.xml file just like you, and added an eventlistener for long click and display the cursor only when a selection starts.
So add the listener in your Activity in the onCreate section:
public TextView htmltextview;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
htmltextview.setOnLongClickListener(new OnLongClickListener(){
public boolean onLongClick(View v) {
htmltextview.setCursorVisible(true);
return false;
}
});
}
And voilá, no cursor at the beginning, and if you long-click, the cursor appears with the selection boundaries.
I hope I could help.
Cheers,
fm
I was also trying to do something similar but still needed a custom approach with manipulation of highlighting of text in TextView. I triggered highlight and copying on LongClick action.
This is how I managed using SpannableString:
SpannableString highlightString = new SpannableString(textView.getText());
highlightString.setSpan(new BackgroundColorSpan(ContextCompat.getColor(getActivity(), R.color.gray))
, 0, textView.getText().length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(highlightString);
copyToClipboard(urlToShare);
and the copy function:
public void copyToClipboard(String copyText) {
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("url", copyText);
clipboard.setPrimaryClip(clip);
Toast toast = Toast.makeText(getActivity(), "Link is copied", Toast.LENGTH_SHORT);
toast.show();
}
I hope it's helpful for someone who ends up on this question :)
I have found it doesn't work the first time I double click, but it works there after ( at least in android 11). This told me it needed to get focus. So, in the onCreate event, I first made the text view selectable, then I requested the focus to shift to the text view. Now I'm not saying the text view can lose focus and the first attempted selection will work. Not guaranteed. What is guaranteed is once it has focus, it'll work every time until it loses focus again. Don't forget about androids animations. So allow at least a half second for the non overridable animation to play out when the keyboard is hiding.
// In onCreate
TextView1.setTextIsSelectable( true );
// Allow animations to play out.
timer = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView1.requestFocus();
}
});
}
};
_timer.schedule(timer, (int)(1000));
}
Thanks a lot gilbot for your explanation. I just want to add another thing.
Selected text background color follows your app theme's colorAccent
For example check the image below
Here AppTheme is my application theme.
<item name="colorAccent">#color/cold</item>
and the colorAccent value will be the selected text background color.
Just use this simple library:
GitHub: Selectable TextView

Categories

Resources