当前位置: 首页 > news >正文

做瞹瞹嗳视频网站如何用手机做网站

做瞹瞹嗳视频网站,如何用手机做网站,短视频seo排名加盟,推广赚钱平台有哪些TicktockMusic 音乐播放器项目相关文章汇总#xff1a;简介之前做 TicktockMusic 音乐播放器#xff0c;一个必要的需求肯定是歌词#xff0c;在 github 上找了几个#xff0c;发现或多或少都有点不满足需求#xff0c;所以就自己动手写了一个#xff0c;本篇文章主要介绍…TicktockMusic 音乐播放器项目相关文章汇总简介之前做 TicktockMusic 音乐播放器一个必要的需求肯定是歌词在 github 上找了几个发现或多或少都有点不满足需求所以就自己动手写了一个本篇文章主要介绍下实现的原理。先附上项目地址和效果图效果图image需求歌词的需求我想大家都很清楚简单的话直接打开一个音乐播放器查看一下。我们打开后分析一下歌词的功能歌词完整的显示出来、当前歌词变色、可以根据时间而进行定位、可以手动滑动、滑动后显示一个指示器、点击指示器播放进度跳转、滑动时指示器变色等等。OK我们自己写歌词控件这些功能也是必不可少的接下来就逐步分析下实现的过程。实现歌词解析歌词显示滑动处理指示器基本实现就是这几个过程接下来一步步的分析。歌词解析首先我们在网上下载一个歌词即以 lrc 为后缀的文件。比如海阔天空这首歌的歌词我们用记事本或者其他工具打开后就可以看到具体的歌词内容如下[ti: 海阔天空][ar:黄家驹][al:乐与怒][by:mp3.50004.com][00:00.00]Beyond海阔天空[01:40.00][00:16.00]今天我寒夜里看雪飘过[01:48.00][00:24.00]怀著冷却了的心窝飘远方[01:53.00][00:29.00]风雨里追赶...[00:42.00]多少次迎著冷眼与嘲笑[00:49.00]从没有放弃过心中的理想[00:54.00]一刹那恍惚...可以看到歌词主要包含歌名、歌手、专辑、作者等头元素以及歌词的主体内容我们需要处理的就是主体的歌词内容。首先歌词是一行一行的文本其次每行的文本都包含时间标签和具体的一行歌词我们首先将歌词解析为一行行的数据。InputStreamReader isr null;BufferedReader br null;try {isr new InputStreamReader(inputStream, CHARSET);br new BufferedReader(isr);String line;while ((line br.readLine()) ! null) {//此处的 line 即为一行行的文本//parseLrc 方法为解析单行List lrcList parseLrc(line);if (lrcList ! null lrcList.size() ! 0) {lrcs.addAll(lrcList);}}sortLrcs(lrcs);return lrcs;}catch ...解析为一行行的文字后就需要具体的处理单行的文字了我们可以看到大部分歌词包含两种格式即单个时间标签和多个时间标签这里可以采用正则表达式来匹配文字正则表达式为 (([\d{2}:\d{2}.\d{2}]))(.*)[01:53.00][00:29.00]风雨里追赶 //多个时间标签[00:42.00]多少次迎著冷眼与嘲笑 //单个时间标签接下来根据正则表达式来解析单行歌词private static List parseLrc(String lrcLine) {if (lrcLine.trim().isEmpty()) {return null;}List lrcs new ArrayList();Matcher matcher Pattern.compile(LINE_REGEX).matcher(lrcLine);if (!matcher.matches()) {return null;}String time matcher.group(1);String content matcher.group(3);Matcher timeMatcher Pattern.compile(TIME_REGEX).matcher(time);while (timeMatcher.find()) {String min timeMatcher.group(1);String sec timeMatcher.group(2);String mil timeMatcher.group(3);Lrc lrc new Lrc();if (content ! null content.length() ! 0) {lrc.setTime(Long.parseLong(min) * 60 * 1000 Long.parseLong(sec) * 1000 Long.parseLong(mil) * 10);lrc.setText(content);lrcs.add(lrc);}}return lrcs;}这样第一步就完成了歌词解析完成后得到歌词的数据集合每个元素都包括时间和内容。歌词显示歌词显示的思路就是将歌词一行行的画出来我们首先假设屏幕足够大那么只需要定位第一行歌词的位置画出来第一行歌词然后逐行下移一个固定的距离再画出下一行歌词依次类推整个歌词内容就会全部画在画布上了。依照这个思路我们可以先画出来文字。//此处为伪代码float y getLrcHeight() / 2;float x getLrcWidth() / 2 getPaddingLeft();for (int i 0; i getLrcCount(); i) {if (i 0) {y textHeight mLrcLineSpaceHeight;}...canvas.drawText(text, x, y, mPaint);}画出来文字的思路就是这样首先从屏幕的中间开始然后纵坐标每次增加文字的高度与距离之和依次画出来每行文字。这样假如屏幕足够大的话那么所有的歌词就会从屏幕中间开始依次向下一行行的显示出来。但是我们的屏幕不可能是无限大的。首先假如一行歌词很长的话canvas.drawText() 的效果会是屏幕覆盖掉多余的 text 文字所以当一行文字超过我们设置的 View 最大宽度时最理想的方法就是多余的部分换行就像 TextView 一样。所幸的是Android 中给我们提供了方法那就是 StaticLayout StaticLayout 用法很简单我们使用它来替代 canvas.drawText()下面是基本用法。private void drawLrc(Canvas canvas, float x, float y, int i) {mTextPaint.setTextSize(mLrcTextSize);String text mLrcData.get(i).getText();StaticLayout staticLayout new StaticLayout(text, mTextPaint, getLrcWidth(),Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false);canvas.save();canvas.translate(x, y - staticLayout.getHeight() / 2 - mOffset);staticLayout.draw(canvas);canvas.restore();}这样我们就能获取想要的效果了文字一行行的排列文字比较长的话会自动换行到下一行。但是这样仅仅是实现效果在 onDraw() 方法中我们应该尽量的避免新建对象以免造成界面的卡顿而 StaticLayout 需要实例化对象所以这边需要我们手动优化一下。因为使用 StaticLayout 后一行文字的高度不再固定所以 y 坐标不再累加固定的文字高度而是上一行和下一行文字之和的一半文字间距。代码如下for (int i 0; i getLrcCount(); i) {if (i 0) {y (getTextHeight(i - 1) getTextHeight(i)) / 2 mLrcLineSpaceHeight;}drawLrc(canvas, x, y, i);}为了避免过多的实例化在使用 StaticLayout 时这里采用 map 进行缓存创建过对象后缓存起来后边就不需要再继续创建。private void drawLrc(Canvas canvas, float x, float y, int i) {String text mLrcData.get(i).getText();StaticLayout staticLayout mLrcMap.get(text);if (staticLayout null) {mTextPaint.setTextSize(mLrcTextSize);staticLayout new StaticLayout(text, mTextPaint, getLrcWidth(),Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false);mLrcMap.put(text, staticLayout);}canvas.save();canvas.translate(x, y - staticLayout.getHeight() / 2 - mOffset);staticLayout.draw(canvas);canvas.restore();}到这里我们已经解决了水平方向的显示但是垂直方向呢垂直方向则利用滑动来解决这也是歌词的基本需求之一。滑动处理歌词的滑动是做歌词控件的必然要求包括根据音乐播放的进度进行自动的滑动以及用户主动拖动的滑动我们来逐个分析。1、根据播放进度滚动音乐的播放时间进度可以根据 MediaPlayer 来获取在一首音乐播放的过程中播放的进度是不断更新的所以就需要我们根据这个不断更新的时间来决定歌词滚动的位置。我们需要比较不断更新的时间和每行歌词的时间最接近或者相等时就可以视作音乐播放的进度对应当前这一行歌词所以需要获取播放时间对应的歌词行数。private int getUpdateTimeLinePosition(long time) {int linePos 0;for (int i 0; i getLrcCount(); i) {Lrc lrc mLrcData.get(i);if (time lrc.getTime()) {if (i getLrcCount() - 1) {假如时间大于最后一行歌词的时间则行数为最后一行linePos getLrcCount() - 1;} else if (time mLrcData.get(i 1).getTime()) {//否则若同时小于下一行则行数为 ilinePos i;break;}}}return linePos;}获取行数之后行数变化时就可以利用动画来让歌词进行滚动。private void scrollToPosition(int linePosition) {float scrollY getItemOffsetY(linePosition);//将要滚动的一行的偏移量final ValueAnimator animator ValueAnimator.ofFloat(mOffset, scrollY);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {Overridepublic void onAnimationUpdate(ValueAnimator animation) {mOffset (float) animation.getAnimatedValue();invalidateView();}});animator.setDuration(300);animator.start();}此处最重要的属性就是 mOffset mOffset 是为了决定歌词偏移量而定义的一个属性 mOffset 的取值是在原有值和目标行的偏移量之间由动画控制其变化。假如向下滑动初始为0则滚动到第二行歌词mOffset 就是从 0 到 getItemOffsetY(1) 的过程。 getItemOffsetY(i) 就是第 i 行的偏移量。private float getItemOffsetY(int linePosition) {float tempY 0;for (int i 1; i linePosition; i) {tempY (getTextHeight(i - 1) getTextHeight(i)) / 2 mLrcLineSpaceHeight;}return tempY;}然后再根据播放进度进行不断的更新。public void updateTime(long time) {if (isLrcEmpty()) {return;}int linePosition getUpdateTimeLinePosition(time);if (mCurrentLine ! linePosition) {mCurrentLine linePosition;ViewCompat.postOnAnimation(LrcView.this, mScrollRunnable);}}private Runnable mScrollRunnable new Runnable() {Overridepublic void run() {scrollToPosition(mCurrentLine);}};到此为止我们已经完成了歌词的自动滚动功能。2、滑动事件处理仅仅有自动滚动是无法满足歌词的需求的所以我们还需要控制歌词的滑动事件让用户可以手动滑动歌词到某个位置。既然是手势的事件那么就需要我们重写 onTouch 方法处理不同的手势。Overridepublic boolean onTouchEvent(MotionEvent event) {if (isLrcEmpty()) { //歌词为空则默认事件return super.onTouchEvent(event);}//速度跟踪if (mVelocityTracker null) {mVelocityTracker VelocityTracker.obtain();}mVelocityTracker.addMovement(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN:removeCallbacks(mScrollRunnable);if (!mOverScroller.isFinished()) {mOverScroller.abortAnimation();}mLastMotionX event.getX();mLastMotionY event.getY();isUserScroll true;isDragging false;break;case MotionEvent.ACTION_MOVE:float moveY event.getY() - mLastMotionY;if (Math.abs(moveY) mScaledTouchSlop) {isDragging true;isShowTimeIndicator isEnableShowIndicator;}if (isDragging) {float maxHeight getItemOffsetY(getLrcCount() - 1);if (mOffset 0 || mOffset maxHeight) {moveY / 3.5f;}mOffset - moveY;mLastMotionY event.getY();invalidateView();}break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:handleActionUp(event);break;}return true;}简单解释下上述代码先忽略掉 VelocityTracker 和 OverScroller。在 ACTION_DOWN 时记录下 x 和 y 的坐标然后在 ACTION_MOVE 时若拖动的距离大于触发滑动的最小值则改变 mOffset 的值然后刷新 View。当 mOffset 0 或者 mOffset maxHeight 即歌词已经滚动到顶部或者底部时为了回弹的阻尼效果将 moveY 的值大幅减小。接下来介绍下手势抬起的事件VelocityTracker 和 OverScroller 就是用于此处在手势滑动抬起时我们希望有一个 fling 的效果Android 中的 OverScroller 可以简单的实现这种效果。private void handleActionUp(MotionEvent event) {//越界的处理if (overScrolled() mOffset 0) {scrollToPosition(0);ViewCompat.postOnAnimationDelayed(LrcView.this, mScrollRunnable, mTouchDelay);return;}if (overScrolled() mOffset getItemOffsetY(getLrcCount() - 1)) {scrollToPosition(getLrcCount() - 1);ViewCompat.postOnAnimationDelayed(LrcView.this, mScrollRunnable, mTouchDelay);return;}mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);float YVelocity mVelocityTracker.getYVelocity();float absYVelocity Math.abs(YVelocity);if (absYVelocity mMinimumFlingVelocity) {mOverScroller.fling(0, (int) mOffset, 0, (int) (-YVelocity), 0,0, 0, (int) getItemOffsetY(getLrcCount() - 1),0, (int) getTextHeight(0));invalidateView();}releaseVelocityTracker();if (isAutoAdjustPosition) {ViewCompat.postOnAnimationDelayed(LrcView.this, mScrollRunnable, mTouchDelay);}}当手势抬起时计算下当前的手势速度然后利用 mOverScroller.fling() 方法在 computeScroll() 中改变 mOffset 的值即可。Overridepublic void computeScroll() {super.computeScroll();if (mOverScroller.computeScrollOffset()) {mOffset mOverScroller.getCurrY();invalidateView();}}这样主动的手势功能也已经实现了。指示器用户手动滑动歌词的目的很大一部分是为了滑动后能根据歌词来控制播放的进度所以指示器也是一个不可或缺的功能。当用户滑动歌词时显示指示器歌词经过指示器的位置时变色用户点击指示器按钮后歌词跳转到这个位置播放进度也到了这里。首先要做的就是显示指示器以及歌词变色这里就需要我们获取歌词在指示器的位置时歌词的行数因为指示器画在歌词的中间位置所以某一行歌词的偏移量和 mOffset 的差值最小时就可以看作这一行歌词经过了指示器。public int getIndicatePosition() {int pos 0;float min Float.MAX_VALUE;//itemOffset 和 mOffset 最小时当前的位置for (int i 0; i mLrcData.size(); i) {float offsetY getItemOffsetY(i);float abs Math.abs(offsetY - mOffset);if (abs min) {min abs;pos i;}}return pos;}然后在 onDraw() 中画出来具体的特性。if (isShowTimeIndicator) {mPlayDrawable.draw(canvas); // 画出指示器的播放按钮long time mLrcData.get(indicatePosition).getTime();float timeWidth mIndicatorPaint.measureText(LrcHelper.formatTime(time)); //获取指示时间的文字长度mIndicatorPaint.setColor(mIndicatorLineColor);// 画出指示线canvas.drawLine(mPlayRect.right mIconLineGap, getHeight() / 2,getWidth() - timeWidth * 1.3f, getHeight() / 2, mIndicatorPaint);int baseX (int) (getWidth() - timeWidth * 1.1f);float baseline getHeight() / 2 - (mIndicatorPaint.descent() - mIndicatorPaint.ascent()) / 2 - mIndicatorPaint.ascent();mIndicatorPaint.setColor(mIndicatorTextColor);//画出指示时间文字canvas.drawText(LrcHelper.formatTime(time), baseX, baseline, mIndicatorPaint);}最后处理用户点击事件并且将当前行的歌词及时间进行回调来控制播放进度。if (isShowTimeIndicator mPlayRect ! null onClickPlayButton(event)) {isShowTimeIndicator false;invalidateView();if (mOnPlayIndicatorLineListener ! null) {mOnPlayIndicatorLineListener.onPlay(mLrcData.get(getIndicatePosition()).getTime(),mLrcData.get(getIndicatePosition()).getText());}}//点击在按钮范围才响应private boolean onClickPlayButton(MotionEvent event) {float left mPlayRect.left;float right mPlayRect.right;float top mPlayRect.top;float bottom mPlayRect.bottom;float x event.getX();float y event.getY();return mLastMotionX left mLastMotionX right mLastMotionY top mLastMotionY bottom x left x right y top y bottom;}这样指示器的功能也就完成了。总结上述就是整个歌词控件绘制的流程还有一些颜色变化等细节功能就不一一说明了有兴趣可以看一看源码。这个控件我也已经封装成了一个自定义 View 的库可以在 https://github.com/Lauzy/LyricView 这里看下具体的使用。欢迎讨论、欢迎 star。参考:
http://www.pierceye.com/news/899219/

