您的位置:首页 > 移动开发 > Android开发

Android中歌词显示的实现

2015-05-03 09:10 435 查看
之前想写一个音乐播放器,但是一直不明白歌词应该怎么显示。

用UI Automator观察了一下各大音乐播放器,发现QQ音乐和百度音乐是在ScrollView里显示歌词。不清楚ScrollView里面具体是什么,都支持滑动到某个位置之后跳转播放

Flyme 4自带音乐是在ListView里显示,内部嵌套了多个TextView,一行一行显示歌词,支持滑动到某个位置之后跳转到该位置播放(魅族的UI做得的确很不错)

MIUI 6自带音乐的实现方式略奇葩,在ScrollView里嵌套了三个TextView,分别显示之前的所有歌词,当前显示的歌词和之后的所有歌词。实现起来很容易,但是效果不够理想,也不支持跳转播放

还有其他的实现方式,比如在自定义View里重载onDraw方法,自己绘制歌词……这样比较精确,但要支持滑动之后跳转播放的话难度很大。

还是用ScrollView实现吧……

定义一个LyricView类,用来实现歌词显示及滑动到某个位置之后跳转播放

大概思路是这样:在ScrollView中套一个LinearLayout,LinearLayout中放置三个View:

1.一个空白的View,高度是LyricView高度的一半 2.GridView,只有一列,用来显示歌词内容,但不能滑动 3.一个空白的View,高度是LyricView高度的一半

实现滑动后跳转到某个位置播放,要获取GridView里面每一个TextView的高度,然后通过当前所在位置计算现在在LyricView正中间的是哪一行,也就是要跳转到的行

如果布局文件已经在XML中定义好,要获取一个View的高度,直接调用其getWidth()和getHeight()方法就可以了。但是这里每一个TextView都是动态显示的,没有在XML中进行定义。而且每个TextView的高度可能会随歌词长度变化而变化。

最大的难点在于,每个View的实际高度只能在ViewGroup的onLayout方法执行完毕之后才能获取。然而直接获取GridView或ListView的适配器中,getView方法返回的View的高度是不正确的。(这时候获取的高度都是0,因为getView方法返回的对象实际上还没有被显示。)

要获取每个View的正确高度,要通过android.view.ViewTreeObserver类实现。

源码中对ViewTreeObserver的说明:

/**
* A view tree observer is used to register listeners that can be notified of global
* changes in the view tree. Such global events include, but are not limited to,
* layout of the whole tree, beginning of the drawing pass, touch mode change....
*
* A ViewTreeObserver should never be instantiated by applications as it is provided
* by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()}
* for more information.
*/


注册监听器,用于监听view tree中的全局变化。这样的全局事件包括但不限于整个view树的显示,绘制过程的开始和触控模式变化

ViewTreeObserver对象不能通过应用进行实例化,因为它是通过view层次提供的。

获取View对应的ViewTreeObserver,然后添加ViewTreeObserver.OnGlobalLayoutListener可实现在onLayout执行完毕之后,获取View的正确大小。

重写GridView的Adapter中的getView方法,添加OnGlobalLayoutListener获取每行高度。

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final TextView textView = new TextView(context);
textView.setText(strs.get(position));
//设置每行的文字居中
textView.setGravity(Gravity.CENTER);

//当前位置的文本设为黑色
if(position == currentpos){
textView.setTextColor(Color.BLACK);
}

//获取TextView对应的ViewTreeObserver
final ViewTreeObserver vto = textView.getViewTreeObserver();
//添加OnGlobalLayoutListener,在TextView显示之后获取宽度,高度
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
textView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height = textView.getHeight();
int width = textView.getWidth();
heights[position] = height;
}
});
return textView;
}
同样的方法也可以用在其他View上。

直接将GridView嵌套进LinearLayout的话,只能看到一行内容。

第二个难点在于,如何让GridView不滑动,也就是平铺显示所有内容。

重载GridView的onMeasure方法:

public class NoScrollGridView extends GridView {
public NoScrollGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public NoScrollGridView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandMeasureSpec);
}
}


过了5个月终于把代码传上去了= =

Demo: https://github.com/entalent/LyricViewDemo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: