Android仿天天动听歌曲自动滚动view

最近项目中要做一个类似天天动听歌曲自动滚动行数的效果。首先自己想了下Android要滚动的那就是scroller类或者scrollto、scrollby结合了,或者view.layout()方法,或者使用动画。但是要循环滚动,貌似这些到最后一行滚动到第一行都有往回滚的效果,都不是很好的解决方法。怎么会忘记了可以绘制事件万物的的canvas呢。好吧,既然找到了,那就用这个方案吧!但是天天动听歌曲还有一个手动滑动的效果,貌似这篇文章没写。既然这样,那就自己来写下吧!实现之前还是先看下天天动听的效果:

正文

想法1:获取滑动的距离,然后计算滑动了多少行,然后更新数据。实现起来貌似效果不咋地。
想法2:我们可以看的出来他滚动是一行一行的滚动的,只是根据滚动的快慢来决定滚动行数的快慢。既然这样的话,只要滚动了,就一定时间的去一行行的滚动,然后根据滚动的速度来决定更新的间隔时间。

嗯,想好了怎么实现,现在就来写代码吧。

先来写一个类,继承TextView

VerticalScrollTextView.class

public class VerticalScrollTextView extends TextView implements Runnable{
 //绘制歌词画笔
 private Paint mContentPaint;
 //绘制基线画笔
 private Paint mLinePaint;
 //绘制滑动进度背景画笔
 private Paint mRectPaint;
 //歌词数据
 private List<Sentence> mDataList;
 //行数
 private int index = 0 ;
 //当前view的宽
 private float mX;
 //当前view的高
 private float mY;
 //当前view垂直方向中线
 private float middleY;
 //行与行之间的间距
 private final static int DY = 80 ;
 //歌词文字大小
 private int mTextSize = 35;
 //歌词中间字体的大小
 private int mBigTextSize = 45;
 //当前是否按下
 private boolean isTouch = false ;
 //上一次触摸view的y轴坐标
 private float mLastY;
 //是否正在滑动
 private boolean isMoving;
 //记录上一次滑动的时间
 private long lastMoveTime;
 //滑动速度追踪类
 private VelocityTracker mVelocityTracker;
 //滑动最大速度
 private int mMaximumVelocity;
 //歌词是否为空
 private boolean isEmpty;

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

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

