android如何写一个循环文字滚动的TextView

我们来看这样一个Text("如何在TextView中插入表情符号如(#f1),邮件youjiancau@sina.com,电话号码18559298168呢,默认的TextView是支持不了这个特性的"),我们如何把里面的表情标签,电话号码等提取出来的,这个我可以用正则表达式可以实现:

private static Pattern EMAIL_PATTERN = Patterns.EMAIL_ADDRESS;
private static Pattern PHONE_PATTERN = Patterns.PHONE;
private static Pattern WEBURL_PATTERN = Patterns.WEB_URL;

public static ArrayList<LinkInfo> parseStr(String strLink) {
   if(TextUtils.isEmpty(strLink)){
          return null;
       }
   ArrayList<LinkInfo> resultList = new ArrayList<LinkInfo>();
       ArrayList<LinkInfo> infoList = null;
       try{
          infoList = new ArrayList<LinkInfo>();
          //把所有的email标签都取出来
          Matcher matcher = EMAIL_PATTERN.matcher(strLink);
          int begin = 0;
          while(matcher.find()) {
             int start = matcher.start();
             int end = matcher.end();
              LinkInfo info = new LinkInfo();
              info.setStartIndex(start);
              info.setEndIndex(end);
               info.setContent(matcher.group());
               info.setType(LinkInfo.EMAIL);
              infoList.add(info);
          }

      //把所有的phone标签都取出来
          Matcher matcher1 = PHONE_PATTERN.matcher(strLink);
          while(matcher1.find()) {
             int start = matcher1.start();
             int end = matcher1.end();
              LinkInfo info = new LinkInfo();
              info.setStartIndex(start);
              info.setEndIndex(end);
               info.setContent(matcher1.group());
               info.setType(LinkInfo.PHONENUMBER);
              infoList.add(info);
          }

      //把所有的表情标签都取出来
          Pattern pattern = Pattern.compile("(\\(#\\S{1,2}\\))");
           Matcher matcher2 = pattern.matcher(strLink);
           while(matcher2.find()) {
             int start = matcher2.start();
             int end = matcher2.end();
              LinkInfo info = new LinkInfo();
              info.setStartIndex(start);
              info.setEndIndex(end);
               info.setContent(matcher2.group());
               info.setFace(true);
              infoList.add(info);
          }
          
          //对infoList进行排序
          Collections.sort(infoList);
          int last = 0;
          //按照文本的顺序重新组装成一个resultList
          for(int i=0;i<infoList.size();i++) {
             LinkInfo info = infoList.get(i);
             if(begin != info.getStartIndex()){
                LinkInfo infoBefore = new LinkInfo();
                 infoBefore.setContent(strLink.substring(begin,info.getStartIndex()));
                 resultList.add(infoBefore);
             }
             resultList.add(info);
             begin = info.getEndIndex();
             last = info.getEndIndex();
          }
          if(last < strLink.length()) {
             LinkInfo info = new LinkInfo();
              info.setContent(strLink.substring(last,strLink.length()));
              resultList.add(info);
          }
          
          
       }catch(Exception ex){
          ex.printStackTrace();
       }
       return resultList;
}

解析出来后我们就要把这个结果放到view中展示,对于一个自定义view,我们需要重写onMeasure和onDraw,onDraw的流程图如下:

https://img1.mukewang.com/5c088ddc000136e203530568.jpg

这个View的处理数据源是ArrayList<LinkInfo>

在onMeasure中,首先我们看如何去measure 表情的宽度

if (info.isFace()) {
    Bitmap faceBmp = null;
    if (mFaceType == MSG_FACE_TYPE) {
        faceBmp = StateFaceModel.getInstance()
                .getSmallFaceIcon(info.getContent());                        }
    if (faceBmp != null) {
        int wSize = faceBmp.getWidth() + 4;
        if (width + wSize >= maxWidth) {
            width = maxWidth;
            break forLable;
        }
        width += wSize;
    }
    continue;
}

measure 普通文本的宽度:

String text = info.getContent();
if (!TextUtils.isEmpty(text)) {
    float wSize = tempPaint.measureText(text);
    if (width + wSize >= maxWidth) {
        width = maxWidth;
        break forLable;
    }
    width += wSize;
}

onDraw表情:

