`

高效快捷解决一个TextView显示多种字体的控件SpannableTextView

阅读更多
这个控件本人强烈推荐,它会使得布局非常的简单且高效;
下面这个布局如果是你,你会用多少层?多少控件生成?



告诉你吧,一个SpannableTextView控件就搞定了!

它把TextView和Spannable封装在了一起,可以在一个TextView中显示不同的字体颜色,大小,背景色等;
它支持如下样式:

* Babushka Method      Internal Span
*     textSize            AbsoluteSizeSpan
*     textColor           ForegroundColorSpan
*     textSizeRelative    RelativeSizeSpan
*     backgroundColor     BackgroundColorSpan
*     style               StyleSpan
*     underline           UnderlineSpan
*     strike              StrikethroughSpan
*     superscript         SuperscriptSpan
*     subscript           SubscriptSpan

用法也很简单:
/**
     * 为一个TextView设置多种字体(大小,颜色,背景色等)
     * 
     * @param tv
     * @param title
     * @param content
     */
    public void createSpannableTextView(SpannableTextView tv, String title, String content)
    {

        // clear pieces
        tv.reset();
        // Add the first piece
        tv.addPiece(new SpannableTextView.Piece.Builder(title).textColor(App.res.getColor(R.color.text_color_c2))
                .textSize((int) App.res.getDimension(R.dimen.font_xbig)).build());

        // Add the second piece
        tv.addPiece(new SpannableTextView.Piece.Builder(content).textColor(App.res.getColor(R.color.text_color_c8))
                .textSize((int) App.res.getDimension(R.dimen.font_middle)).build());

        // Display the final, styled text
        tv.display();
    }


SpannableTextView tv = null;
                tv = (SpannableTextView) (v.findViewById(R.id.spannableTextView0));
                context.createSpannableTextView(tv, "血糖\n", "记录血糖指数");
                tv = (SpannableTextView) (v.findViewById(R.id.spannableTextView1));
                context.createSpannableTextView(tv, "血压\n", "记录血压指数");
                tv = (SpannableTextView) (v.findViewById(R.id.spannableTextView2));
                context.createSpannableTextView(tv, "体重\n", "记录体重");
                tv = (SpannableTextView) (v.findViewById(R.id.spannableTextView3));
                context.createSpannableTextView(tv, "饮食\n", "记录日常饮食");
                tv = (SpannableTextView) (v.findViewById(R.id.spannableTextView4));
                context.createSpannableTextView(tv, "运动\n", "记录运动时间");


<cn.tangdada.tangbang.widget.SpannableTextView
                android:id="@+id/spannableTextView0"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center_vertical"
                android:background="@drawable/line_bottom"
                android:drawableRight="@drawable/arrow_right"
                android:drawableLeft="@drawable/icon_0"
                android:gravity="center_vertical"
                android:lineSpacingExtra="4dp"
                android:paddingRight="16dp"
                android:singleLine="false" />


源码:
package cn.tangdada.tangbang.widget;

/*
 * Copyright (C) 2014 Henrique Boregio.
 *
 * 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.
 *
 * @author Henrique Boregio (hboregio@gmail.com)
 */

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

import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
import android.text.style.UnderlineSpan;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * usage:
 * 
 * <pre>
 * SpannableTextView tv = (SpannableTextView) findViewById(R.id.spannable_textview);
 * 
 * // Add the first piece &quot;Central Park&quot;
 * tv.addPiece(new SpannableTextView.Piece.Builder(&quot;Central Park, NY\n&quot;).textColor(Color.parseColor(&quot;#414141&quot;)).build());
 * 
 * // Add the second piece &quot;1.2 mi&quot;
 * tv.addPiece(new SpannableTextView.Piece.Builder(&quot;1.2 mi &quot;).textColor(Color.parseColor(&quot;#0081E2&quot;)).textSizeRelative(0.9f).build());
 * 
 * // Add the third piece &quot;from here&quot;
 * tv.addPiece(new SpannableTextView.Piece.Builder(&quot;from here&quot;).textColor(Color.parseColor(&quot;#969696&quot;)).textSizeRelative(0.9f).build());
 * 
 * // Display the final, styled text
 * tv.display();
 * </pre>
 * 
 * <pre>
 * // grab the Piece at position 1
 * Piece piece = babushka.getPiece(1);
 * 
 * // modify it's text
 * piece.setText(&quot;1.9 km &quot;);
 * 
 * // you must always call display after you alter a Piece's text
 * tv.display();
 * </pre>
 * 
 * <pre>
 * Babushka Method      Internal Span
 *     textSize            AbsoluteSizeSpan
 *     textColor           ForegroundColorSpan
 *     textSizeRelative    RelativeSizeSpan
 *     backgroundColor     BackgroundColorSpan
 *     style               StyleSpan
 *     underline           UnderlineSpan
 *     strike              StrikethroughSpan
 *     superscript         SuperscriptSpan
 *     subscript           SubscriptSpan
 * </pre>
 * 
 * BabushkaText is a TextView which lets you customize the styling of parts of your text via Spannables, but without the
 * hassle of having to deal directly with Spannable themselves.
 * 
 * The idea behind a BabushkaText is that it is made up of {@code Piece}s. Each Piece represents a section of the final
 * text displayed by this TextView, and each Piece may be styled independently from the other Pieces. When you put it
 * all together, the final results is still a a single TextView, but with a a very different graphic output.
 * 
 * 
 * https://github.com/quiqueqs/BabushkaText
 */
public class SpannableTextView extends TextView
{

    // some default params
    private static int DEFAULT_ABSOLUTE_TEXT_SIZE;

    private static float DEFAULT_RELATIVE_TEXT_SIZE = 1;

    private List<Piece> mPieces;

    /**
     * Create a new instance of a this class
     * 
     * @param context
     */
    public SpannableTextView(Context context)
    {
        super(context);
        init();
    }

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

    public SpannableTextView(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init()
    {
        mPieces = new ArrayList<Piece>();
        SpannableTextView.DEFAULT_ABSOLUTE_TEXT_SIZE = (int) getTextSize();
    }

    /**
     * Use this method to add a {@link SpannableTextView.BabushkaText.Piece} to a BabushkaText. Each
     * {@link SpannableTextView.BabushkaText.Piece } is added sequentially, so the order you call this method matters.
     * 
     * @param aPiece the Piece
     */
    public void addPiece(Piece aPiece)
    {
        mPieces.add(aPiece);
    }

    /**
     * Adds a Piece at this specific location. The underlying data structure is a {@link java.util.List}, so expect the
     * same type of behaviour.
     * 
     * @param aPiece the Piece to add.
     * @param location the index at which to add.
     */
    public void addPiece(Piece aPiece, int location)
    {
        mPieces.add(location, aPiece);
    }

    /**
     * Replaces the Piece at the specified location with this new Piece. The underlying data structure is a
     * {@link java.util.List}, so expect the same type of behaviour.
     * 
     * @param newPiece the Piece to insert.
     * @param location the index at which to insert.
     */
    public void replacePieceAt(int location, Piece newPiece)
    {
        mPieces.set(location, newPiece);
    }

    /**
     * Removes the Piece at this specified location. The underlying data structure is a {@link java.util.List}, so
     * expect the same type of behaviour.
     * 
     * @param location the index of the Piece to remove
     */
    public void removePiece(int location)
    {
        mPieces.remove(location);
    }

    /**
     * Clear all the Pieces, same as reset()
     */
    public void clearPiece()
    {
        mPieces.clear();
    }

    /**
     * Get a specific {@link SpannableTextView.BabushkaText.Piece} in position index.
     * 
     * @param location position of Piece (0 based)
     * @return Piece o null if invalid index
     */
    public Piece getPiece(int location)
    {
        if (location >= 0 && location < mPieces.size())
        {
            return mPieces.get(location);
        }

        return null;
    }

    /**
     * Call this method when you're done adding {@link SpannableTextView.BabushkaText.Piece}s and want this TextView to
     * display the final, styled version of it's String contents.
     * 
     * You MUST also call this method whenever you make a modification to the text of a Piece that has already been
     * displayed.
     */
    public void display()
    {

        // generate the final string based on the pieces
        StringBuilder builder = new StringBuilder();
        for (Piece aPiece : mPieces)
        {
            builder.append(aPiece.text);
        }

        // apply spans
        int cursor = 0;
        SpannableString finalString = new SpannableString(builder.toString());
        for (Piece aPiece : mPieces)
        {
            applySpannablesTo(aPiece, finalString, cursor, cursor + aPiece.text.length());
            cursor += aPiece.text.length();
        }

        // set the styled text
        setText(finalString);
    }

    private void applySpannablesTo(Piece aPiece, SpannableString finalString, int start, int end)
    {

        if (aPiece.subscript)
        {
            finalString.setSpan(new SubscriptSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        if (aPiece.superscript)
        {
            finalString.setSpan(new SuperscriptSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        if (aPiece.strike)
        {
            finalString.setSpan(new StrikethroughSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        if (aPiece.underline)
        {
            finalString.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        // style
        finalString.setSpan(new StyleSpan(aPiece.style), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        // absolute text size
        finalString.setSpan(new AbsoluteSizeSpan(aPiece.textSize), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        // relative text size
        finalString.setSpan(new RelativeSizeSpan(aPiece.textSizeRelative), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        // text color
        finalString.setSpan(new ForegroundColorSpan(aPiece.textColor), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        // background color
        if (aPiece.backgroundColor != -1)
        {
            finalString.setSpan(new BackgroundColorSpan(aPiece.backgroundColor), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }

    /**
     * Resets the styling of this view and sets it's content to an empty String.
     */
    public void reset()
    {
        mPieces = new ArrayList<Piece>();
        setText("");
    }

    /**
     * Change text color of all pieces of textview.
     */
    public void changeTextColor(int textColor)
    {
        for (Piece mPiece : mPieces)
        {
            mPiece.setTextColor(textColor);
        }
        display();
    }

    /**
     * A Piece represents a part of the text that you want to style. Say for example you want this BabushkaText to
     * display "Hello World" such that "Hello" is displayed in Bold and "World" is displayed in Italics. Since these
     * have different styles, they are both separate Pieces.
     * 
     * You create a Piece by using it's {@link SpannableTextView.BabushkaText.Piece.Builder}
     * 
     */
    public static class Piece
    {

        private String text;

        private int textColor;

        private final int textSize;

        private final int backgroundColor;

        private final float textSizeRelative;

        private final int style;

        private final boolean underline;

        private final boolean superscript;

        private final boolean strike;

        private final boolean subscript;

        public Piece(Builder builder)
        {
            this.text = builder.text;
            this.textSize = builder.textSize;
            this.textColor = builder.textColor;
            this.backgroundColor = builder.backgroundColor;
            this.textSizeRelative = builder.textSizeRelative;
            this.style = builder.style;
            this.underline = builder.underline;
            this.superscript = builder.superscript;
            this.subscript = builder.subscript;
            this.strike = builder.strike;
        }

        /**
         * Sets the text of this Piece. If you're creating a new Piece, you should do so using it's
         * {@link SpannableTextView.BabushkaText.Piece.Builder}.
         * 
         * Use this method if you want to modify the text of an existing Piece that is already displayed. After doing
         * so, you MUST call {@code display()} for the changes to show up.
         * 
         * @param text the text to display
         */
        public void setText(String text)
        {
            this.text = text;
        }

        /**
         * Sets the text color of this Piece. If you're creating a new Piece, you should do so using it's
         * {@link SpannableTextView.BabushkaText.Piece.Builder}.
         * 
         * Use this method if you want to change the text color of an existing Piece that is already displayed. After
         * doing so, you MUST call {@code display()} for the changes to show up.
         * 
         * @param color of text (it is NOT android Color resources ID, use getResources().getColor(R.color.colorId) for
         *            it)
         */
        public void setTextColor(int textColor)
        {
            this.textColor = textColor;
        }

        /**
         * Builder of Pieces
         */
        public static class Builder
        {

            // required
            private final String text;

            // optional
            private int textSize = DEFAULT_ABSOLUTE_TEXT_SIZE;

            private int textColor = Color.BLACK;

            private int backgroundColor = -1;

            private float textSizeRelative = DEFAULT_RELATIVE_TEXT_SIZE;

            private int style = Typeface.NORMAL;

            private boolean underline = false;

            private boolean strike = false;

            private boolean superscript = false;

            private boolean subscript = false;

            /**
             * Creates a new Builder for this Piece.
             * 
             * @param text the text of this Piece
             */
            public Builder(String text)
            {
                this.text = text;
            }

            /**
             * Sets the absolute text size.
             * 
             * @param textSize text size in pixels
             * @return a Builder
             */
            public Builder textSize(int textSize)
            {
                this.textSize = textSize;
                return this;
            }

            /**
             * Sets the text color.
             * 
             * @param textColor the color
             * @return a Builder
             */
            public Builder textColor(int textColor)
            {
                this.textColor = textColor;
                return this;
            }

            /**
             * Sets the background color.
             * 
             * @param backgroundColor the color
             * @return a Builder
             */
            public Builder backgroundColor(int backgroundColor)
            {
                this.backgroundColor = backgroundColor;
                return this;
            }

            /**
             * Sets the relative text size.
             * 
             * @param textSizeRelative relative text size
             * @return a Builder
             */
            public Builder textSizeRelative(float textSizeRelative)
            {
                this.textSizeRelative = textSizeRelative;
                return this;
            }

            /**
             * Sets a style to this Piece.
             * 
             * @param style see {@link android.graphics.Typeface}
             * @return a Builder
             */
            public Builder style(int style)
            {
                this.style = style;
                return this;
            }

            /**
             * Underlines this Piece.
             * 
             * @return a Builder
             */
            public Builder underline()
            {
                this.underline = true;
                return this;
            }

            /**
             * Strikes this Piece.
             * 
             * @return a Builder
             */
            public Builder strike()
            {
                this.strike = true;
                return this;
            }

            /**
             * Sets this Piece as a superscript.
             * 
             * @return a Builder
             */
            public Builder superscript()
            {
                this.superscript = true;
                return this;
            }

            /**
             * Sets this Piece as a subscript.
             * 
             * @return a Builder
             */
            public Builder subscript()
            {
                this.subscript = true;
                return this;
            }

            /**
             * Creates a {@link SpannableTextView.BabushkaText.Piece} with the customized parameters.
             * 
             * @return a Piece
             */
            public Piece build()
            {
                return new Piece(this);
            }
        }
    }

}


试着结合这个类Phrase.java那就更爽了;
ColorPhrase实现处理带颜色的字符串
https://github.com/THEONE10211024/ColorPhrase

https://github.com/quiqueqs/BabushkaText

Spanny实现字符串样式处理
https://github.com/binaryfork/Spanny
  • 大小: 8.6 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics