poner - formulario en android studio
manejar el enlace textview hacer clic en mi aplicaciĆ³n de Android (13)
Solución
Implementé una pequeña clase con la ayuda de la cual puede manejar largos clics en TextView y Taps en los enlaces en TextView.
Diseño
TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"/>
TextViewClickMovement.java
import android.content.Context;
import android.text.Layout;
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.Patterns;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.TextView;
public class TextViewClickMovement extends LinkMovementMethod {
private final String TAG = TextViewClickMovement.class.getSimpleName();
private final OnTextViewClickMovementListener mListener;
private final GestureDetector mGestureDetector;
private TextView mWidget;
private Spannable mBuffer;
public enum LinkType {
/** Indicates that phone link was clicked */
PHONE,
/** Identifies that URL was clicked */
WEB_URL,
/** Identifies that Email Address was clicked */
EMAIL_ADDRESS,
/** Indicates that none of above mentioned were clicked */
NONE
}
/**
* Interface used to handle Long clicks on the {@link TextView} and taps
* on the phone, web, mail links inside of {@link TextView}.
*/
public interface OnTextViewClickMovementListener {
/**
* This method will be invoked when user press and hold
* finger on the {@link TextView}
*
* @param linkText Text which contains link on which user presses.
* @param linkType Type of the link can be one of {@link LinkType} enumeration
*/
void onLinkClicked(final String linkText, final LinkType linkType);
/**
*
* @param text Whole text of {@link TextView}
*/
void onLongClick(final String text);
}
public TextViewClickMovement(final OnTextViewClickMovementListener listener, final Context context) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener());
}
@Override
public boolean onTouchEvent(final TextView widget, final Spannable buffer, final MotionEvent event) {
mWidget = widget;
mBuffer = buffer;
mGestureDetector.onTouchEvent(event);
return false;
}
/**
* Detects various gestures and events.
* Notify users when a particular motion event has occurred.
*/
class SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent event) {
// Notified when a tap occurs.
return true;
}
@Override
public void onLongPress(MotionEvent e) {
// Notified when a long press occurs.
final String text = mBuffer.toString();
if (mListener != null) {
Log.d(TAG, "----> Long Click Occurs on TextView with ID: " + mWidget.getId() + "/n" +
"Text: " + text + "/n<----");
mListener.onLongClick(text);
}
}
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
// Notified when tap occurs.
final String linkText = getLinkText(mWidget, mBuffer, event);
LinkType linkType = LinkType.NONE;
if (Patterns.PHONE.matcher(linkText).matches()) {
linkType = LinkType.PHONE;
}
else if (Patterns.WEB_URL.matcher(linkText).matches()) {
linkType = LinkType.WEB_URL;
}
else if (Patterns.EMAIL_ADDRESS.matcher(linkText).matches()) {
linkType = LinkType.EMAIL_ADDRESS;
}
if (mListener != null) {
Log.d(TAG, "----> Tap Occurs on TextView with ID: " + mWidget.getId() + "/n" +
"Link Text: " + linkText + "/n" +
"Link Type: " + linkType + "/n<----");
mListener.onLinkClicked(linkText, linkType);
}
return false;
}
private String getLinkText(final TextView widget, final Spannable buffer, final MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (link.length != 0) {
return buffer.subSequence(buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0])).toString();
}
return "";
}
}
}
Uso
TextView tv = (TextView) v.findViewById(R.id.textview);
tv.setText(Html.fromHtml("<a href=''test''>test</a>"));
textView.setMovementMethod(new TextViewClickMovement(this, context));
Campo de golf
Espero que este helops! Puedes encontrar el código here .
Actualmente, estoy procesando la entrada de HTML en un TextView de esta manera:
tv.setText(Html.fromHtml("<a href=''test''>test</a>"));
El HTML que se muestra se me proporciona a través de un recurso externo, por lo que no puedo cambiar las cosas como lo haré, pero puedo, por supuesto, manipular algunas expresiones regulares con el HTML, para cambiar el valor href, por ejemplo, a otra cosa.
Lo que quiero es poder manejar un enlace, hacer clic directamente desde dentro de la aplicación, en lugar de hacer que el enlace abra una ventana del navegador. ¿Es esto alcanzable en absoluto? Supongo que sería posible establecer el protocolo del href-value en algo como "myApp: //", y luego registrar algo que permita que mi aplicación maneje ese protocolo. Si esta es la mejor manera, me gustaría saber cómo se hace, pero espero que haya una manera más fácil de decir: "cuando se hace clic en un enlace en esta vista de texto, quiero plantear un evento que recibe el valor href del enlace como parámetro de entrada "
Aquí hay una solución más genérica basada en la respuesta de @Arun
public abstract class TextViewLinkHandler extends LinkMovementMethod {
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_UP)
return super.onTouchEvent(widget, buffer, event);
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
if (link.length != 0) {
onLinkClick(link[0].getURL());
}
return true;
}
abstract public void onLinkClick(String url);
}
Para usarlo simplemente implemente onLinkClick
de la clase TextViewLinkHandler
. Por ejemplo:
textView.setMovementMethod(new TextViewLinkHandler() {
@Override
public void onLinkClick(String url) {
Toast.makeText(textView.getContext(), url, Toast.LENGTH_SHORT);
}
});
Cambié el color de TextView a azul usando, por ejemplo:
android:textColor="#3399FF"
en el archivo xml Cómo hacer que se destaque se explica here .
Luego use su propiedad onClick para especificar un método (supongo que podría llamar a setOnClickListener(this)
otra manera), por ejemplo:
myTextView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
doSomething();
}
});
En ese método, puedo hacer lo que quiera como normal, como lanzar un intento. Tenga en cuenta que todavía tiene que hacer el normal myTextView.setMovementMethod(LinkMovementMethod.getInstance());
cosa, como en el método onCreate () de su actividad.
Ejemplo: supongamos que ha establecido un texto en la vista de texto y desea proporcionar un enlace sobre una expresión de texto en particular: "Haga clic en #facebook que lo llevará a facebook.com"
En el diseño xml:
<TextView
android:id="@+id/testtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
En actividad:
String text = "Click on #facebook will take you to facebook.com";
tv.setText(text);
Pattern tagMatcher = Pattern.compile("[#]+[A-Za-z0-9-_]+//b");
String newActivityURL = "content://ankit.testactivity/";
Linkify.addLinks(tv, tagMatcher, newActivityURL);
Cree también un proveedor de etiquetas como:
public class TagProvider extends ContentProvider {
@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
// TODO Auto-generated method stub
return 0;
}
@Override
public String getType(Uri arg0) {
return "vnd.android.cursor.item/vnd.cc.tag";
}
@Override
public Uri insert(Uri arg0, ContentValues arg1) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
return false;
}
@Override
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
String arg4) {
// TODO Auto-generated method stub
return null;
}
@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
// TODO Auto-generated method stub
return 0;
}
}
En el archivo de manifiesto, realice como entrada para el proveedor y la actividad de prueba como:
<provider
android:name="ankit.TagProvider"
android:authorities="ankit.testactivity" />
<activity android:name=".TestActivity"
android:label = "@string/app_name">
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.cc.tag" />
</intent-filter>
</activity>
Ahora cuando hagas clic en #facebook, invocará testamento. Y en la actividad de prueba puede obtener los datos como:
Uri uri = getIntent().getData();
Es muy simple agregar esta línea a tu código:
tv.setMovementMethod(LinkMovementMethod.getInstance());
Esta respuesta extiende la excelente solución de Jonathan S:
Puede usar el siguiente método para extraer enlaces del texto:
private static ArrayList<String> getLinksFromText(String text) {
ArrayList links = new ArrayList();
String regex = "/(?/b((http|https)://www[.])[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(text);
while (m.find()) {
String urlStr = m.group();
if (urlStr.startsWith("(") && urlStr.endsWith(")")) {
urlStr = urlStr.substring(1, urlStr.length() - 1);
}
links.add(urlStr);
}
return links;
}
Esto se puede usar para eliminar uno de los parámetros en el método clickify()
:
public static void clickify(TextView view,
final ClickSpan.OnClickListener listener) {
CharSequence text = view.getText();
String string = text.toString();
ArrayList<String> linksInText = getLinksFromText(string);
if (linksInText.isEmpty()){
return;
}
String clickableText = linksInText.get(0);
ClickSpan span = new ClickSpan(listener,clickableText);
int start = string.indexOf(clickableText);
int end = start + clickableText.length();
if (start == -1) return;
if (text instanceof Spannable) {
((Spannable) text).setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
SpannableString s = SpannableString.valueOf(text);
s.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
view.setText(s);
}
MovementMethod m = view.getMovementMethod();
if ((m == null) || !(m instanceof LinkMovementMethod)) {
view.setMovementMethod(LinkMovementMethod.getInstance());
}
}
Algunos cambios en ClickSpan:
public static class ClickSpan extends ClickableSpan {
private String mClickableText;
private OnClickListener mListener;
public ClickSpan(OnClickListener listener, String clickableText) {
mListener = listener;
mClickableText = clickableText;
}
@Override
public void onClick(View widget) {
if (mListener != null) mListener.onClick(mClickableText);
}
public interface OnClickListener {
void onClick(String clickableText);
}
}
Ahora puede simplemente configurar el texto en la Vista de texto y luego agregarle un oyente:
TextViewUtils.clickify(textWithLink,new TextUtils.ClickSpan.OnClickListener(){
@Override
public void onClick(String clickableText){
//action...
}
});
La mejor forma que usé y siempre funcionó para mí
android:autoLink="web"
Llegando a esto casi un año después, hay una manera diferente en que resolví mi problema particular. Como quería que el enlace fuera manejado por mi propia aplicación, hay una solución que es un poco más simple.
Además del filtro de intención predeterminado, simplemente dejo que mi actividad objetivo escuche ACTION_VIEW
intents, y específicamente, aquellos con el esquema com.package.name
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.VIEW" />
<data android:scheme="com.package.name" />
</intent-filter>
Esto significa que los enlaces que comienzan con com.package.name://
serán gestionados por mi actividad.
Entonces, todo lo que tengo que hacer es construir una URL que contenga la información que quiero transmitir:
com.package.name://action-to-perform/id-that-might-be-needed/
En mi actividad objetivo, puedo recuperar esta dirección:
Uri data = getIntent().getData();
En mi ejemplo, simplemente podría verificar data
para valores nulos, porque cuando no sea nulo, sabré que fue invocado por medio de dicho enlace. A partir de ahí, extraigo las instrucciones que necesito de la URL para poder mostrar los datos apropiados.
Otra forma, toma un poco de Linkify pero te permite personalizar tu manejo.
Clase de span personalizada:
public class ClickSpan extends ClickableSpan {
private OnClickListener mListener;
public ClickSpan(OnClickListener listener) {
mListener = listener;
}
@Override
public void onClick(View widget) {
if (mListener != null) mListener.onClick();
}
public interface OnClickListener {
void onClick();
}
}
Función de ayudante:
public static void clickify(TextView view, final String clickableText,
final ClickSpan.OnClickListener listener) {
CharSequence text = view.getText();
String string = text.toString();
ClickSpan span = new ClickSpan(listener);
int start = string.indexOf(clickableText);
int end = start + clickableText.length();
if (start == -1) return;
if (text instanceof Spannable) {
((Spannable)text).setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
SpannableString s = SpannableString.valueOf(text);
s.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
view.setText(s);
}
MovementMethod m = view.getMovementMethod();
if ((m == null) || !(m instanceof LinkMovementMethod)) {
view.setMovementMethod(LinkMovementMethod.getInstance());
}
}
Uso:
clickify(textView, clickText,new ClickSpan.OnClickListener()
{
@Override
public void onClick() {
// do something
}
});
Solo para compartir una solución alternativa usando una biblioteca que creé. Con Textoo , esto se puede lograr como:
TextView locNotFound = Textoo
.config((TextView) findViewById(R.id.view_location_disabled))
.addLinksHandler(new LinksHandler() {
@Override
public boolean onClick(View view, String url) {
if ("internal://settings/location".equals(url)) {
Intent locSettings = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(locSettings);
return true;
} else {
return false;
}
}
})
.apply();
O con fuente HTML dinámica:
String htmlSource = "Links: <a href=''http://www.google.com''>Google</a>";
Spanned linksLoggingText = Textoo
.config(htmlSource)
.parseHtml()
.addLinksHandler(new LinksHandler() {
@Override
public boolean onClick(View view, String url) {
Log.i("MyActivity", "Linking to google...");
return false; // event not handled. Continue default processing i.e. link to google
}
})
.apply();
textView.setText(linksLoggingText);
para quien busca más opciones aquí hay uno
// Set text within a `TextView`
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText("Hey @sarah, where did @jim go? #lost");
// Style clickable spans based on pattern
new PatternEditableBuilder().
addPattern(Pattern.compile("//@(//w+)"), Color.BLUE,
new PatternEditableBuilder.SpannableClickedListener() {
@Override
public void onSpanClicked(String text) {
Toast.makeText(MainActivity.this, "Clicked username: " + text,
Toast.LENGTH_SHORT).show();
}
}).into(textView);
RECURSO: CodePath
si hay múltiples enlaces en la vista de texto. Por ejemplo, textview tiene "https: //" y "tel no", podemos personalizar el método LinkMovement y manejar los clics de palabras según un patrón. Adjunto está el Método de Movimiento de Enlace personalizado.
public class CustomLinkMovementMethod extends LinkMovementMethod
{
private static Context movementContext;
private static CustomLinkMovementMethod linkMovementMethod = new CustomLinkMovementMethod();
public boolean onTouchEvent(android.widget.TextView widget, android.text.Spannable buffer, android.view.MotionEvent event)
{
int action = event.getAction();
if (action == MotionEvent.ACTION_UP)
{
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
if (link.length != 0)
{
String url = link[0].getURL();
if (url.startsWith("https"))
{
Log.d("Link", url);
Toast.makeText(movementContext, "Link was clicked", Toast.LENGTH_LONG).show();
} else if (url.startsWith("tel"))
{
Log.d("Link", url);
Toast.makeText(movementContext, "Tel was clicked", Toast.LENGTH_LONG).show();
} else if (url.startsWith("mailto"))
{
Log.d("Link", url);
Toast.makeText(movementContext, "Mail link was clicked", Toast.LENGTH_LONG).show();
}
return true;
}
}
return super.onTouchEvent(widget, buffer, event);
}
public static android.text.method.MovementMethod getInstance(Context c)
{
movementContext = c;
return linkMovementMethod;
}
Esto debe invocarse desde la vista de texto de la siguiente manera:
textViewObject.setMovementMethod(CustomLinkMovementMethod.getInstance(context));
public static void setTextViewFromHtmlWithLinkClickable(TextView textView, String text) {
Spanned result;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
result = Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY);
} else {
result = Html.fromHtml(text);
}
textView.setText(result);
textView.setMovementMethod(LinkMovementMethod.getInstance());
}