LinkInfo info = infoList.get(i);
//如果LinkInfo为表情,那么我们需要用canvas.drawBitmap的方式去绘制
if (info.isFace()) {
    Bitmap faceBmp = null;
    if (mFaceType == MSG_FACE_TYPE) {
        faceBmp = StateFaceModel.getInstance()
                .getSmallFaceIcon(info.getContent());
    }
    if (faceBmp != null) {
        int xLen = faceBmp.getWidth() + 4;
        if (curLen + xLen >= displayWidth) {
            y += fontHeight;
            curLen = 0;
        }
        canvas.drawBitmap(faceBmp, curLen + 2, y - fontHeight + 4, paint);
        curLen += xLen;
    }
    continue;
}

onDraw中phone,email:

private void paintSelectRectF(Canvas canvas, Paint paint, LinkInfo info) {
    ArrayList<RectF> rectList = info.getRectFList();
    int len = rectList.size();
    for (int i = 0; i < len; i++) {
        RectF rectF = rectList.get(i);
        paint.setColor(this.getContext().getResources().getColor(R.drawable.gray3));
        canvas.drawRect(rectF.left, rectF.top - 2, rectF.right, rectF.bottom - 2, paint);
    }
}

完成的view的代码是:

public class IntroView extends TextView {

    private ArrayList<LinkInfo> titleList;
    private int displayWidth = 0;
    private float displayHeight = 0;
    private float curLen = 0;
    private Bitmap starBmp;
    private Bitmap selectedBmp;
    private float posX = 0;
    private float posY = 0;
    private LinkInfo curInfo;
    private OnClickLinkListener Listener;

    public void setOnClickLinkListener(OnClickLinkListener listener) {
        this.Listener = listener;
    }

    public interface OnClickLinkListener {
        public abstract void onClick(LinkInfo linkInfo);
    }

    private String mFaceType = MSG_FACE_TYPE;
    public static final String MSG_FACE_TYPE = "msgtype";
    public static final String STATUS_FACE_TYPE = "statustype";

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

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

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


    public void setTitleList(ArrayList<LinkInfo> titleList) {
        this.titleList = titleList;
        displayHeight = 0;
        requestLayout();
    }


    public void setDisplayWidth(int width) {
    }

    public void setSelectedBmp(Bitmap selectedBmp) {
        this.selectedBmp = selectedBmp;
    }

    public void setStarBmp(Bitmap starBmp) {
        this.starBmp = starBmp;
    }


    public void setFaceType(String faceType) {
        this.mFaceType = faceType;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        try {
            if (titleList == null || titleList.size() == 0) {
                return;
            }
            curLen = 0;
            Paint paint = new Paint();
            paint.setTextSize(getTextSize());
            paint.setAntiAlias(true);
            paint.setStyle(Style.FILL);
            float y = -paint.ascent();
            y = drawList(canvas, paint, titleList, y);
        } catch (Exception ex) {

        }
    }

