java PatternEditableBuilder - 在TextView中创建彩色可点击跨度的简便方法!

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java PatternEditableBuilder - 在TextView中创建彩色可点击跨度的简便方法!相关的知识,希望对你有一定的参考价值。

# PatternEditableBuilder

This Android utility is about making clickable colored spans within a `TextView` as painless and simple as possible! One common use case for this is building a Twitter or Facebook type app where **different words in a message have different meanings** and can be clicked to trigger an action. For example:

 * Tweet items where "@foo" can be clicked to view a user's profile.
 * Facebook posts where "Billy Jean" can be clicked to view a user. 
 * Slack messges where "#general" can be clicked to go to a different room. 

## Usage

This utility assumes you have one or more `TextView` that are filled with text that you'd like to "spannify" based on different patterns. Suppose we have a `TextView` that contains the following totally unstyled text:

<img src="http://i.imgur.com/YarVGm9.png" width="500" />

### Linkify Pattern

Linkify all sub-strings within `TextView` that match a regular expression:

```java
new PatternEditableBuilder().
         addPattern(Pattern.compile("\\@(\\w+)")).
         into(textView);
```
         
This results in:

<img src="http://i.imgur.com/IX1M1Qt.png" width="500" />

### Linkify Pattern with Text Color

Linkify all sub-strings within `TextView` that match a regular expression and then set color:

```java
new PatternEditableBuilder().
        addPattern(Pattern.compile("\\@(\\w+)"), Color.CYAN).
        into(textView);
```
        
This results in:

<img src="http://i.imgur.com/w8tsGyS.png" width="500" />

### Linkify Pattern with Color and Click

Linkify all sub-strings within `TextView` that match a pattern and then set color, and click handler:

```java
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);
 ```
       
This results in:

<img src="http://i.imgur.com/OHFMMTc.gif" width="500" />

### Linkify with Custom Span Style

Linkify all sub-strings within `TextView` that match a pattern and then set custom styles:

```java
new PatternEditableBuilder().
	addPattern(Pattern.compile("\\@(\\w+)"), 
	new PatternEditableBuilder.SpannableStyleListener() {
	    @Override
	    void onSpanStyled(TextPaint ds) {
	        // ds contains everything you need to style the span
	        ds.bgColor = Color.GRAY;
	        ds.linkColor = Color.MAGENTA;
	    }
	}).into(textView);
```

and this results in:

<img src="http://i.imgur.com/xsec2zf.png" width="500" />

### Complete Example

This is a more complete example which matches both usernames and hashtags:

```java
new PatternEditableBuilder().
	addPattern(Pattern.compile("\\@(\\w+)"), Color.GREEN, 
	new PatternEditableBuilder.SpannableClickedListener() {
	    @Override
	    public void onSpanClicked(String text) {
	        Toast.makeText(MainActivity.this, "Clicked username: " + text,
	                Toast.LENGTH_SHORT).show();
	    }
	}).
	addPattern(Pattern.compile("\\#(\\w+)"), Color.CYAN, 
	new PatternEditableBuilder.SpannableClickedListener() {
	    @Override
	    public void onSpanClicked(String text) {
	        Toast.makeText(MainActivity.this, "Clicked hashtag: " + text,
	                Toast.LENGTH_SHORT).show();
	    }
	}).into(textView);
```

and this results in:

<img src="http://i.imgur.com/BwXtSCe.gif" width="500" />

## Attribution

