Giter Site home page Giter Site logo

android-textview-linkbuilder's Introduction

Android TextView-LinkBuilder Android Arsenal

Screenshot

Insanely easy way to create clickable links within a TextView.

While creating Talon for Twitter, one of the most difficult things I encountered was creating these clickable links based on specific text. Luckily, I have made it easy for anyone to apply this type of style to their TextView's.

Features

Similar to how all the big players do it (Google+, Twitter, cough Talon cough), this library allows you to create clickable links for any combination of Strings within a TextView.

  • Specify long and short click actions of a specific word within your TextView
  • Provide user feedback by highlighting the text when the user touches it
  • Match single Strings or use a regular expression to set clickable links to any text conforming to that pattern
  • Change the color of the linked text
  • Change the color of the linked text when the user touches it
  • Modify the transparency of the text's highlighting when the user touches it
  • Set whether or not you want the text underlined
  • Set whether or not you want the text bold
  • Default link color from an activity theme

The main advantage to using this library over TextView's autolink functionality is that you can link anything, not just web address, emails, and phone numbers. It also provides color customization and touch feedback.

Installation

There are two ways to use this library:

As a Gradle dependency

This is the preferred way. Simply add:

dependencies {
    compile 'com.klinkerapps:link_builder:2.0.5'
}

to your project dependencies and run gradle build or gradle assemble.

As a library project

Download the source code and import it as a library project in Eclipse. The project is available in the folder library. For more information on how to do this, read here.

Example Usage

Functionality can be found in the Kotlin example's MainActivity. For Java check JavaMainActivity.

For a list of regular expressions that I use in Talon, you can go here

// Create the link rule to set what text should be linked.
// can use a specific string or a regex pattern
Link link = new Link("click here")
    .setTextColor(Color.parseColor("#259B24"))                  // optional, defaults to holo blue
    .setTextColorOfHighlightedLink(Color.parseColor("#0D3D0C")) // optional, defaults to holo blue
    .setHighlightAlpha(.4f)                                     // optional, defaults to .15f
    .setUnderlined(false)                                       // optional, defaults to true
    .setBold(true)                                              // optional, defaults to false
    .setOnLongClickListener(new Link.OnLongClickListener() {
        @Override
        public void onLongClick(String clickedText) {
        	// long clicked
        }
    })
    .setOnClickListener(new Link.OnClickListener() {
        @Override
        public void onClick(String clickedText) {
        	// single clicked
        }
    });

TextView demoText = (TextView) findViewById(R.id.test_text);

// create the link builder object add the link rule
LinkBuilder.on(demoText)
    .addLink(link)
    .build(); // create the clickable links

You can also create a CharSequence instead of creating and applying the links directly to the TextView. Do not forget to set the movement method on your TextView's after you have applied the CharSequence, or else the links will not be clickable.

// find the text view. Used to create the link builder
TextView demoText = (TextView) findViewById(R.id.test_text);

// Add the links and make the links clickable
CharSequence sequence = LinkBuilder.from(this, demoText.getText().toString())
    .addLinks(getExampleLinks())
    .build();

demoText.setText(sequence);

// if you forget to set the movement method, then your text will not be clickable!
demoText.setMovementMethod(TouchableMovementMethod.getInstance());

If you would like to set the default text color for links without inputting it manually on each Link object, it can be set from the activity theme.

<style name="LinkBuilderExampleTheme" parent="android:Theme.Holo.Light">
    <item name="linkBuilderStyle">@style/LinkBuilder</item>
</style>
<style name="LinkBuilder">
    <item name="defaultLinkColor">#222222</item>
    <item name="defaultTextColorOfHighlightedLink">#444444</item>
</style>

Kotlin Support

The library is built on Kotlin, so you get some extension methods that you can use to apply the links to the TextView, instead of creating the builder.

val demo = findViewById<TextView>(R.id.demo_text)
demo.applyLinks(link1, link2, ...)
demo.applyLinks(listOfLinks)

Usage with ListView.OnItemClickListener

By default, LinkBuilder will consume all the touch events on your TextView. This means that ListView.OnItemClickListener will never get called if you try to implement it. The fix for this is to implement the LinkConsumableTextView rather than the normal TextView in your layouts.

My LinkConsumableTextView will only consume touch events if you have clicked the link within the TextView. Otherwise, it will defer the touch event to the parent, which allows you to use ListView.OnItemClickListener method.

