我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android实现文字动态高亮读取进度效果

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android实现文字动态高亮读取进度效果

本文实例为大家分享了Android实现文字动态高亮读取进度的具体代码,供大家参考,具体内容如下

1、效果图

类似歌词的效果。播放下面文字的音频,同时音频播放的进度和文字高亮进度保持一致。

2、代码结构和实现

简单的类图:

ISubtitleView接口代码如下:



public interface ISubtitleView {

    
    List<SubtitleItem> getSubtitleItemList();

    
    void setSubtitleItemList(List<SubtitleItem> linesTextList);

    
    void setDuration(long duration);

    
    void updatePts(long pts);

    
    void reset();
}
EMSubtitleView类的代码如下:


public class EMSubtitleView extends android.support.v7.widget.AppCompatTextView  implements ISubtitleView{

    private int mMeasuredWidth;
    private int mMeasuredHeight;
    private List<SubtitleItem> mSubtitleItemList;
    private List<LineEntity> mLinesTextList;
    private Paint mNormalPaint;
    private Paint mHLPaint;
    private long mPts = 0;
    private int mCurHLLine;
    private float mReadSubtitleCount;
    private Rect mLastHLRect;
    private int mHLTextColor;
    private long mDuration;
    private boolean mIsPlain = false;

    public EMSubtitleView(Context context) {
        this(context , null);
    }

    public EMSubtitleView(Context context, AttributeSet attrs) {
        this(context, attrs , 0);
    }

    public EMSubtitleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mSubtitleItemList = new ArrayList<>();
        mLinesTextList = null;
        mLastHLRect = new Rect();
        mNormalPaint = null;
        initAttrs(attrs , defStyleAttr);

    }
    private void initAttrs(AttributeSet attrs , int defStyleAttr) {
        TypedArray ta = getContext().obtainStyledAttributes(attrs , R.styleable.EMSubtitleView , defStyleAttr , R.style.EMSubtitleViewDefaultTheme);
        for(int i = 0 ; i < ta.getIndexCount() ; i++){
            int index = ta.getIndex(i);
            if (index == R.styleable.EMSubtitleView_highLightTextColor) {
                mHLTextColor = ta.getColor(index, getResources().getColor(R.color.emvideovisit_color_FF9000));
            }
        }
        ta.recycle();
    }
    private void initHLPaint() {
        mHLPaint = new Paint(mNormalPaint);
        mHLPaint.setColor(mHLTextColor);
        mHLPaint.setTextSize(getTextSize());
    }

    
    public void setRichText(String text){
        mIsPlain = false;
        reset();
        setText(text);
        setDuration(mDuration , true);
    }

    
    public void setPlainText(String text){
        mIsPlain = true;
        reset();
        setText(text);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mMeasuredWidth == 0 || mMeasuredHeight == 0) {
            mMeasuredWidth = getMeasuredWidth();
            mMeasuredHeight = getMeasuredHeight();
        }

        if(mIsPlain){
            super.onDraw(canvas);
            return;
        }
        if(mNormalPaint == null){
            super.onDraw(canvas);
            mNormalPaint = getPaint();
            initHLPaint();
            return;
        }

        if(mLinesTextList == null){
            fillLinesEntityListJustOnce();
        }
        //没有pts,绘制
        if(mPts <= 0){
            drawNormalText(canvas);
            return;
        }
        drawNormalText(canvas);
        calculateReadLineAndWordsCount();

        //绘制高亮部分歌词
        drawHLText(canvas);
    }

    private void drawHLText(Canvas canvas) {
        if(mCurHLLine >= 0){
            int curLineTextCount = 0;
            for(int i = 0 ; i < mLinesTextList.size() ; ++i){
                LineEntity entity = mLinesTextList.get(i);
                if(mCurHLLine > i){
                    canvas.drawText( entity.lineText , entity.left , entity.baseLine , mHLPaint);
                }else if(mCurHLLine == i && mReadSubtitleCount > 0 ){
                    canvas.save();
                    mLastHLRect.set(entity.left , entity.top , entity.left + (int) (mHLPaint.measureText(entity.lineText)*1.0f/entity.lineText.length()*(mReadSubtitleCount-curLineTextCount)), entity.bottom);
                    canvas.clipRect(mLastHLRect );
                    canvas.drawText( entity.lineText , entity.left , entity.baseLine , mHLPaint);
                    canvas.restore();
                    //遮挡的话需要滚动
                    if(entity.baseLine > getHeight()){
                        if(getScrollY() != entity.bottom - getHeight() + 1){
                            setScrollY( entity.bottom - getHeight() + 1);
                        }
                    }
                    break;
                }
                curLineTextCount += entity.lineText.length();
            }
        }
    }

    private void calculateReadLineAndWordsCount() {
        float curSubtitleCount = 0;
        for(SubtitleItem subtitleItem :  mSubtitleItemList){
            //文字之间不可以有空隙,否则mCurHLLine可能一直为-1;
            if(mPts >= subtitleItem.getStartTime() && mPts < subtitleItem.getEndTime()){
                float lineOffset = (subtitleItem.getWords().length()*1.0f/(subtitleItem.getEndTime() - subtitleItem.getStartTime()))*(mPts-subtitleItem.getStartTime());
                curSubtitleCount += lineOffset;
                int curLineTextCount = 0;
                for(int i = 0 ; i < mLinesTextList.size() ; ++i){
                    curLineTextCount += mLinesTextList.get(i).lineText.length();
                    if(curLineTextCount > curSubtitleCount){
                        mCurHLLine = i;
                        break;
                    }
                }
                break;
            }
            curSubtitleCount += subtitleItem.getWords().length();
        }
        mReadSubtitleCount = curSubtitleCount;
    }

    private void fillLinesEntityListJustOnce() {
        if(mLinesTextList != null){
            return;
        }
        mLinesTextList = new ArrayList<>();
        Layout layout = getLayout();
        int line=getLayout().getLineCount();
        String text=layout.getText().toString();
        for(int i=0;i<line;i++){
            int start=layout.getLineStart(i);
            int end=layout.getLineEnd(i);
            int left = (int) layout.getLineLeft(i);
            int baseLine = layout.getLineBaseline(i);
            Paint.FontMetrics fontMetrics = getPaint().getFontMetrics();
            int top = (int) (baseLine + fontMetrics.top);
            int bottom = (int) (baseLine + fontMetrics.bottom);
            int ascent = (int) (baseLine + fontMetrics.ascent);
            int descent = (int) (baseLine + fontMetrics.descent);

            mLinesTextList.add(new LineEntity(text.substring(start, end) , top , bottom , ascent , descent ,baseLine , left));
        }
    }

    private void drawNormalText(Canvas canvas) {
        for(LineEntity entity : mLinesTextList){
            canvas.drawText(entity.lineText, entity.left,  entity.baseLine , mNormalPaint);
        }
    }

    public List<SubtitleItem> getSubtitleItemList() {
        return mSubtitleItemList;
    }

    
    public void setSubtitleItemList(List<SubtitleItem> linesTextList) {
        if(mSubtitleItemList != null){
            mSubtitleItemList.clear();
            mSubtitleItemList.addAll(linesTextList);
        }else{
            this.mSubtitleItemList = linesTextList;
        }
    }

    
    public void setDuration(long duration){
        setDuration( duration , false);
    }

    private void setDuration(long duration , boolean force){
        if((duration > 0 && mDuration != duration) || force){
            mDuration = duration;
            if(mSubtitleItemList != null){
                mSubtitleItemList.clear();
            }else{
                this.mSubtitleItemList = new ArrayList<>();
            }
            mSubtitleItemList.add(new SubtitleItem(getText().toString() , 0 , duration));
        }
    }

    
    public void updatePts(long pts){
        mPts = pts;
        postInvalidate();
    }

    
    public void reset(){
        mPts = 0;
        mCurHLLine = 0;
        mReadSubtitleCount = 0;
        mLinesTextList = null;
        mNormalPaint = null;
        setScrollY(0);
        postInvalidate();

    }

    static class LineEntity{
       String lineText;
       int top;
       int bottom;
       int ascent;
       int descent;
       int baseLine;
       int left;


        public LineEntity(String lineText, int top, int bottom, int ascent, int descent, int baseLine, int left) {
            this.lineText = lineText;
            this.top = top;
            this.bottom = bottom;
            this.ascent = ascent;
            this.descent = descent;
            this.baseLine = baseLine;
            this.left = left;
        }

    }
}