 public VerticalScrollTextView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 //获取最大的滑动速度值
 mMaximumVelocity = ViewConfiguration.get(context)
  .getScaledMaximumFlingVelocity();
 init();
 }
 private void init(){
 setFocusable(true);
 setClickable(true);
 //歌词为空设置默认值
 if(mDataList==null){
  mDataList = new ArrayList<>();
  Sentence sentence = new Sentence(0,"没有获取到歌词",0);
  mDataList.add(sentence);
  isEmpty = true ;
 }
 //初始化歌词画笔
 mContentPaint = new Paint();
 mContentPaint.setTextSize(mTextSize);
 mContentPaint.setAntiAlias(true);
 mContentPaint.setColor(Color.parseColor("#e5e2e2"));
 //设置为serif字体
 mContentPaint.setTypeface(Typeface.SERIF);
 //设置字体为居中
 mContentPaint.setTextAlign(Paint.Align.CENTER);
 //初始化基线画笔
 mLinePaint = new Paint();
 mLinePaint.setAntiAlias(true);
 mLinePaint.setStrokeWidth(1);
 mLinePaint.setColor(Color.WHITE);
 //进度背景颜色画笔
 mRectPaint = new Paint();
 mLinePaint.setAntiAlias(true);
 mRectPaint.setColor(Color.parseColor("#66666666"));
 }

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 //如果当前进度为-1,直接返回,不用绘制
 if (index == -1)
  return;
 Sentence sentence = mDataList.get(index);
 //绘制中间行的歌词,设置为高亮白色,大字体
 mContentPaint.setColor(Color.WHITE);
 mContentPaint.setTextSize(mBigTextSize);
 canvas.drawText(sentence.getName(), mX/2, middleY, mContentPaint);
 //当前为歌词不为空并且按下的情况下,绘制基线和进度
 if(!isEmpty&&isTouch){
  //获取中间行字体最高的位置
  float baseLine = middleY-Math.abs(mContentPaint.ascent());
  //绘制进度背景
  canvas.drawRect(10.0f,baseLine-70,150.0f,baseLine,mRectPaint);
  //绘制基线
  canvas.drawLine(10.0f,baseLine,mX-10,baseLine,mLinePaint);
  //设置进度字体大小
  mContentPaint.setTextSize(mTextSize);
  //绘制进度字体
  canvas.drawText(String.valueOf(index),85,baseLine-35,mContentPaint);
 }
 //初始化isEmpty
 isEmpty = false ;
 //初始化歌词内容画笔
 mContentPaint.setColor(Color.parseColor("#e5e2e2"));
 mContentPaint.setTextSize(mTextSize);
 //暂时保存中间线位置,来绘制中间线以上的行数字体
 float tempY = middleY;
 //绘制中间线以上的歌词
 for (int i = index - 1; i >= 0; i--) {
  tempY = tempY - DY;
  if (tempY < 0) {
  break;
  }
  Sentence preSentence = mDataList.get(i);
  canvas.drawText(preSentence.getName(), mX/2, tempY, mContentPaint);
 }
 tempY = middleY;
 //绘制中间线以下的歌词
 for (int i = index + 1; i < mDataList.size(); i++) {
  tempY = tempY + DY;
  if (tempY > mY) {
  break;
  }
  Sentence nexeSentence = mDataList.get(i);
  canvas.drawText(nexeSentence.getName(), mX/2, tempY, mContentPaint);
 }
 //初始化isMoving,到这里表示滑动结束
 isMoving = false ;
 }
 protected void onSizeChanged(int w, int h, int ow, int oh) {
 super.onSizeChanged(w, h, ow, oh);
 //获取view的宽和高
 mX = w;
 mY = h;
 middleY = h * 0.5f;
 }
 public long updateIndex(int index) {
 if (index == -1)
  return -1;
 this.index=index;
 return index;
 }
 public List<Sentence> getDataList() {
 return mDataList;
 }
 public void setDataList(List<Sentence> mDataList){
 this.mDataList = mDataList ;
 }
 public void updateUI(){
 new Thread(this).start();
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 int action = event.getAction();
 switch (action){
  case MotionEvent.ACTION_DOWN:
  isTouch =true;
  mLastY = event.getY();
  break;
  case MotionEvent.ACTION_MOVE:
  //创建速度追踪器
  initVelocityTrackerIfNotExists();
  mVelocityTracker.addMovement(event);
  mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  //获取当前速度。默认为100
  float velocity = mVelocityTracker.getYVelocity()==0?100:mVelocityTracker.getYVelocity();
  long currentTime = System.currentTimeMillis();
  //设置一个固定值和速度结合决定滑动更新的快慢
  if(!isMoving&&currentTime-lastMoveTime>20000/Math.abs(velocity)){
   isMoving = true ;
   lastMoveTime = System.currentTimeMillis();
   float currentY = event.getY();
   float mMoveY = currentY - mLastY;
   //向上滑动-1向下滑动+1
   int newIndex = mMoveY>0?index - 1:index+1;
   //循环滚动
   newIndex=newIndex<0?mDataList.size()-1:newIndex>=mDataList.size()?0:newIndex;
   updateIndex(newIndex);
   invalidate();
   mLastY = currentY;
  }
  break;
  case MotionEvent.ACTION_UP:
  isTouch = false ;
  recycleVelocityTracker();
  break;
 }
 return super.onTouchEvent(event);
 }

 @Override
 public void run() {
 //自动滚动刷新的时间间隔
 long time = 1000;
 //控制进度
 int i=0;
 while (true) {
  //当前不在按下的情况下自动滚动
  if(!isTouch){
  //设置当前的进度值
  long sleeptime = updateIndex(i);
  //使用handle刷新ui
  mHandler.post(mUpdateResults);
  if (sleeptime == -1)
   return;
  try {
   Thread.sleep(time);
   i++;
   //当到了最后一行的时候自动跳转到第一行
   if(i==getDataList().size())
   i=0;
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  }
 }
 }
 Handler mHandler = new Handler();
 Runnable mUpdateResults = new Runnable() {
 public void run() {
  invalidate();
 }
 };
 //创建速度追踪器
 private void initVelocityTrackerIfNotExists() {
 if (mVelocityTracker == null) {
  mVelocityTracker = VelocityTracker.obtain();
 }
 }
 //释放
 private void recycleVelocityTracker() {
 if (mVelocityTracker != null) {
  mVelocityTracker.recycle();
  mVelocityTracker = null;
 }
 }
}

