android高仿天天动听,Android仿天天动听歌曲自动滚动view

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

815f5a07ea228d28b36c451ec4ebd40a.png

正文

想法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"

/>

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

36aad75db16a6d1a9063eebdbc3065fb.gif

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/547483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详解UML中的聚合,关联,泛化等关系

1. Overview UML设计类中&#xff0c;类的关系分为Generalization(泛化)&#xff0c;Dependency(依赖关系)、Association(关联关系)、Aggregation(聚合关系)、Composition(组合关系)五种! 2. Generalization(泛化) Generalization(泛化)表现为继承或实现关系(is a)。具体形式为…

CentOS 8 安装 Docker  报错  requires containerd.io >= 1.4.1, but none of the providers can be installed

CentOS 8.1安装 Docker 官方参考地址&#xff1a;https://docs.docker.com/install/linux/docker-ce/centos/ 里面包含包下载地址&#xff1a;https://download.docker.com/linux/centos/8/x86_64/stable/Packages/containerd.io-1.4.3-3.1.el8.x86_64.rpm 一。确认CentOS 版…

android.support.v7 fragme,打造最强RecyclerView侧滑菜单,长按拖拽Item,滑动删除Item

前几天写了一片关于RecyclerView滑动删除Item&#xff0c;RecyclerView长按拖拽Item的博客&#xff0c;本来很简单一个使用&#xff0c;阅读量还挺高的&#xff0c;原博客传送门。今天介绍一个RecyclerView Item侧滑菜单&#xff0c;RecyclerView滑动删除Item&#xff0c;Recyc…

有关PHP、HTML单引号、双引号转义以及转成HTML实体的那些事!

一、单引号和双引号转义在PHP的数据存储过程中用得比较多&#xff0c;即往数据库里面存储数据时候需要注意转义单、双引号&#xff1b; 先说几个PHP函数&#xff1a; 1、addslashes — 使用反斜线引用&#xff08;转义&#xff09;字符串&#xff1b; 返回字符串&#xff0c;…

centos 8 安装使用配置

服务端安装nfs 1、使用yum安装nfs yum install nfs-utils nfs-utils-lib -y 如果出现上述错误请安装lvm2 yum install -y lvm2 2、编辑文件exports vim /etc/exports 加入代码&#xff0c;如&#xff1a; /home *(insecure,rw,sync,no_root_squash) #参数详解 ro #只读共享…

2s相机 android6,Android Camera2 使用总结

最近在做自定义相机相关的项目&#xff0c;网上查了资料都是有关android.hardware.Camera的资料&#xff0c;开始使用的才发现这个类已经废弃了。Android 5.0(21)之后android.hardware.Camera就被废弃了&#xff0c;取而代之的是全新的android.hardware.Camera2 。Android 5.0对…

CentOS 7上搭建Spark3.0.1+ Hadoop3.2.1分布式集群

CentOS 7上搭建Spark3.0.1 Hadoop3.2.1分布式集群 VMWare 安装CentOS 7使用Xshell连接虚拟机集群设置安装JDK 1.8SSH 免密登陆安装hadoop 3.2安装Spark 3.0.1总结VMWare 安装CentOS 7 推荐使用VMware Workstation Pro 16&#xff0c;下载安装即可。下载最新的CentOS 7 Minimal…

再见安卓 你好鸿蒙,安卓,再见!你好,鸿蒙系统!

今年9月份&#xff0c;华为宣布鸿蒙OS操作系统将面向手机发布&#xff0c;今年12月份正式开放开发者Beta版本。昨天&#xff0c;华为鸿蒙OS迎来里程碑式的新进展&#xff0c;开发者Beta版本如约而至&#xff0c;正式开启线上公测招募&#xff0c;我们也可以可以一睹鸿蒙系统真容…

Kubernetes 部署 Traefik Ingress 控制器 (1.7.12)

目录[-] . 一、Ingress 介绍. 二、Traefik 介绍. 三、部署 Ingress 控制器 Traefik. 1、Traefik 两种部署方式介绍. 2、创建 Traefik 配置文件. 3、将 Traefik 配置文件挂载到 ConfigMap. 4、设置 CA 证书. 5、给节点设置 Label. 6、创建 Traefik 服务账户与角色权限. 7、创建…