布局文件里使用类似如下:


<com.eastmoney.emvideovisit.view.EMSubtitleView
        android:id="@+id/play_text"
        android:layout_width="match_parent"
        android:layout_height="@dimen/emvideovisit_dp_60"
        android:layout_marginLeft="@dimen/emvideovisit_dp_60"
        android:layout_marginRight="@dimen/emvideovisit_dp_60"
        android:gravity="center_horizontal"
        android:layout_marginTop="@dimen/emvideovisit_dp_20"
        android:textColor="#fff"
        app:highLightTextColor="#A6EA5504"
        android:text="@string/emvideovisit_string_headset_tips"
        android:textSize="@dimen/emvideovisit_dp_16"
        android:layout_marginBottom="@dimen/emvideovisit_dp_4"/>

3、其它

处理过程中文字位置的参数需要注意:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android实现文字动态高亮读取进度效果

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android如何实现文字动态高亮读取进度效果

小编给大家分享一下Android如何实现文字动态高亮读取进度效果,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!具体内容如下1、效果图类似歌词的效果。播放下面文字的音频,同时音频播放的进度和文字高亮进度保持一致。2、代码结构
2023-06-15

Android自定义Dialog实现文字动态加载效果

之前在技术问答上面看到一个提问 “加载中…” 后面三个点是动态的,这么一个效果实现。想来想去,好像没想到好的处理方式。 尝试了一下,以一个最笨的方式实现了。先来看一下效果 :我是通过自定义一个Dialog,加载中的效果,是在Dialog内
2022-06-06

前方高能 实现PPT动态文字效果的三种方法

  什么?你竟然知道怎么在PPT中怎么制作动态文字效果?这么好玩而且高大上的技能大家怎么能错过呢?今天我们将会讲到三种在PPT中的实现动态文字效果的方法,让大家轻松制作出一个独特而且极具美感的PPT,多学到一项新技能!  首先我们来看一下三种动态文字效果吧!然后小编再给大家逐一讲解不同的动态文字效果的制作方法。。  &
前方高能 实现PPT动态文字效果的三种方法
2024-04-17

CSS3怎么实现歌词进度文字颜色填充变化动态效果的思路

这篇文章将为大家详细讲解有关CSS3怎么实现歌词进度文字颜色填充变化动态效果的思路,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。播放音乐时,歌词会随歌曲的进度逐渐填充颜色,不是逐字改变颜色,而是从左向右横
2023-06-08

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录