Contributing

Please fork this repository and contribute back using pull requests. Features can be requested using issues. All code, comments, and critiques are greatly appreciated.

Changelog

The full changelog for the library can be found here.

License

Copyright 2015 Luke Klinker

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

android-textview-linkbuilder's People

Contributors

bod avatar fegabe avatar jamescardona11 avatar johnfrontzos avatar johnjohndoe avatar k2wanko avatar klinker24 avatar klinker41 avatar mauin avatar plackemacher avatar rasheedsulayman avatar rubengees avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-textview-linkbuilder's Issues

Support for Links

This is a very useful library but is there anyway that you could add support for clicking web addresses? The default implementation for TextView in the Android SDK doesn't play well with ListViews.

If you happen to use a certain regex, it would be great if you could share it.

Dismiss DialogFragment by link click crashes the app

Kotlin. Device: Xiaomi. OS: 10.0
We have Fragment A, which opens Dialog Fragment. When the user clicks on the link in Dialog Fragment, we call dismiss(). And it throws an exception:

E/InputEventReceiver: Exception dispatching input event. E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback E/MessageQueue-JNI: kotlin.KotlinNullPointerException at com.klinker.android.link_builder.TouchableMovementMethod.onTouchEvent(TouchableMovementMethod.kt:78) at android.widget.TextView.onTouchEvent(TextView.java:10922) at android.view.View.dispatchTouchEvent(View.java:14000) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3088) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2781) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3088) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2781) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3088) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2781) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3088) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2781) at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:496) at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1853) at android.app.Dialog.dispatchTouchEvent(Dialog.java:863) at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:454) at android.view.View.dispatchPointerEvent(View.java:14261) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5802) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5602) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5103) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5156) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5122) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5262) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5130) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5319) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5103) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5156) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5122) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5130) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5103) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7855) at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7824) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7785) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7986) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:251) at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:336) at android.os.Looper.loop(Looper.java:182) at android.app.ActivityThread.main(ActivityThread.java:7564) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

Link for second word with similar content not working

Hi @klinker24,

In a TextView if I add a LinkBuilder that links hashtags or @mentions then second word sometimes doesn't link properly. The use case is when the second word starts with the first word.

Example: Consider following text as contents of TextView.
#hello #helloBuddy #hello_buddy

#hello is highlighted and linked in all 3 words.

Below is the screenshot for same.
screenshot_1500276421

Please reply ASAP.

Avoiding the need for vibrate permission

The library can call performHapticFeedback() on the TextView to cause vibration. That does not require permissions, and it follows the preferences for vibration that the user set in the device settings

How to apply to different portion of text

Hello , please how can i apply this to different portion of text eg I want to apply this, that and this

i don't know if you understand what i mean, i want to apply to series of text

Customizable default color

It would be nice if users could set their own default text color. In many cases applications use the same color for all the links. If the default color has been set there is no need to repeatedly call Link#setTextColor() then.
Setting the default color could be implemented through resource attributes I suppose.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Theme">
        <attr name="text_link_default_color" format="color" />
    </declare-styleable>
</resources>

Cancel long press monitor

Hello, thank your library, I have a problem, now I am only added setOnClickListener, not added setOnLongClickListener, but when I click text long time, the phone will vibrate, this shows LongClickListener are effective, So I want to cancel LongClickListener or modify the default value, How should I do it? Thank you.

        Link terms = new Link("translate")
                .setTextColor(accentColor)
                .setOnClickListener(s -> {
                    AndroidUtil.openLink(getParent(), "https://translate.google.cn/");
                });

I want to use in combination with Html.fromHtml

That it is the following code will be broken style.

textView.setText(Html.fromHtml(stringHtml));
textView.setMovementMethod(LinkMovementMethod.getInstance());
LinkBuilder.on(textView)
                .addLink(new Link(Patterns.WEB_URL).setTextColor(color).setOnClickListener(webLinkListener))
                .build();

I have a correction. 978d0f6

Regular expression useless

there are several same strings and i just want turn one of them into clickable.
but the regular expression turns out useless(i have verify that the regular expression is right).
after reading the source code, i found the problem.

when meeting a link that contains a pattern, you convert it into a individual link by calling method turnPatternsToLinks().
but after calling that method, some information that the regular expression takes lost, such as the first string that matches or the last one.

