在开发当中我们时常会自己定义View对象,今天我写了一个View对象,来实现了歌词的滚动,因为知识不全面,所以之前或者现在的代码都有一定的缺陷,在以后会慢慢修改,,现在只是为了了解大概框图,然后不断学习,废话不多说代码实现
程序入口
package com.example.viewdemo;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在写xml文件的时候,我们自己定义的View一定要记得引入包名
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<com.example.viewdemo.LrcView
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
自定义View' 代码注解里面应该说的很清楚了
package com.example.viewdemo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import org.xml.sax.InputSource;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class LrcView extends View {
private Paint paint;
private int text_x,text_y;
private int width,height;
private int refreshIndex;
private int currentLrcIndex;
private ArrayList<String> lrcs;
private ArrayList<Integer> lrcTimes;
private int move_y;
private int move_count;
//3个构造方法,前两个用于xml解析,第三个是用于java构建对象
public LrcView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//通过为最多的参数设置初始化,然后让其它的都直接调用它
init();
}
public LrcView(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public LrcView(Context context) {
this(context,null);
}
private void init() {
//创建画笔
paint=new Paint();
paint.setTextSize(30);
//获取字体的大小,字体是正方形的
float TextSize=paint.getTextSize();
text_x=0;
text_y=(int) TextSize;
//每次取得歌词时,向上移动一个字的高度
move_y=(int) TextSize;
//歌词初始值
currentLrcIndex=-1;
refreshIndex=-2;
//读取歌词
try {
readLrc();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
//时间一般由多媒体框架返回,但是这里我们是测试,所以我们就使用线程的开始时间
long musicStart=System.currentTimeMillis();
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//计算应该放哪一句歌词
long currentTime=System.currentTimeMillis();
long duration=currentTime-musicStart;
int lrcDataLength=lrcTimes.size();
//从所有的歌词中找到当前时间应该显示的歌词的下标
for(int i=0;i<lrcDataLength;i++){
int time=lrcTimes.get(i);
System.out.println("time="+time);
System.out.println("duration="+duration);
//找到第一个大于我们当前的时间点,那么前一个就是我们需要找的歌词
if(time>duration){
currentLrcIndex=i-1;
if(currentLrcIndex!=-1&&refreshIndex!=currentLrcIndex){
//调用postInvalidata()方法,这个方法回毁掉onDraw方法绘制试图
postInvalidate();
//下标更新
refreshIndex=currentLrcIndex;
}
break;
}
}
}
}
}).start();
}
//重写onLayout方法,为了获得容器的宽和高
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
// TODO Auto-generated method stub
super.onLayout(changed, left, top, right, bottom);
width=right-left;
height=bottom-top;
}
//重写onDraw方法,实现视图的展示
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int lrcShowLength=lrcs.size();
for(int i=0;i<lrcShowLength;i++){
if(i==currentLrcIndex){
paint.setColor(Color.BLUE);
}else{
paint.setColor(Color.BLACK);
}
//取出歌词
String lrc=lrcs.get(i);
//重新获取画笔的大小
float textSize=paint.getTextSize();
float[] widths=new float[lrcs.size()];
int number=paint.getTextWidths(lrc, 0, lrc.length()-1, widths);
int textwidth=0;
for (float w : widths) {
textwidth+=w;
}
//设置文字的锚点x坐标
text_x=width/2-textwidth/2;
//设置文字的锚点的y坐标
int text_y_offset=(int) (i*textSize);
int text_time_offset=move_y*move_count;
text_y=(int) (height/2+textSize/2+text_y_offset+text_time_offset);
canvas.drawText(lrc, text_x, text_y, paint);
}
//每绘制一次,我们的坐标就像上移动一次
move_count--;
}
private void readLrc() throws IOException {
lrcs=new ArrayList<String>();
lrcTimes=new ArrayList<Integer>();
//由于每个View的构造器都有一个context,那么在LrcView中也会有一个context
Context context=getContext();
//assests中的文件不会被转换成为2进制
AssetManager assetsManager=context.getAssets();
InputStream in=assetsManager.open("text.txt");
InputStreamReader isr=new InputStreamReader(in);
BufferedReader bReader=new BufferedReader(isr);
String lineStr = null;
bReader.readLine() ;
//
while ((lineStr = bReader.readLine()) != null) {
// 不为空,则能读取到数据
String timeStr = lineStr.substring(1, 9);
String lrcStr = lineStr.substring(10);
// System.out.println("timeStr = "+timeStr);
// System.out.println("lrcStr = "+lrcStr);
//解析 00:00.00
String min = timeStr.substring(0, 2) ;
String sec = timeStr.substring(3, 5) ;
String ms = timeStr.substring(6) ;
//使用封装类,将数字组成的字符串,转换为数字
int minI = Integer.valueOf(min) ;
int secI = Integer.valueOf(sec) ;
int msI = Integer.valueOf(ms) ;
int lrcTime = minI*60*1000 + secI*1000 + msI*10 ;
System.out.println("lrcTime = "+lrcTime);
//添加时间点
lrcTimes.add(lrcTime) ;
//添加歌词
lrcs.add(lrcStr) ;
}
}
}
需要解析的lrc歌词文件,自己可以去看,格式是差不多的,这里主要是提供了一种思路