    private float drawList(Canvas canvas, Paint paint, ArrayList<LinkInfo> infoList, float StartY) {
        if (infoList == null || infoList.size() == 0) {
            return StartY;
        }
        float y = StartY;
        float fontHeight = -paint.ascent() + paint.descent();
        int len = infoList.size();
        int color = getCurrentTextColor();
        for (int i = 0; i < len; i++) {
            LinkInfo info = infoList.get(i);
            //如果LinkInfo为表情,那么我们需要用canvas.drawBitmap的方式去绘制
            if (info.isFace()) {
                Bitmap faceBmp = null;
                if (mFaceType == MSG_FACE_TYPE) {
                    faceBmp = StateFaceModel.getInstance()
                            .getSmallFaceIcon(info.getContent());
                }
                if (faceBmp != null) {
                    int xLen = faceBmp.getWidth() + 4;
                    if (curLen + xLen >= displayWidth) {
                        y += fontHeight;
                        curLen = 0;
                    }
                    canvas.drawBitmap(faceBmp, curLen + 2, y - fontHeight + 4, paint);
                    curLen += xLen;
                }
                continue;
            }
            String strContent = info.getContent();

            if (mFaceType == MSG_FACE_TYPE && strContent.startsWith("\n")) {
                y += fontHeight;
                curLen = 0;
            }

            strContent = strContent.replaceAll("\n", " ");
            if ((info.isEmail() || info.isPhoneNumber() || info.isWebUrl()) && info.isSelected()) {
                paintSelectRectF(canvas, paint, info);
            }
            float xLen = paint.measureText(strContent);
            int starLen = 0;
            if (info.isCommonString()) {
                paint.setColor(color);
            } else {
                paint.setColor(getResources().getColor(R.drawable.blue1));
            }

            //如果只是普通文字,那么只要canvas.drawText就行
            if (curLen + xLen + starLen >= displayWidth) {
                int lenStr = strContent.length();
                for (int j = 0; j < lenStr; j++) {
                    float width = paint.measureText(strContent, j, j + 1);
                    if (curLen + width >= displayWidth) {
                        y += fontHeight;
                        curLen = 0;
                    }
                    canvas.drawText(strContent, j, j + 1, curLen, y, paint);
                    curLen += width;
                }
            } else {
                canvas.drawText(strContent, curLen, y, paint);
                curLen += xLen;
            }
        }
        return y;
    }


    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        Paint paint = new Paint();
        paint.setTextSize(getTextSize());
        paint.setAntiAlias(true);
        paint.setStyle(Style.FILL);
        displayHeight = -paint.ascent() + paint.descent() + 2;//fontMetrics.bottom - fontMetrics.top + 4;
        measureList(paint, titleList, 0);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min((int) displayHeight, specSize);
            } else {
                result = (int) displayHeight;
            }
        }
        return result;
    }

    private float measureList(Paint paint, ArrayList<LinkInfo> infoList, float StartX) {
        if (infoList == null || infoList.size() == 0) {
            displayHeight = StartX;
            return StartX;
        }
        float x = StartX;
        int len = infoList.size();
        float fontHeight = -paint.ascent() + paint.descent();
        for (int i = 0; i < len; i++) {
            LinkInfo info = infoList.get(i);

            info.getRectFList().clear();

            if (info.isFace()) {
                Bitmap faceBmp = null;
                if (mFaceType == MSG_FACE_TYPE) {
                    faceBmp = StateFaceModel.getInstance()
                            .getSmallFaceIcon(info.getContent());                }
                if (faceBmp != null) {
                    int xLen = faceBmp.getWidth() + 4;
                    if (x + xLen >= displayWidth) {
                        displayHeight += fontHeight;
                        x = 0;
                    }
                    x += xLen;
                }
                continue;
            }
            String strContent = info.getContent();
            strContent = strContent.replaceAll("\n", " ");

            float xLen = paint.measureText(strContent);
            if (x + xLen >= displayWidth) {
                float startX = x;
                int lenStr = strContent.length();
                for (int j = 0; j < lenStr; j++) {
                    float width = paint.measureText(strContent, j, j + 1);
                    if (x + width >= displayWidth) {
                        RectF rectF = new RectF();
                        rectF.set(startX, displayHeight - fontHeight, x, displayHeight);
                        info.addRectF(rectF);
                        displayHeight += fontHeight;
                        x = width;
                        startX = 0;
                    } else {
                        x += width;
                    }
                }
                RectF rectF = new RectF();
                rectF.set(startX, displayHeight - fontHeight, x - startX, displayHeight);
                info.addRectF(rectF);
            } else {
                RectF rectF = new RectF();
                rectF.set(x, displayHeight - fontHeight, x + xLen, displayHeight);
                info.addRectF(rectF);
                x += xLen;
            }
        }
        return x;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        if (!this.isEnabled() || Listener == null) {
            return false;
        }
        if (event.getAction() == MotionEvent.ACTION_UP) {
            float x = event.getX();
            float y = event.getY();
            boolean flag = cancelCurInfo(x, y);
            if (flag) {
                return false;
            }
            if (clickLink(titleList, x, y, MotionEvent.ACTION_UP)) {
                return true;
            }
        } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
            float x = event.getX();
            float y = event.getY();
            boolean flag = clickLink(titleList, x, y, MotionEvent.ACTION_DOWN);
            if (flag) {
                return true;
            }
        } else {
            float x = event.getX();
            float y = event.getY();
            cancelCurInfo(x, y);
        }
        return false;
    }

    /**
     * 如果是phone或是email的链接,那么我们使用canvas.drawRect的方式进行绘制
     * @param canvas
     * @param paint
     * @param info
     */
    private void paintSelectRectF(Canvas canvas, Paint paint, LinkInfo info) {
        ArrayList<RectF> rectList = info.getRectFList();
        int len = rectList.size();
        for (int i = 0; i < len; i++) {
            RectF rectF = rectList.get(i);
            paint.setColor(this.getContext().getResources().getColor(R.drawable.gray3));
            canvas.drawRect(rectF.left, rectF.top - 2, rectF.right, rectF.bottom - 2, paint);
        }
    }

    private boolean cancelCurInfo(float x, float y) {
        if (curInfo == null) {
            return true;
        }
        if (!curInfo.contains(x, y)) {
            curInfo.setSelected(false);
            this.invalidate();
            curInfo = null;
            return true;
        }
        return false;
    }

    private boolean clickLink(ArrayList<LinkInfo> infoList, float x, float y, int action) {
        if (infoList == null) {
            return false;
        }
        int len = infoList.size();

        for (int i = 0; i < len; i++) {
            LinkInfo info = infoList.get(i);
            if (info.isCommonString()) {
                continue;
            }
            if (info.contains(x, y)) {
                if (action == MotionEvent.ACTION_DOWN) {
                    info.setSelected(true);
                    this.invalidate();
                    this.curInfo = info;
                } else if (action == MotionEvent.ACTION_UP) {
                    this.curInfo = null;
                    info.setSelected(false);
                    this.invalidate();
                    Listener.onClick(info);
                }
                return true;
            }
        }
        return false;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        try {
            int width = measureWidth(widthMeasureSpec);
            int height = measureHeight(heightMeasureSpec);
            setMeasuredDimension(width, height);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Determines the width of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result = 0;

        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int initialWidth = getPaddingLeft() + getPaddingRight();
        int width = initialWidth;
        int maxWidth = 0;

        TextPaint tempPaint = null;

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            if (tempPaint == null) {
                tempPaint = new TextPaint();
                tempPaint.setStyle(Style.FILL);
                tempPaint.setAntiAlias(true);
                tempPaint.setTextSize(getTextSize());
            }

            if (titleList != null && titleList.size() > 0) {
                maxWidth = specSize;

                int size = titleList.size();
                forLable:
                for (int i = 0; i < size; i++) {
                    LinkInfo info = titleList.get(i);

                    if (info.isFace()) {
                        Bitmap faceBmp = null;
                        if (mFaceType == MSG_FACE_TYPE) {
                            faceBmp = StateFaceModel.getInstance()
                                    .getSmallFaceIcon(info.getContent());                        }
                        if (faceBmp != null) {
                            int wSize = faceBmp.getWidth() + 4;
                            if (width + wSize >= maxWidth) {
                                width = maxWidth;
                                break forLable;
                            }
                            width += wSize;
                        }
                        continue;
                    }

                    String text = info.getContent();
                    if (!TextUtils.isEmpty(text)) {
                        float wSize = tempPaint.measureText(text);
                        if (width + wSize >= maxWidth) {
                            width = maxWidth;
                            break forLable;
                        }
                        width += wSize;
                    }

                }
            }

            result = width;
        }

        displayWidth = result;
        return result;
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }


}