for example, for text "fraborna likes fraborna's car", the regular expression "^fraborna" matches the first fraborna, but after call method turnPatternsToLinks(), the link contains pattern just becomes the same as the link contains string "fraborna".

Support for CharSequence

Currently, LinkBuilder only supports taking in a TextView param and setting the text on it directly. This is useful, but a little restricting if the text is only part of a process.

Example:

Say you have a JSON object that comes down from the server. This object has some text that needs to be linkified, and it would be nice to apply that linkification as part of the JSON conversion and store the result in a CharSequence field (which SpannableString implements).

It would be nice if there was an overload for LinkBuilder's constructor that took a CharSequence or String instead of a TextView, and offered a buildCharSeq() method or something similar that just returned the linkified SpannableString as a CharSequence.

What do you think? If you're open to it, I'd be happy to take a crack at pull requesting this.

NullPointerException

java.lang.NullPointerException:
Attempt to invoke virtual method 'int java.lang.String.indexOf(java.lang.String, int)' on a null object reference
at java.util.regex.Pattern.quote(Pattern.java:439)
at com.klinker.android.link_builder.LinkBuilder.on(Unknown Source)
setText
setContext
addLinks
build
addLinkToSpan
addLinkToSpan
applyLink

There is 2 major issues and 1 Imporvment

1st Issue :
setHighlightAlpha Doesn't work with Patterns.
The Fix : You missed this.highlightAlpha = link.getHighlightAlpha(); in the Link(Link link) constructor.

2nd Issue: If TextView doesn't have any links that match patterns, it doesn't show any text in the textView.
The Fix: Move this.spannable = SpannableString.valueOf(text); from addLinkToSpan to any method before Build. or to be more precise, any where before turnPatternsToLinks(); method is called in build.

Improvements : Support LinkBuilder Class using String as parameter not TextView.

app freeze when applying links to large texts with a lot of links

The problem occurs when we have a text with about 3000 symbols, all made of "www.gmail.com www.gmail.com www.gmail.com..." about 230 of them, there are 26 000 for loop iterations in SpannableStringInternal.getSpans() :
at android.text.SpannableStringInternal.getSpans(SpannableStringInternal.java:216)
at android.text.SpannableString.getSpans(SpannableString.java:25)
at android.text.SpannableStringInternal.sendSpanAdded(SpannableStringInternal.java:310)
at android.text.SpannableStringInternal.setSpan(SpannableStringInternal.java:138)
at android.text.SpannableString.setSpan(SpannableString.java:46)
at com.klinker.android.link_builder.LinkBuilder.applyLink(LinkBuilder.java:226)
at com.klinker.android.link_builder.LinkBuilder.addLinkToSpan(LinkBuilder.java:199)
at com.klinker.android.link_builder.LinkBuilder.addLinkToSpan(LinkBuilder.java:174)
at com.klinker.android.link_builder.LinkBuilder.build(LinkBuilder.java:148)

The problems seems that after every setSpan() call there is a sendSpanAdded() call, which triggers the getSpans(), which iterates for every span it has.

Cannot use other spans with the goodies of this library

I want to use custom touchable spans (without links) in my Spannable Strings alongside LinkConsumableTextView and TouchableMovementMethod in a very specific use case. But Link Builder forces using of TouchableSpan which requires Link.
I think it's better that TouchableSpan extends an abstract base class which could be used to create custom touchable spans and also is compatible with LinkConsumableTextView and TouchableMovementMethod.

Support for Text Selection

I am using LinkConsumableTextView and enabled setTextIsSelectable(true) for it in RecyclerView but as soon as I long-press on the LinkConsumableTextView it highlights the text being selected and immediately fires the onClickListener and jumps to the second screen which shouldn't be the case. Can you please help me to achieve the below mentioned functionality?

Functionality:

  • Long Press should enable text selection but shouldn't fire onClickListener().
  • Click (Not Links) should jump to next screen.
  • Clicking on links should function as it is.

cannot use it with EllipsizingTextView

Use

