显示歌词行数的优化
在我之前的的文章简单音乐播放器的设计(http://blog.csdn.net/xz_studying/article/details/51584309) 中,自定义的歌词View当时是在借鉴的基础上完成的,因此在自己的应用中多少存在一些问题。在后来的不断学习中,发现了一些解决的办法,和大家分享一下。
原歌词的View是:
public class LyricView extends TextView{
private float width; //歌词视图宽度
private float height; //歌词视图高度
private Paint currentPaint; //当前歌词画笔对象
private Paint notCurrentPaint; //非当前歌词画笔对象
private float textHeight = 30; //间隔高度
private float textSize = 18; //文本大小
private int index = 0; //歌词list集合下标
private List<Lyric> mLrcList = new ArrayList<>();
public void setmLrcList(List<Lyric> mLrcList) {
this.mLrcList = mLrcList;
}
public LyricView(Context context) {
super(context);
init();
}
public LyricView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public LyricView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setFocusable(true); //设置可对焦
//当前播放歌词,高亮部分
currentPaint = new Paint();
currentPaint.setAntiAlias(true); //设置抗锯齿,让文字美观饱满
currentPaint.setTextAlign(Paint.Align.CENTER);//设置文本对齐方式
//非当前播放歌词,非高亮部分
notCurrentPaint = new Paint();
notCurrentPaint.setAntiAlias(true);
notCurrentPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if(canvas == null) {
return;
}
currentPaint.setColor(Color.argb(210, 251, 248, 29));
notCurrentPaint.setColor(Color.argb(140, 255, 255, 255));
currentPaint.setTextSize(24);
currentPaint.setTypeface(Typeface.SERIF);
notCurrentPaint.setTextSize(textSize);
notCurrentPaint.setTypeface(Typeface.DEFAULT);
try {
canvas.drawText(mLrcList.get(index).getLcontent(), width / 2, height / 2, currentPaint); //显示当前歌词
float tempY = height / 2;
//画出本句之前的句子
for(int i = index - 1; i >= 0; i--) {
//向上推移
tempY = tempY - textHeight;
canvas.drawText(mLrcList.get(i).getLcontent(), width / 2, tempY, notCurrentPaint);
}
tempY = height / 2;
//画出本句之后的句子
for(int i = index + 1; i < mLrcList.size(); i++) {
//往下推移
tempY = tempY + textHeight;
canvas.drawText(mLrcList.get(i).getLcontent(), width / 2, tempY, notCurrentPaint);
}
} catch (Exception e) {
canvas.drawText("...没发现歌词文件...", width / 2, height/2, notCurrentPaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.width=w;
this.height=h;
}
//设置歌词文件的对应歌词的位置
public void setIndex(int index) {
this.index = index;
}
}
1、此View目前存在的一些问题:
(1)歌词出现的超出了布局中范围,此问题比较明显,在我上次的截图中也可以看到这种情况
(2)太长歌词显示不全的情况
2、现针对(1)问题作出说明
(1)歌词出现的超出了布局中范围,是因为onDraw()中未对绘制文本的范围加以限制,直接绘制完整个歌词,另为让整个Activity的界面采用同一个背景,歌词上、下的其他控件的是透明的,因此歌词也就显示在整个界面中。
3、解决的办法:
(1)根据分析的原因可以有两种解决办法:
<1>为歌词的上、下控件的控件的区域设置非透明的背景,遮挡住相应的歌词,即可显示正常。
<2>如果要设置整体的布局背景的话,则需要控制绘制文本的范围。为保证歌词在不同大小下的自动失配,需要计算文本绘制的具体数据。
4.相关的具体说明:
(1)歌词View通过onSizeChanged()获取控件在布局中的大小,得到的大小值是以像素px为单位,大家可以通过打印查看得到的数据。
绘制文本的单位是sp,在标准大小的情况下,
1sp=1dp=dpi*px。
dpi的获取可通过:
getResources().getDisplayMetrics().density
(2)绘制文本时以下图中的baseLine为基准
因此歌词View中textHeight是距离上次绘制的间隔,而非文本的实际高度,因此textHeight要大于textSize,否则会出现文字重叠。因此可以据此计算出大致每一行歌词的在Y轴的位置,即
tempY+row*textHeight*dpi
据此可计算向上和向下的绘制的行数,以本View计算,
row=Height/2/textHeight/dpi
(4)更改后的代码如下:
public class LyricView extends TextView{
private float width; //歌词视图宽度
private float height; //歌词视图高度
private Paint currentPaint; //当前画笔对象
private Paint notCurrentPaint; //非当前画笔对象
private float textHeight = 50; //间隔高度
private float textSize = 20; //文本大小
private int index = 0; //list集合下标
private float dpi=getResources().getDisplayMetrics().density;
private int row;
private List<Lyric> mLrcList = new ArrayList<>();
public void setmLrcList(List<Lyric> mLrcList) {
this.mLrcList = mLrcList;
}
public LyricView(Context context) {
super(context);
init();
}
public LyricView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public LyricView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setFocusable(true); //设置可对焦
//高亮部分
currentPaint = new Paint();
currentPaint.setAntiAlias(true); //设置抗锯齿,让文字美观饱满
currentPaint.setTextAlign(Paint.Align.CENTER);//设置文本对齐方式
//非高亮部分
notCurrentPaint = new Paint();
notCurrentPaint.setAntiAlias(true);
notCurrentPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if(canvas == null) {
return;
}
row=(int)(height/2/textHeight/dpi);
currentPaint.setColor(Color.argb(210, 251, 248, 29));
notCurrentPaint.setColor(Color.argb(140, 255, 255, 255));
currentPaint.setTextSize(25);
currentPaint.setTypeface(Typeface.SERIF);
notCurrentPaint.setTextSize(textSize);
notCurrentPaint.setTypeface(Typeface.DEFAULT);
try {
float tempY = height / 2;
Paint linePaint=new Paint();
linePaint.setTextSize(16);
linePaint.setColor(Color.argb(210, 251, 248, 29));
canvas.drawText(MusicListAdapter.toTime(mLrcList.get(index).getLyrictime()),width/2, tempY+20, linePaint); canvas.drawText(mLrcList.get(index).getLcontent(), width / 2, tempY, currentPaint);
//画出本句之前的句子
for(int i = index - 1; i >= 0; i--) {
//向上推移
tempY = tempY - textHeight;
if (tempY<height/2-row*textHeight) {
break;
}
canvas.drawText(mLrcList.get(i).getLcontent(), width / 2, tempY, notCurrentPaint);
}
tempY = height / 2;
//画出本句之后的句子
for(int i = index + 1; i < mLrcList.size(); i++) {
//往下推移
tempY = tempY + textHeight;
if (tempY>height / 2+row*textHeight){
break;
}
/*if (tempY>height / 2+5*textHeight){
break;
}*/
canvas.drawText(mLrcList.get(i).getLcontent(), width / 2, tempY, notCurrentPaint);
}
} catch (Exception e) {
canvas.drawText("...没有歌词文件...", width / 2, height/2, notCurrentPaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.width=w;
this.height=h;
}
public void setIndex(int index) {
this.index = index;
}
}