Git本地缓存问题 修改密码后git无法拉取

Git本地缓存问题 修改密码后git无法拉取 问题描述&#xff1a;使用正确的用户名和密码可以登录到Git代码仓库&#xff0c;但是在本地无法使用Git bash命令行的方式拉取代码。 问题原因&#xff1a;第一次使用Git bash方式拉取代码时&#xff0c;会根据当前的用户和密码生成一串…

Ext.Net常用方法

1、js&#xff08;Ext&#xff09;操作 Ext.Msg.alert(系统提示, 未连接血站&#xff0c;该功能暂时不能使用。); Ext.getCmp("id").getValue();Ext.getCmp("id").focus();Ext.getCmp("id").selectText(); //选中修改 if (!GridPanel1.hasSelec…

Pytorch 版YOLOV5训练自己的数据集

1、环境搭建 https://github.com/ultralytics/yolov5 2、安装需要的软件 pip install -U -r requirements.txt 3、准备数据 在data文件下建立上面三个文件&#xff08;Annotations、images与ImageSets&#xff0c;labels后续我们脚本生成&#xff09;其中Annotations存放xml…

使用SQL Server作业设置定时任务

1.开启SQL Server Agent服务 使用作业需要SQL Agent服务的支持&#xff0c;并且需要设置为自动启动&#xff0c;否则你的作业不会被执行。 以下步骤开启服务:开始-->>>运行-->>>输入"services.msc"-->>>进入服务,开启SQL Server Agent服…

ListView与GridView优化

前言 ListView是Android中最常用的控件&#xff0c;通过适配器来进行数据适配然后显示出来&#xff0c;而其性能是个很值得研究的话题。本文与你一起探讨Google I/O提供的优化Adapter方案&#xff0c;欢迎大家交流。 声明 欢迎转载&#xff0c;但请保留文章原始出处:) 博客园&…

2021安徽舒城中学高考成绩查询,2021安徽省地区高考成绩排名查询,安徽省高考各高中成绩喜报榜单...

怀宁中学芜湖2017年芜湖市高考文科头名是来自于芜湖师大附中的唐逸云&#xff0c;高考成绩653分。2017年芜湖市高考理科头名是来自于芜湖师大附中的茅志鹏&#xff0c;高考成绩676分。师大附中芜湖一中淮南淮南二中文科考生最高分631(市应届生第一名)&#xff0c;理科考生最高分…

jsonp的简单学习

前言&#xff1a;说到AJAX就会不可避免的面临两个问题&#xff0c;第一个是AJAX以何种格式来交换数据&#xff1f;第二个是跨域的需求如何解决&#xff1f;这两个问题目前都有不同的解决方案&#xff0c;比如数据可以用自定义字符串或者用XML来描述&#xff0c;跨域可以通过服务…

win7台式计算机怎么连热点,怎么用台式电脑开热点,电脑怎么开热点win7

导读&#xff1a;手机是每个人的随身携带之物&#xff0c;手机的功能让越来越多的人痴迷其中&#xff0c;也正是因为这个原因&#xff0c;手机流量的消耗也越来越多&#xff0c;购买流量让我们花费了很多金钱&#xff0c;我们该如何解决这个问题呢?下面我们就一起来看看电脑的…

WebLogic11g-创建域(Domain)及基本配置

2019独角兽企业重金招聘Python工程师标准>>> 最近看到经常有人提问weblogic相关问题&#xff0c;所以闲暇之际写几篇博文&#xff08;基于weblogic11&#xff09;&#xff0c;仅供大家参考。 具体weblogic的介绍以及安装&#xff0c;这里就不赘述了。 以域的创建开篇…

计算机教室规则英语作文,班级规则初中英语作文

【导语】无规则不成方圆&#xff0c;班级亦是如此&#xff0c;也拥有属于它的规章制度。下面是无忧考网为你整理的有关班级规则初中英语作文&#xff0c;希望你喜欢!【篇一】班级规则初中英语作文Students should get to school for a half-hour reading before 7:30. Everyone…