List<Link> links = new ArrayList<>();
        Link explore = new Link(Pattern.compile("#[0-9a-zA-Z\\u4e00-\\u9fa5_-]+"));
        explore.setTextColor(Color.parseColor("#f95548"));
        explore.setHighlightAlpha(0f);
        explore.setUnderlined(false);
        explore.setOnClickListener(new Link.OnClickListener() {
            @Override
            public void onClick(String clickedText) {
            }
        });

        Link at = new Link(Pattern.compile("@[0-9a-zA-Z\\u4e00-\\u9fa5_-]+"));
        at.setTextColor(Color.parseColor("#f95548"));
        at.setHighlightAlpha(0f);
        at.setUnderlined(false);
        at.setOnClickListener(new Link.OnClickListener() {
            @Override
            public void onClick(String clickedText) {
                String nickname = clickedText.replace("@", "");
                new BestAction().SearchAtUser(context,nickname);
            }
        });

        links.add(explore);
        links.add(at);
        LinkBuilder.on(textView)
                .addLinks(links)
                .build();

EllipsizingTextView.java

import android.content.Context;
import android.graphics.Canvas;
import android.text.Layout;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class EllipsizingTextView extends TextView {
    private static final String ELLIPSIS = "...▼";

    public interface EllipsizeListener {
        void ellipsizeStateChanged(boolean ellipsized);
    }

    private final List<EllipsizeListener> ellipsizeListeners = new ArrayList<EllipsizeListener>();
    private boolean isEllipsized;
    private boolean isStale;
    private boolean programmaticChange;
    private String fullText;
    private int maxLines = -1;
    private float lineSpacingMultiplier = 1.0f;
    private float lineAdditionalVerticalPadding = 0.0f;

    public EllipsizingTextView(Context context) {
        super(context);
    }

    public EllipsizingTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }


    @Override
    public void setMaxLines(int maxLines) {
        super.setMaxLines(maxLines);
        this.maxLines = maxLines;
        isStale = true;
    }

    public int getMaxLines() {
        return maxLines;
    }

    @Override
    public void setLineSpacing(float add, float mult) {
        this.lineAdditionalVerticalPadding = add;
        this.lineSpacingMultiplier = mult;
        super.setLineSpacing(add, mult);
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int before, int after) {
        super.onTextChanged(text, start, before, after);
        if (!programmaticChange) {
            fullText = text.toString();
            isStale = true;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (isStale) {
            super.setEllipsize(null);
            resetText();
        }
        super.onDraw(canvas);
    }

    private void resetText() {
        int maxLines = getMaxLines();
        String workingText = fullText;
        boolean ellipsized = false;
        if (maxLines != -1) {
            Layout layout = createWorkingLayout(workingText);
            if (layout.getLineCount() > maxLines) {
                workingText = fullText.substring(0, layout.getLineEnd(maxLines - 1)).trim();
                while (createWorkingLayout(workingText + ELLIPSIS).getLineCount() > maxLines) {
                    int lastSpace = workingText.lastIndexOf(' ');
                    if (lastSpace == -1) {
                        break;
                    }
                    workingText = workingText.substring(0, lastSpace - 1);
                }
                workingText = workingText + ELLIPSIS;
                ellipsized = true;
            }
        }
        if (!workingText.equals(getText())) {
            programmaticChange = true;
            try {
                setText(workingText);
            } finally {
                programmaticChange = false;
            }
        }
        isStale = false;
        if (ellipsized != isEllipsized) {
            isEllipsized = ellipsized;
            for (EllipsizeListener listener : ellipsizeListeners) {
                listener.ellipsizeStateChanged(ellipsized);
            }
        }
    }

    private Layout createWorkingLayout(String workingText) {
        return new StaticLayout(workingText, getPaint(), getWidth() - getPaddingLeft() - getPaddingRight(),
                Alignment.ALIGN_NORMAL, lineSpacingMultiplier, lineAdditionalVerticalPadding, false);
    }

    @Override
    public void setEllipsize(TruncateAt where) {
        // Ellipsize settings are not respected
    }
}

java.lang.AbstractMethodError:

Error occurs on some devices