相关文章:

  • 做网站万网长春建站模板展示
  • 广州专业建网站公司福州网站制作
  • 西安烽盈网站建设郑州营销网站托管
  • 诸几建设银行网站怎么维护好网站
  • 深圳市国外网站建设简单html5网页设计
  • 网站制作公司西南城乡建设部网站首页
  • 网站名和域名能一样吗企业网站建设硬件
  • 德州做网站公司怎么开网店淘宝
  • 苏州做网站优化的电商定制开发
  • 广西庆海建设发展有限公司网站昆山有做网站的公司吗
  • 前端课程网站wordpress 微博登陆
  • asp怎么做网站适配开发公司安置房项目工程推进大会
  • 学做网站可以赚钱吗怎么批量修改wordpress文章内容
  • 写作网站vir上海博大园林建设发展有限公司网站
  • wordpress video gallery网站代码优化怎么做
  • 厦门网站设计品牌企业互联网门户网站建设
  • 做名片模板网站中文响应式网站
  • 用tornado做网站石家庄 外贸网站建设公司
  • 档案网站建设网页wordpress keyshot
  • 鞍山制作网站哪家好建设银行员工网站
  • 手机怎么提升网站流量品牌型网站成功案例图片
  • 网站视频主持人制作网站开发 质量管理
  • 网站的外链建设计划石家庄市城乡建设部网站
  • 电子商务网站规划与建设论文电子商务营销方法
  • 宁波做网站费用电子商城开发网站开发
  • 太原市住房和城乡建设部网站免费的logo在线设计
  • 做it的在哪个网站找工作wordpress 幻燈片 插件
  • 湘潭做网站 i磐石网络博学网站建设公司
  • 揭阳市建设发展总公司网站自己做的视频网站如何赚钱
  • 泉州自助建站软件天眼查在线查询官网