Created by [Nathan Esquenazi](https://gist.github.com/nesquena) from CodePath in 2016. 

## References

* <http://stackoverflow.com/questions/15851655/android-change-the-background-color-of-a-clickablespan-when-clicked>
* <http://stackoverflow.com/questions/20856105/change-the-text-color-of-a-single-clickablespan-when-pressed-without-affecting-o>
* <http://stackoverflow.com/questions/3282940/set-color-of-textview-span-in-android>
* <http://stackoverflow.com/questions/19750458/android-clickablespan-get-text-onclick>
* <http://stackoverflow.com/questions/5595785/highlight-on-clickablespan-click>
* <http://stackoverflow.com/questions/11903414/find-a-particular-regex-word-and-linkify-to-open-an-activity-in-android>
* <http://stackoverflow.com/questions/7570239/android-linkify-text-spannable-text-in-single-text-view-as-like-twitter-twee>
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/*
     Create clickable spans within a TextView
     made easy with pattern matching!

     Created by: Nathan Esquenazi

     Usage 1: Apply spannable strings to a TextView based on pattern

        new PatternEditableBuilder().
           addPattern(Pattern.compile("\\@(\\w+)")).
           into(textView);

     Usage 2: Apply clickable spans to a TextView

         new PatternEditableBuilder().
             addPattern(Pattern.compile("\\@(\\w+)"), Color.BLUE,
             new PatternEditableBuilder.SpannableClickedListener() {
                @Override
                public void onSpanClicked(String text) {
                    // Do something here
                }
             }).into(textView);

     See README for more details.
 */

public class PatternEditableBuilder {
    // Records the pattern spans to apply to a TextView
    ArrayList<SpannablePatternItem> patterns;

    /* This stores a particular pattern item
       complete with pattern, span styles, and click listener */
    public class SpannablePatternItem {
        public SpannablePatternItem(Pattern pattern, SpannableStyleListener styles, SpannableClickedListener listener) {
            this.pattern = pattern;
            this.styles = styles;
            this.listener = listener;
        }

        public SpannableStyleListener styles;
        public Pattern pattern;
        public SpannableClickedListener listener;
    }

    /* This stores the style listener for a pattern item
       Used to style a particular category of spans */
    public static abstract class SpannableStyleListener {
        public int spanTextColor;

        public SpannableStyleListener() {
        }

        public SpannableStyleListener(int spanTextColor) {
            this.spanTextColor = spanTextColor;
        }

        abstract void onSpanStyled(TextPaint ds);
    }

    /* This stores the click listener for a pattern item
       Used to handle clicks to a particular category of spans */
    public interface SpannableClickedListener {
        void onSpanClicked(String text);
    }

    /* This is the custom clickable span class used
       to handle user clicks to our pattern spans
       applying the styles and invoking click listener.
     */
    public class StyledClickableSpan extends ClickableSpan {
        SpannablePatternItem item;

        public StyledClickableSpan(SpannablePatternItem item) {
            this.item = item;
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            if (item.styles != null) {
                item.styles.onSpanStyled(ds);
            }
            super.updateDrawState(ds);
        }

        @Override
        public void onClick(View widget) {
            if (item.listener != null) {
                TextView tv = (TextView) widget;
                Spanned span = (Spanned) tv.getText();
                int start = span.getSpanStart(this);
                int end = span.getSpanEnd(this);
                CharSequence text = span.subSequence(start, end);
                item.listener.onSpanClicked(text.toString());
            }
            widget.invalidate();
        }
    }

    /* ----- Constructors ------- */
    public PatternEditableBuilder() {
        this.patterns = new ArrayList<>();
    }

    /* These are the `addPattern` overloaded signatures */
    // Each allows us to add a span pattern with different arguments
    public PatternEditableBuilder addPattern(Pattern pattern, SpannableStyleListener spanStyles, SpannableClickedListener listener) {
        patterns.add(new SpannablePatternItem(pattern, spanStyles, listener));
        return this;
    }

    public PatternEditableBuilder addPattern(Pattern pattern, SpannableStyleListener spanStyles) {
        addPattern(pattern, spanStyles, null);
        return this;
    }

    public PatternEditableBuilder addPattern(Pattern pattern) {
        addPattern(pattern, null, null);
        return this;
    }

    public PatternEditableBuilder addPattern(Pattern pattern, int textColor) {
        addPattern(pattern, textColor, null);
        return this;
    }

    public PatternEditableBuilder addPattern(Pattern pattern, int textColor, SpannableClickedListener listener) {
        SpannableStyleListener styles = new SpannableStyleListener(textColor) {
            @Override
            public void onSpanStyled(TextPaint ds) {
                ds.linkColor = this.spanTextColor;
            }
        };
        addPattern(pattern, styles, listener);
        return this;
    }

    public PatternEditableBuilder addPattern(Pattern pattern, SpannableClickedListener listener) {
        addPattern(pattern, null, listener);
        return this;
    }

    /* BUILDER METHODS */

    // This builds the pattern span and applies to a TextView
    public void into(TextView textView) {
        SpannableStringBuilder result = build(textView.getText());
        textView.setText(result);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
    }

    // This builds the pattern span into a `SpannableStringBuilder`
    // Requires a CharSequence to be passed in to be applied to
    public SpannableStringBuilder build(CharSequence editable) {
        SpannableStringBuilder ssb = new SpannableStringBuilder(editable);
        for (SpannablePatternItem item : patterns) {
            Matcher matcher = item.pattern.matcher(ssb);
            while (matcher.find()) {
                int start = matcher.start();
                int end = matcher.end();
                StyledClickableSpan url = new StyledClickableSpan(item);
                ssb.setSpan(url, start, end, 0);
            }
        }
        return ssb;
    }

}

以上是关于java PatternEditableBuilder - 在TextView中创建彩色可点击跨度的简便方法!的主要内容,如果未能解决你的问题,请参考以下文章

Java 布尔运算

java [Java] Java常用代码#java

Java - 35 Java 实例

Java While 循环

Java 字符串

Java If ... Else