java.lang.AbstractMethodError: 
  at android.text.TextUtils.writeToParcel (TextUtils.java:702)
  at android.os.Parcel.writeCharSequence (Parcel.java:729)
  at android.view.accessibility.AccessibilityNodeInfo.writeToParcelNoRecycle (AccessibilityNodeInfo.java:3372)
  at android.view.accessibility.AccessibilityNodeInfo.writeToParcel (AccessibilityNodeInfo.java:3181)
  at android.os.Parcel.writeTypedObject (Parcel.java:1516)
  at android.os.Parcel.writeTypedList (Parcel.java:1395)
  at android.os.Parcel.writeTypedList (Parcel.java:1380)
  at android.view.accessibility.IAccessibilityInteractionConnectionCallback$Stub$Proxy.setFindAccessibilityNodeInfosResult (IAccessibilityInteractionConnectionCallback.java:144)
  at android.view.AccessibilityInteractionController.updateInfosForViewportAndReturnFindNodeResult (AccessibilityInteractionController.java:869)
  at android.view.AccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdUiThread (AccessibilityInteractionController.java:340)
  at android.view.AccessibilityInteractionController.access$400 (AccessibilityInteractionController.java:67)
  at android.view.AccessibilityInteractionController$PrivateHandler.handleMessage (AccessibilityInteractionController.java:1324)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:193)
  at android.app.ActivityThread.main (ActivityThread.java:6898)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:537)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:858)

Can't response the item click event

because of in the onTouchEvent method return true.

public boolean onTouchEvent(final TextView textView, final Spannable spannable, MotionEvent event) {
      return true;
}

Do you have some idea ?

Help: I couldn't use it

Please I'm finding it difficult to use. Please can you help me out?

My Activity

    public class PostDetails extends AppCompatActivity {

    private final String TAG = "PostDetails";
    private AlertDialog internetDialog;
    private AlertDialog sthWrongAlert;

    private String postData;

    protected com.nostra13.universalimageloader.core.ImageLoader mImageLoader;


    TextView postTitle, postContent;
    private String post_content;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_post_details);

        showDialog();
        sthWrongDialog();

        postTitle = (TextView) findViewById(R.id.dpost_title);
        postContent = (TextView) findViewById(R.id.dpost_content);
        postContent.setMovementMethod(LinkMovementMethod.getInstance());


        Link link = new Link(Regex.WEB_URL_PATTERN)
                .setTextColor(Color.GREEN)
                .setHighlightAlpha(.4f)
                .setUnderlined(false)
                .setBold(true)
                .setOnClickListener(new Link.OnClickListener() {
                    @Override
                    public void onClick(String clickedText) {
                        Toast.makeText(PostDetails.this, "Link clicked is :: " + clickedText, Toast.LENGTH_SHORT).show();
                        finish();
                    }
                });

        LinkBuilder.on(postContent)
                .addLink(link)
                .build();

        DisplayImageOptions defaultoptions = new DisplayImageOptions.Builder()
                .cacheInMemory(true)
                .cacheOnDisk(true)
                .build();
        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
                .defaultDisplayImageOptions(defaultoptions)
                .writeDebugLogs()
                .build();

        mImageLoader = com.nostra13.universalimageloader.core.ImageLoader.getInstance();
        mImageLoader.init(config);

        loadPost();
    }

    private void showDialog() {
        internetDialog = new AlertDialog.Builder(PostDetails.this)
               ...
    }

    private void sthWrongDialog() {
        sthWrongAlert = new AlertDialog.Builder(PostDetails.this)
               ...
    }

    private void loadPost() {
        Log.d(TAG, "loadPost called");

        final ProgressBar progressBar;
        progressBar = (ProgressBar) findViewById(R.id.progress_circle);
        progressBar.setVisibility(View.VISIBLE);


        String news_id = getIntent().getStringExtra("PostId");
        Log.d(TAG, "You clicked post id " + news_id);

        StringRequest stringRequest = new StringRequest(news_id,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        //Log.d("Debug", response.toString());
                        if (progressBar != null) {
                            progressBar.setVisibility(View.GONE);
                        }
                        parseHtml(response);
                        postData = response;
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        VolleyLog.d(TAG, "Error: " + error.getMessage());

                        if (progressBar != null) {
                            progressBar.setVisibility(View.GONE);
                        }
                        sthWrongAlert.show();
                    }
                });

        //Creating requestqueue
        RequestQueue requestQueue = Volley.newRequestQueue(this);

        //Adding request queue
        requestQueue.add(stringRequest);
    }

    private void parseHtml(String response) {
        Log.d(TAG, "parsinghtml");
        Document document = Jsoup.parse(response);
        String post_title = document.select("h1.entry-title").first().text();
        post_content = document.select("div.entry-content").first().html();

        postTitle.setText(post_title);

        Spanned spanned = Html.fromHtml(post_content, new UILImageGetter(postContent, this), null );
        postContent.setText(spanned);

        //Unhiding views
        postTitle.setVisibility(View.VISIBLE);
        postContent.setVisibility(View.VISIBLE);
    }


    ...
}

