Android Open Source - android-bubble-text Chips Text View






From Project

Back to project page android-bubble-text.

License

The source code is released under:

MIT License

If you think the Android project android-bubble-text listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.oliveira.bubble;
//  w  ww . ja va2 s  .  c om
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.text.*;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

@SuppressWarnings("unused")
public class ChipsTextView extends View implements ILayoutCallback {

    public static boolean DEBUG;

    protected BubbleSpan selectedSpan;
    protected ArrayList<BubbleSpan> spans = new ArrayList<BubbleSpan>();
    protected HashMap<BubbleSpan, ArrayList<Rect>> positions = new HashMap<BubbleSpan, ArrayList<Rect>>();
    protected Spannable text;
    protected Spannable moreText;
    protected TextPaint textPaint;
    protected StaticLayout truncatedLayout;
    protected StaticLayout expandedLayout;
    protected boolean truncated;
    protected boolean animating;
    protected OnBubbleClickedListener listener;
    protected float lineSpacing = 1.25f;
    protected int maxLines = 0;

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

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

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

    public StaticLayout layout() {
        return truncated ? truncatedLayout : expandedLayout;
    }

    @SuppressWarnings("NullableProblems")
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();

        x += getScrollX();
        y += getScrollY();

        boolean retValue = false;

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                retValue = selectBubble(x, y);
                break;
            case MotionEvent.ACTION_MOVE:
                if (selectedSpan != null) {
                    selectedSpan.setPressed(spanContains(selectedSpan, x, y), (Spannable) layout().getText());
                    retValue = true;
                }
                break;
            case MotionEvent.ACTION_UP:
                if (listener != null && selectedSpan != null && spanContains(selectedSpan, x, y)) {
                    listener.onBubbleClicked(this, selectedSpan);
                }

            case MotionEvent.ACTION_CANCEL:
                if (selectedSpan != null) {
                    retValue = true;
                    if ( -1 != ((Spannable)truncatedLayout.getText()).getSpanEnd(selectedSpan)) {
                        selectedSpan.setPressed(false, (Spannable)truncatedLayout.getText());
                    }
                    if ( -1 != ((Spannable)expandedLayout.getText()).getSpanEnd(selectedSpan)) {
                        selectedSpan.setPressed(false, (Spannable)expandedLayout.getText());
                    }
                }
                selectedSpan = null;
                break;

