最近项目中要做一个类似天天动听歌曲自动滚动行数的效果。首先自己想了下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 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 getDataList() {

return mDataList;

}

public void setDataList(List 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

Sentence sen=new Sentence(i,str[i],i+1202034);

lst.add(i, sen);

}

mSampleView.setDataList(lst);

mSampleView.updateUI();

}

}

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

最后看下布局文件

activity_main.xml

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/sampleView1"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@drawable/bg"

/>

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

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

android高仿天天动听,Android仿天天动听歌曲自动滚动view相关推荐

  1. android仿天天动听歌曲自动滚动view

    转载请标明出处: http://blog.csdn.net/iamzgx/article/details/51448620: 本文出自:[iGoach的博客] 背景 最近项目中要做一个类似天天动听歌曲 ...

  2. Android自定义组件系列【16】——最帅气的自动滚动广告条

    前一段时间要实现一个滚动的广告条,参考了一下网上许多实现,发现实现都很麻烦,所以我决定自己使用ViewFlipper来实现一个,在此将代码贴出来,与大家共享. 转载请说明出处:http://blog. ...

  3. Android recyclerview item获焦时更新UI导致列表自动滚动到获焦处

    问题场景描述:scrollview嵌套rv,rv item里有edittext,当edittext处于获焦状态时,item中有数据改变导致更新UI时,此时列表会自动滚动到获焦处. 分析原因:更新UI时 ...

  4. android+高仿视频录制,android高仿微信视频编辑页

    android高仿微信视频编辑页-视频多张图片提取 上一篇中介绍了有关视频提取图片的知识点,如果对这个不太了解 建议看下android提取视频多张图片和视频信息之前这篇. 这里实现的是仿微信的视频编辑 ...

  5. Android 高仿微信6.0主界面 带你玩转切换图标变色

    目录(?)[+] 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41087219,本文出自:[张鸿洋的博客] 1.概述 学习And ...

  6. android qq红点,Android高仿QQ小红点功能

    先给大家展示下效果图: 绘制贝塞尔曲线: 主要是当在一定范围内拖拽时算出固定圆和拖拽圆的外切直线以及对应的切点,就可以通过path.quadTo()来绘制二阶贝塞尔曲线了~ 整体思路: 1.当小红点静 ...

  7. Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码

    Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码 左右側滑效果图 1.主页的实现 直接将DrawerLayout作为根布局,然后其内部第一个View为内容区域,第二个Vie ...

  8. php支付密码控件,Android高仿微信支付密码输入控件实例代码

    这篇文章主要为大家详细介绍了Android高仿微信支付密码输入控件的具体实现代码,供大家参考,具体内容如下 像微信支付密码控件,在app中是一个多么司空见惯的功能.最近,项目需要这个功能,于是乎就实现 ...

  9. android+qq底部界面,Android 高仿QQ 界面滑动效果

    Android高仿QQ界面滑动效果 点击或者滑动切换画面,用ViewPager实现, 首先是布局文件: android:layout_width="match_parent" an ...

最新文章

  1. 超级干货:3个性能监控和优化命令详解
  2. Jmeter下载、安装、配置 和 使用(一)
  3. 微信图文内容自动同步到腾讯内容开放平台的操作步骤
  4. python如何创建问答窗口_在tkin中创建一个新的单独窗口
  5. 3.4 内置函数(1)
  6. RHEL下安装配置基于2台服务器的MYSQL集群
  7. 从一个表复制到另一个表SQL
  8. Linux搭建虚拟专用,Ubuntu的发行版如何搭建虚拟专用网
  9. 动态加载so库的实现方法与问题处理
  10. 数据库面试题之PL/SQL面试题
  11. 【Codeforces Round #508 (Div. 2)】Slime【简单贪心】
  12. UG NX10.0 软件安装教程
  13. 万能声卡驱动精灵2016官方版
  14. pcm设备的注册流程
  15. mac安装微软服务器系统,mac air安装windows10图文教程
  16. 深圳保障性住房【公租房、安居房、人才房】简单说明
  17. PowerPoint 在播放时自动运行宏
  18. 微信小程序夜间模式,实现更换皮肤,切换白天黑夜模式,简单易懂
  19. 程序员的选择,技术or管理
  20. 结对编程-四则运算-题目去重

热门文章

  1. HTTP1.1新增了五种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 、 CONNECT
  2. android学习之-Style样式的定义
  3. android 动态壁纸
  4. 2.JVM和DVM之间的区别
  5. sql排名名次分页mysql_mysql 实现排名及中文排序实例[分页累加行号]
  6. go conn 读取byte数组后是否要_【技术推荐】正向角度看Go逆向
  7. redis 关系数据库怎么转换 和_redis数据库设计(转)
  8. 操作系统中的死锁_操作系统中的死锁介绍
  9. Java DataOutputStream size()方法及示例
  10. 英特尔核芯显卡控制面板没有了_核显和独显、集成显卡有什么区别