自定义view基本就是这样了,我们可以把要定义的一些属性写在attrs里面了,这里就懒得写了。大概的思路就是先绘制指定的index行的歌词,然后绘制index上面行的歌词,然后绘制index下面行的歌词。然后新建一个线程,让它通过handle隔一定的时间定时刷新歌词行数。然后在onTouchEvent处理触摸滚动行数,获取到当前滚动速度来决定一个更新的时间间隔。从而实现触摸滚动刷新的快慢。基本上就是这样了。其他的看注释。

再看下初始化数据测试的Activity:

VerticalScrollTextActivity.class

public class VerticalScrollTextActivity extends Activity {
 VerticalScrollTextView mSampleView;
 String[] str = {"你在南方的艳阳里 大雪纷飞",
  "我在北方的寒夜里 四季如春",
  "如果天黑之前来的及",
  "我要忘了你的眼睛",
  "穷极一生 做不完一场梦",
  "他不在和谁谈论相逢的孤岛",
  "因为心里早已荒无人烟",
  "他的心里在装不下一个家",
  "做一个只对自己说谎的哑巴",
  "他说你任何为人称道的美丽",
  "不及他第一次遇见你",
  "时光苟延残喘 无可奈何",
  "如果所有土地连在一起",
  "走上一生只为拥抱你",
  "喝醉了他的梦 晚安",
  "你在南方的艳阳里 大雪纷飞",
  "我在北方的寒夜里 四季如春",
  "如果天黑之前来的及",
  "我要忘了你的眼睛",
  "穷极一生 做不完一场梦",
  "他不在和谁谈论相逢的孤岛",
  "因为心里早已荒无人烟",
  "他的心里在装不下一个家",
  "做一个只对自己说谎的哑巴",
  "他说你任何为人称道的美丽",
  "不及他第一次遇见你",
  "时光苟延残喘 无可奈何",
  "如果所有土地连在一起",
  "走上一生只为拥抱你",
  "喝醉了他的梦 晚安"
 };
 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 mSampleView = (VerticalScrollTextView) findViewById(R.id.sampleView1);
 List lst=new ArrayList<>();
 for(int i=0;i<str.length;i++){
  Sentence sen=new Sentence(i,str[i],i+1202034);
  lst.add(i, sen);
 }
 mSampleView.setDataList(lst);
 mSampleView.updateUI();
 }
}

模拟了一首歌词数据,然后setDataList,在调用updateUI()就行了。

最后看下布局文件

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <com.goach.lib.VerticalScrollTextView
 android:id="@+id/sampleView1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/bg"
 />
</RelativeLayout>

测试下,我们就可以看到效果了:

源码下载:Android仿天天动听歌曲自动滚动

以上就是本文的全部内容,希望对大家学习Android软件编程有所帮助。

(0)