            default:
                break;
        }

        invalidate();
        return retValue;
    }

    private boolean spanContains(BubbleSpan span, int x, int y) {

        ArrayList<Rect> rects = positions.get(span);
        if (rects != null) {
            for (Rect rect : rects) {
                if (rect.contains(x, y)) {
                    return true;
                }
            }
        }

        return false;
    }

    public boolean selectBubble(int x, int y) {
        for (BubbleSpan span : spans) {
            if (spanContains(span, x, y)) {
                span.setPressed(true, (Spannable)layout().getText());
                selectedSpan = span;
                return true;
            }
        }
        return false;
    }

    public void setTextPaint(TextPaint textPaint) {
        this.textPaint = textPaint;
    }

    public void setText(Spannable text) {
        this.text = text;
        if (getWidth() > 0) {
            requestLayout();
        }
    }

    private void recomputeSpans(Spannable text) {

        spans.clear();

        Collections.addAll(
                spans,
                text.getSpans(0, text.length(), BubbleSpan.class));
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (layout() == null) {
            return;
        }

        canvas.translate(getPaddingLeft(), getPaddingTop());
        layout().draw(canvas);

        if (DEBUG) {

            int n = layout().getLineCount();
            for (int i = 0; i < n; i++) {

                Paint paint = new Paint();
                paint.setColor(Color.RED);
                paint.setStyle(Paint.Style.STROKE);

                Rect bounds = new Rect();
                layout().getLineBounds(i, bounds);
                canvas.drawRect(bounds, paint);

                int baseLine = layout().getLineBaseline(i);
                drawLine(canvas, bounds, paint, baseLine, Color.GREEN);
                drawLine(canvas, bounds, paint, baseLine + layout().getPaint().getFontMetricsInt().descent, Color.BLUE);
            }
        }
    }

    private void drawLine(Canvas canvas, Rect line, Paint paint, int height, int color) {
        paint.setColor(color);
        canvas.drawLine(line.left, height, line.right, height, paint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = widthSize;
        int height = heightSize;

        if (!animating) {
            build(width);
            height = layout() == null ? 0 : layout().getHeight() + getPaddingTop() + getPaddingBottom();

            if (layout() != null && layout().getLineCount() > 0) {

                height -= (layout().getLineBottom(0) - layout().getPaint().getFontMetricsInt().descent - layout().getLineBaseline(0));

                if (layout().getLineCount() == 1 && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
                    width = (int) (layout().getLineWidth(0) + getPaddingLeft() + getPaddingRight());
                }
            }
        }

        this.setMeasuredDimension(width, height);
    }

    private void build(int width) {

        positions.clear();
        width = width - getPaddingLeft() - getPaddingRight();

        if (width <= 0 || TextUtils.isEmpty(text)) {
            truncatedLayout = expandedLayout = null;
            return;
        }

        for (BubbleSpan span : spans) {
            span.resetWidth(width);
        }

        try {

            truncatedLayout = expandedLayout = new StaticLayout(text,
                    textPaint,
                    width,
                    Layout.Alignment.ALIGN_NORMAL,
                    lineSpacing,
                    1,
                    false);

            if (maxLines > 0 && truncatedLayout.getLineCount() > maxLines) {

                int lineEnd = truncatedLayout.getLineEnd(maxLines - 1);
                int offset = -1;
                StaticLayout sl = new StaticLayout(moreText,
                        textPaint,
                        width,
                        Layout.Alignment.ALIGN_NORMAL,
                        lineSpacing,
                        1,
                        false);

                sl.getWidth();
                while (truncatedLayout.getLineCount() > maxLines && lineEnd > 0) {

                    if (offset == -1 && truncatedLayout.getLineWidth(maxLines - 1) + sl.getLineWidth(0) > width) {

                        offset = truncatedLayout.getOffsetForHorizontal(
                                maxLines - 1,
                                width - sl.getLineWidth(0));

                        lineEnd = offset;

                    } else if (offset > 0) {
                        lineEnd--;
                    }

                    SpannableStringBuilder textTruncated = new SpannableStringBuilder(text.subSequence(0, lineEnd));
                    textTruncated.append(moreText);

                    truncatedLayout = new StaticLayout(textTruncated,
                            textPaint,
                            width,
                            Layout.Alignment.ALIGN_NORMAL,
                            lineSpacing,
                            1,
                            false);
                }
            }
        } catch (java.lang.ArrayIndexOutOfBoundsException e) {
            return;
        }

        if (truncated) {
            recomputeSpans((Spannable)truncatedLayout.getText());
        } else {
            recomputeSpans((Spannable)expandedLayout.getText());
        }

        for (BubbleSpan span : spans) {
            positions.put(span, span.rect(this));
        }
    }

    @Override
    public Point getCursorPosition(int pos) {
        int line = layout().getLineForOffset(pos);
        int baseline = layout().getLineBaseline(line);
        int ascent = layout().getLineAscent(line);
        float x = layout().getPrimaryHorizontal(pos);
        float y = baseline + ascent;
        return new Point((int)x+getPaddingLeft(), (int)y+getPaddingTop());
    }

    @Override
    public int getLine(int pos) {
        return layout().getLineForOffset(pos);
    }

    @Override
    public Spannable getSpannable() {
        return ((Spannable)layout().getText());
    }

    @Override
    public int getLineEnd(int line) {
        return layout().getLineEnd(line);
    }

    @Override
    public int getLineHeight() {
        return layout().getLineBottom(0);
    }

    public interface OnBubbleClickedListener {
        public void onBubbleClicked(View view, BubbleSpan bubbleSpan);
    }

    public void setOnBubbleClickedListener(OnBubbleClickedListener listener) {
        this.listener = listener;
    }

    public void setLineSpacing(float value) {
        lineSpacing = value;
    }

    public int getTextSize() {
        return (int)textPaint.getTextSize();
    }

    public void setMaxLines(int maxLines, Spannable moreText) {
        truncated = true;
        this.moreText = moreText;
        this.maxLines = maxLines;
    }

    public void expand(boolean animate) {

        if (!truncated) {
            return;
        }

        truncated = false;
        if (!animate) {
            requestLayout();
            return;
        }

        ResizeAnimation expandAnimation = new ResizeAnimation(
                getHeight() + expandedLayout.getHeight() - truncatedLayout.getHeight()
        );

        expandAnimation.setDuration(400);
        expandAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override public void onAnimationStart(Animation animation) { animating = true; }
            @Override public void onAnimationEnd(Animation animation) { animating = false; }
            @Override public void onAnimationRepeat(Animation animation) {}
        });

        startAnimation(expandAnimation);
    }

    public void setTruncated(boolean truncated) {
        this.truncated = truncated;
    }

    public class ResizeAnimation extends Animation {
        final int startHeight;
        final int targetHeight;

        public ResizeAnimation(int targetHeight) {
            this.targetHeight = targetHeight;
            startHeight = getHeight();
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            getLayoutParams().height = (int) (startHeight + (targetHeight - startHeight) * interpolatedTime);
            requestLayout();
        }

        @Override
        public void initialize(int width, int height, int parentWidth, int parentHeight) {
            super.initialize(width, height, parentWidth, parentHeight);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }
}




Java Source Code List

com.asolutions.widget.RowLayout.java
com.oliveira.bubble.AutoCompleteManager.java
com.oliveira.bubble.AutoCompletePopover.java
com.oliveira.bubble.AwesomeBubble.java
com.oliveira.bubble.BubbleSpanImpl.java
com.oliveira.bubble.BubbleSpan.java
com.oliveira.bubble.BubbleStyle.java
com.oliveira.bubble.ChipsEditText.java
com.oliveira.bubble.ChipsTextView.java
com.oliveira.bubble.CursorDrawable.java
com.oliveira.bubble.DefaultBubbles.java
com.oliveira.bubble.ILayoutCallback.java
com.oliveira.bubble.Linkify.java
com.oliveira.bubble.MultilineEditText.java
com.oliveira.bubble.Regex.java
com.oliveira.bubble.TappableSpan.java
com.oliveira.bubble.Utils.java