When I click on a link, it's supposed to close this activity and show a toast but that ain't happening. Rather, clicking a link still fires the chooser to choose web browsers.
Please, what am I getting wrong?

Lib doesn't work with UrlSpan

Hi, fyi I noticed that the TouchableMovementMethod doesn't propagate events to the LinkMovementMethod always (e.g. event.action == MotionEvent.ACTION_DOWN) . This makes traditional URLSpans to not work.

Request: prepend links

Thanks for making this library public, it's very useful. I have a feature request: it would be nice to be able to prepend some text to a link. For instance: I'm using two different link colors for internal and external links, but I'd like to add a character to indicate it's an external link. Perhaps something like:

link.setPrependedText('text here')

More than one file was found with OS independent path 'META-INF/library_release.kotlin_module'

Hi, when I add the library to the app/build.gradle file:
implementation 'com.klinkerapps:link_builder:2.0.5'

I get the following build error:
`Execution failed for task ':app:mergeDebugJavaResource'.

A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
More than one file was found with OS independent path 'META-INF/library_release.kotlin_module'`

for the moment this resolve the problem:
packagingOptions { exclude 'META-INF/library_release.kotlin_module' }

Links inside a ListView

This is more like a question or maybe a feature request.

I am trying to have clickable links in cells of a ListView, while keeping the rest of the View clickable - and by that I mean that the ListView's OnItemClickListener still get called when clicking outside of the TextView, and also inside of the TextView, but on non-link text.

I've seen people having this problem with ClickableSpans, notably here: http://stackoverflow.com/questions/8558732/listview-textview-with-linkmovementmethod-makes-list-item-unclickable.

I've tried to apply the accepted solution in conjunction with this library but I failed. I suppose this is due to the fact that this library installs its own MovementMethod and so installing another incompatible one would not work.
I have to admit I am not very familiar with the whole Span/MovementMethod deal (which is why I use your library in the first place!) and so the reason or solution of the problem are not so clear to me.

Well I hope this makes sense :) Thanks for the very useful library in any case!

minSdk 14?

Are features from sdk 14 actually used by the code?
If not, you might consider lowering it, to make this library usable by apps targeting < 14.

java.lang.AbstractMethodError:

Mobile device Xiomi Redmi Note 4 and 7

RAM3072MB (TotalMem: 2846 – 3599MB) | Form factorPhone
System on ChipQualcomm MSM8953 (Snapdragon 625) | Screen size1080 x 1920 (normal)
CPU8x ARM Cortex-A53 (2000 Mhz) | Screen density (DPI)480
ABIarm64-v8aarmeabi-v7aarmeabi | SDKAndroid 6.0 (SDK 23)Android 7.0 (SDK 24)
GPUQualcomm Adreno 506 (650 Mhz) | OpenGL ES version3.13.2
Important: Go through these articles to solve the issue:

  1. https://developer.android.com/reference/android/text/ParcelableSpan.html
  2. https://stackoverflow.com/questions/47890687/crash-on-java-lang-abstractmethoderror-android-text-textutils-writetoparcel-may
  3. https://jira.exoplatform.org/browse/MOB-1974