相关推荐

  • android开发之横向滚动/竖向滚动的ListView(固定列头)

    由于项目需要,我们需要一个可以横向滚动的,又可以竖向滚动的 表格.而且又要考虑大数据量(行)的展示视图.经过几天的研究终于搞定,做了一个演示.贴图如下:      好吧.让我们看思路是什么样的: 1. 上下滚动直接使用 listView来实现. 2. 左右滚动使用HorizontalScrollView,来处理滚动.我写一个类MyHScrollView继承 自它. 2.1 . ListView里的每行(row)分为 两部分,不滚动的和可滚动的区域.比如本demo的第一列,就是静态的.而后面的所有

  • Android PickerView滚动选择器的使用方法

    手机里设置闹钟需要选择时间,那个选择时间的控件就是滚动选择器,前几天用手机刷了MIUI,发现自带的那个时间选择器效果挺好看的,于是就自己仿写了一个,权当练手.先来看效果: 效果还行吧?实现思路就是自定义一个PickerView,单独滚动的是一个PickerView,显然上图中有分和秒的选择所以在布局里用了两个PickerView.由于这里不涉及到text的点击事件,所以只需要继承View就行了,直接把text用canvas画上去.PickerView的实现的主要难点: 难点1: 字体随距离的渐变

  • Android仿淘宝商品浏览界面图片滚动效果

    用手机淘宝浏览商品详情时,商品图片是放在后面的,在第一个ScrollView滚动到最底下时会有提示,继续拖动才能浏览图片.仿照这个效果写一个出来并不难,只要定义一个Layout管理两个ScrollView就行了,当第一个ScrollView滑到底部时,再次向上滑动进入第二个ScrollView.效果如下: 需要注意的地方是: 1.如果是手动滑到底部需要再次按下才能继续往下滑,自动滚动到底部则不需要 2.在由上一个ScrollView滑动到下一个ScrollView的过程中多只手指相继拖动也不会导

  • Android高仿京东垂直循环滚动新闻栏

    实现思路其实很简单,就是一个自定义的LinearLayout,并且textView能够循环垂直滚动,而且条目可以点击,显示区域最多显示2个条目,并且还有交替的属性垂直移动的动画效果,通过线程来控制滚动的实现. 不多说看效果: 代码实现 我们先来为控件设置自定义属性: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="JDAdv

  • Android中实现多行、水平滚动的分页的Gridview实例源码

    功能要求: (1)比如每页显示2X2,总共2XN,每个item显示图片+文字(点击有链接). 如果单行水平滚动,可以用Horizontalscrollview实现. 如果是多行水平滚动,则结合Gridview(一般是垂直滚动的)和Horizontalscrollview实现. (2)水平滚动翻页,下面有显示当前页的icon. 1.实现自定义的HorizontalScrollView(HorizontalScrollView.java): 因为要翻页时需要传当前页给调用者,所以fling函数中自己

  • android实现上下滚动的TextView

    一 说明    这里重要应用类 AutoTextView,这是一个自定义的类,继承至TextSwitcher,下面临 AutoTextView类做简要说明: 1. 该类应用的重点,在于设置两个动画, setInAnimation(...)  和 setOutAnimation(...),分离是文字进入的动画和文字退出的动画: 2. 类中定义了一个外部类-Rotate3dAnimation,重要靠该类实现文字进出动画,该外部类继承至Animation.说来偶合,这个恰好是在apiDemo中看到了,

  • Android仿UC浏览器左右上下滚动功能

    本文要解决在侧滑菜单右边加个文本框,并能实现文本的上下滑动和菜单的左右滚动.这里推荐可以好好看看android的触摸事件的分发机制,这里我就不详细讲了,我只讲讲这个应用.要实现的功能就像UC浏览器(或其它手机浏览器)的左右滚动,切换网页,上下滚动,拖动内容. 本文的效果: 一.功能要求与实现 1.功能要求: (1)手指一开始按着屏幕左右移动时,只能左右滚动菜单,如果这时手指一直按着,而且上下移动了,那么菜单显示部分保持不变,但文本框也不上下移动!                       (2

  • Android新闻广告条滚动效果

    项目中需要用到类似公告栏的控件,能用的基本不支持多行显示,于是只好自己动手,苦于没有自定义过一个像样的控件,借鉴Android公告条demo,实现了多行向上滚动的控件.在原控件基础之上添加如下功能:  •传入数据分页显示  •添加Left Drawable  •手指触摸事件处理  •添加3D动画翻滚效果 效果图 源码 package com.android.view; import android.content.Context; import android.content.res.Typed

  • Android程序开发ListView+Json+异步网络图片加载+滚动翻页的例子(图片能缓存,图片不错乱)

    例子中用于解析Json的Gson请自己Google下载 主Activity: package COM.Example.Main; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import COM.Example.Main.R; import COM.Example.Main.stringG

  • Android自定义View实现广告信息上下滚动效果

    先看看效果: 实现代码: public class ScrollBanner extends LinearLayout { private TextView mBannerTV1; private TextView mBannerTV2; private Handler handler; private boolean isShow; private int startY1, endY1, startY2, endY2; private Runnable runnable; private Li

随机推荐