代码在:https://github.com/nickgao1986/StepSport

 

Android 中实现 TextView 文字滚动可以使用以下两种方式: 一、使用 Marquee(跑马灯)属性 在布局文件中的 TextView 中添加以下属性: ``` <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="这是一段需要滚动文字这是一段需要滚动文字这是一段需要滚动文字" android:singleLine="true" android:ellipsize="marquee" android:focusable="true" android:focusableInTouchMode="true" android:marqueeRepeatLimit="marquee_forever" android:scrollHorizontally="true" android:padding="5dp" android:textColor="#000000" android:textSize="20sp" /> ``` 其中,关键属性为: - android:ellipsize="marquee":当文字超出 TextView 的宽度时,显示省略号并开启跑马灯效果。 - android:focusable="true" 和 android:focusableInTouchMode="true":设置为可获得焦点,让 TextView 能够滚动。 - android:marqueeRepeatLimit="marquee_forever":设置跑马灯无限循环。 - android:scrollHorizontally="true":可水平滚动。 在 Java 代码中,调用 setHorizontallyScrolling() 方法也可以实现水平滚动,示例代码如下: ``` TextView textView = findViewById(R.id.text_view); textView.setHorizontallyScrolling(true); ``` 二、使用代码实现 在 Java 代码中使用 TextView 的 setEllipsize() 方法和 setMarqueeRepeatLimit() 方法可以实现 TextView文字滚动效果。示例代码如下: ``` TextView textView = findViewById(R.id.text_view); textView.setText("这是一段需要滚动文字这是一段需要滚动文字这是一段需要滚动文字"); textView.setSingleLine(true); textView.setEllipsize(TextUtils.TruncateAt.MARQUEE); textView.setMarqueeRepeatLimit(-1); textView.setFocusable(true); textView.setFocusableInTouchMode(true); textView.requestFocus(); ``` 需要注意的是,如果在代码中设置了跑马灯效果,还需要在布局文件中设置以下属性: ``` android:singleLine="true" android:scrollHorizontally="true" ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值