java.lang.AbstractMethodError:
at android.text.TextUtils.writeToParcel (TextUtils.java:648)
at android.os.Parcel.writeCharSequence (Parcel.java:585)
at android.os.Parcel.writeValue (Parcel.java:1420)
at android.os.Parcel.writeList (Parcel.java:811)
at android.view.accessibility.AccessibilityEvent.writeAccessibilityRecordToParcel (AccessibilityEvent.java:1174)
at android.view.accessibility.AccessibilityEvent.writeToParcel (AccessibilityEvent.java:1140)
at android.view.accessibility.IAccessibilityManager$Stub$Proxy.sendAccessibilityEvent (IAccessibilityManager.java:269)
at android.view.accessibility.AccessibilityManager.sendAccessibilityEvent (AccessibilityManager.java:412)
at android.view.ViewRootImpl.requestSendAccessibilityEvent (ViewRootImpl.java:6734)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.ViewGroup.requestSendAccessibilityEvent (ViewGroup.java:891)
at android.view.View.sendAccessibilityEventUncheckedInternal (View.java:6305)
at android.view.View.sendAccessibilityEventUnchecked (View.java:6284)
at android.view.View.sendAccessibilityEventInternal (View.java:6261)
at android.widget.TextView.sendAccessibilityEventInternal (TextView.java:9278)
at android.view.View.sendAccessibilityEvent (View.java:6228)
at android.widget.TextView.onSelectionChanged (TextView.java:8099)
at android.widget.TextView.spanChange (TextView.java:8309)
at android.widget.TextView$ChangeWatcher.onSpanAdded (TextView.java:10409)
at android.text.SpannableStringInternal.sendSpanAdded (SpannableStringInternal.java:390)
at android.text.SpannableStringInternal.setSpan (SpannableStringInternal.java:213)
at android.text.SpannableString.setSpan (SpannableString.java:46)
at android.text.Selection.setSelection (Selection.java:78)
at com.klinker.android.link_builder.h.onTouchEvent (TouchableMovementMethod.kt:9)
at android.widget.TextView.onTouchEvent (TextView.java:8487)
at android.view.View.dispatchTouchEvent (View.java:9955)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2671)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2301)
at com.android.internal.policy.DecorView.superDispatchTouchEvent (DecorView.java:414)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent (PhoneWindow.java:1810)
at android.app.Activity.dispatchTouchEvent (Activity.java:3196)
at a.a.d.j.dispatchTouchEvent (WindowCallbackWrapper.java:1)
at com.android.internal.policy.DecorView.dispatchTouchEvent (DecorView.java:376)
at android.view.View.dispatchPointerEvent (View.java:10177)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent (ViewRootImpl.java:4487)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess (ViewRootImpl.java:4352)
at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3892)
at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3945)
at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3911)
at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:4038)
at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3919)
at android.view.ViewRootImpl$AsyncInputStage.apply (ViewRootImpl.java:4095)
at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3892)
at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3945)
at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3911)
at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3919)
at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3892)
at android.view.ViewRootImpl.deliverInputEvent (ViewRootImpl.java:6263)
at android.view.ViewRootImpl.doProcessInputEvents (ViewRootImpl.java:6237)
at android.view.ViewRootImpl.enqueueInputEvent (ViewRootImpl.java:6198)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent (ViewRootImpl.java:6369)
at android.view.InputEventReceiver.dispatchInputEvent (InputEventReceiver.java:187)
at android.os.MessageQueue.nativePollOnce (Native Method)
at android.os.MessageQueue.next (MessageQueue.java:323)
at android.os.Looper.loop (Looper.java:142)
at android.app.ActivityThread.main (ActivityThread.java:6342)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:880)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:770)

Clickable area is small

I feel the clickable area on a text is small. When I press towards the ends, it's not registering as an Onclick event. Is there any way I could add some padding to increase the clickable area.

Thanks!

it gives NullPointerException when clicked a link for fragment transaction in a recycler view

12-14 12:12:30.516 784-784/ E/InputEventReceiver: Exception dispatching input event. 12-14 12:12:30.516 784-784/ E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback 12-14 12:12:30.522 784-784/ E/MessageQueue-JNI: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.klinker.android.link_builder.TouchableBaseSpan.setTouched(boolean)' on a null object reference at com.klinker.android.link_builder.TouchableMovementMethod.onTouchEvent(TouchableMovementMethod.java:85) at android.widget.TextView.onTouchEvent(TextView.java:10059) at android.view.View.dispatchTouchEvent(View.java:10723) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:559) at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1870) at android.app.Activity.dispatchTouchEvent(Activity.java:3236) at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:68) at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:68) at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:521) at android.view.View.dispatchPointerEvent(View.java:10952) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5121) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4973) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4504) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4557) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4523) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4656) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4531) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4713) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4504) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4557) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4523) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4531) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4504) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7011) at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6940) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6901) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7121) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185) at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.Me

Links last word Clickable in any area after the last word

There seems to be an issue if i have the link as the last word in the TextView and TextView fills parent and there is a massive space after the last word, if i click on the emtpy space after the last word. The last link gets clicked.

Add release tags

Please maintain consistent releases by also adding Git tags and pushing them to this repository.
Please consider using semantic versioning for release tags.

$ git checkout {some-release-commit}
$ git tag -a "v.1.0.0" -m "Short summary of the release."
$ git push -tags origin master

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.