仿酷狗歌词的滚动效果

先上图:
这里写图片描述

我只是做了个假的效果,真正做的时候需要根据当前歌曲的进度判断歌词扫描的进度;
原理是:1. 自定义一个歌词的view,用来控制每行歌词的扫描进度
2.自定义一个viewGroup,控制歌词的上下滚动
3.通过延时消息控制1和2的交替运行
4.每次滚动,都判断最上面一行有没有到顶部,到顶部则隐藏之
activity的布局就是一个fFrameLayout包裹一个2中定义的自定义ViewGroup,就不贴出来了; activity代码如下:

public class LrcActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lrc);
        findViews();
        initData();
        //首先第一句歌词需要展示在中间
        scrollToCenter();
        //开始滚动歌词
        startScroll();
    }

    private void startScroll() {    
        Message message = Message.obtain();
        message.what = ACTION_SWIPE;
        handler.sendMessage(message);
    }

    private void scrollToCenter() {
        krcListView.post(new Runnable() {
            @Override
            public void run() {
                krcListView.scrollToCenter();
            }
        });
    }

    private void initData() {
        KrcView krcView = null;
        for(int i=0;i<30;i++){
            krcView = new KrcView(strList[i],this);
            krcListView.addView(krcView);
        }
    }

    private void findViews() {
        krcListView = (KrcListView) findViewById(R.id.krcListView);
    }

    private Handler handler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case ACTION_SWIPE:
                    post(swipeRunnable);
                    break;
                case ACTION_SCROLL:
                    post(scrollRunnable);
                    break;
                case ACTION_REMOVE_TOP:
                    krcListView.setTopInvisible(index);
                    break;
                case ACTION_SCROLL_CENTER:
                    krcListView.scrollToCenter();
                    break;
            }
        }
    };


    private Runnable scrollRunnable = new Runnable() {
        @Override
        public void run() {
            index++;
            //遍历每行歌词的画笔颜色
            krcListView.setChildPaint(index);
            if(index >= krcListView.getChildCount()){
                //滚动到最后一行时,恢复到第一行的初始状态
                Message message = Message.obtain();
                message.what = ACTION_SCROLL_CENTER;
                handler.sendMessage(message);
            }else{
                if(index >= KrcListView.MAX_SIZE / 2 - 1){
                //隐藏最上面一行
                    Message message1 = Message.obtain();
                    message1.what = ACTION_REMOVE_TOP;
                    handler.sendMessageDelayed(message1, 500);
                }
                //滚动一行
                krcListView.scrollSingleHeight();
            }
        //发送扫描的消息
            Message message = Message.obtain();
            message.what = ACTION_SWIPE;
            handler.sendMessage(message);
        }
    };

    private Runnable swipeRunnable = new Runnable() {
        @Override
        public void run() {
            index = index >= krcListView.getChildCount() ? 0 : index;
            //找到当前选中的那行,并以每次百分之5的进度扫描
            KrcView krcView = (KrcView)krcListView.getChildAt(index);
            krcView.swipeProgress(swipeProgress);
            krcView.invalidate();

            Message message = Message.obtain();
            if(swipeProgress >= 1.0f){
            //如果该行扫描结束,则开始滚动
                swipeProgress = 0.0f;
                message.what = ACTION_SCROLL;
                handler.sendMessage(message);
            }else{
             //如果该行没有扫描结束,则进度加0.05,继续扫描
                swipeProgress += 0.05f;
                message.what = ACTION_SWIPE;
                handler.sendMessageDelayed(message,100);
            }
        }
    };
    private int index = 0;
    private float swipeProgress = 0.0f;
    private KrcListView krcListView;

    private static final int ACTION_SWIPE = 1;
    private static final int ACTION_SCROLL = 2;
    private static final int ACTION_REMOVE_TOP = 3;
    private static final int ACTION_SCROLL_CENTER = 4;

    private static final String[] strList = new String[]{
            "对这个世界如果你有太多的抱怨",
            "跌倒了就不敢继续往前走",
            "为什麽人要这麽的脆弱 堕落",
            "请你打开电视看看",
            "多少人为生命在努力勇敢的走下去",
            "我们是不是该知足",
            "珍惜一切 就算没有拥有",
            "还记得你说家是唯一的城堡",
            "随着稻香河流继续奔跑",
            "微微笑 小时候的梦我知道",
            "不要哭让萤火虫带着你逃跑",
            "乡间的歌谣永远的依靠",
            "回家吧 回到最初的美好",
            "陈湘制作QQ:123154129",
            "不要这麽容易就想放弃",
            "就像我说的",
            "追不到的梦想",
            "换个梦不就得了",
            "为自己的人生鲜艳上色",
            "先把爱涂上喜欢的颜色",
            "笑一个吧 功成名就不是目的",
            "让自己快乐快乐这才叫做意义",
            "童年的纸飞机",
            "现在终於飞回我手里",
            "所谓的那快乐",
            "赤脚在田里追蜻蜓追到累了",
            "偷摘水果被蜜蜂给叮到怕了",
            "谁在偷笑呢",
            "我靠着稻草人吹着风唱着歌睡着了",
            "哦 哦 午后吉它在虫鸣中更清脆 "
    };

下面是控制滚动的ViewGroup代码,滚动的原理是Scroller:
public class KrcListView extends LinearLayout {

public KrcListView(Context context) {
    this(context,null,0);
}

public KrcListView(Context context, AttributeSet attrs) {
    this(context, attrs,0);
}

public KrcListView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mScroller = new Scroller(context);
    init();
}

public void scrollToCenter() {
//滚动到中心,使第一行的歌词居中
    scrollTo(0, -getHeight() / 2 + getHeight() / (2 * MAX_SIZE));
    int childCount = getChildCount();
    int height = getHeight();
    for (int i = 0; i < childCount; i++) {
        KrcView krcView = (KrcView) getChildAt(i);
        //设置每行歌词的高度
        krcView.getLayoutParams().height = height / MAX_SIZE;
    //滚动到中心时,所有歌词可见(因为滚动一行之后,顶部的歌词会不可见,所以重置一下)
        if(krcView.getVisibility() != VISIBLE)
            krcView.setVisibility(VISIBLE);
        if(krcView.getNoCurrentAlpha() < 255){
            krcView.setNoCurrentAlpha(1.0f);
            krcView.invalidate();
        }
    }
}


@Override
public void computeScroll() {
    super.computeScroll();
    if (mScroller.computeScrollOffset()) {
        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
        invalidate();
    }
}

private void init() {
    setOrientation(VERTICAL);
}

public void setChildPaint(int index) {
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {

        KrcView krcView = (KrcView) getChildAt(i);
        krcView.isCurrent = index == i;

        int distance = Math.abs(index - i);
        //离当前歌词距离越远,画笔越透明
        float alpha = 1.0f - 0.05f * distance;
        krcView.setNoCurrentAlpha(alpha);
        krcView.invalidate();
    }
}


public void scrollSingleHeight() {
    if(getChildCount()==0)
        return;
    int childHeight = getChildAt(0).getHeight();
    mScroller.startScroll(0,getScrollY(),0,childHeight,1000);
    invalidate();
}
/**设置顶部的歌词不可见
**/
public void setTopInvisible(int index) {
    int topIndex = index-5;
    if(topIndex >= 0){
        getChildAt(topIndex).setVisibility(INVISIBLE);
    }
}

private Scroller mScroller;
public static final int MAX_SIZE = 11;

最后是歌词的view代码:

public class KrcView extends View {

    public KrcView(String krc, Context context) {
        super(context);
        this.context = context;
        lineStr = krc;
        init();
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int height = getHeight();
        //根据父布局给他的高度,适配字体大小
        float textSize = height*0.62f;
        noCurrentPaint.setTextSize(textSize);
        currentPaint.setTextSize(textSize);
        currentPassPaint.setTextSize(textSize);

    //计算字体高度,ondraw时drawText用到,详细可见我之前的博客
        Paint.FontMetrics fontMetrics = currentPassPaint.getFontMetrics();
        textHeight = (int) Math.ceil(fontMetrics.bottom - fontMetrics.top);
        bottomY = fontMetrics.bottom;
        textWidth = (int) currentPassPaint.measureText(lineStr);
    }

    public void setNoCurrentAlpha(float alpha) {
        noCurrentPaint.setAlpha((int) (alpha*256));
    }

    public int getNoCurrentAlpha() {
        return noCurrentPaint.getAlpha();
    }

    public void swipeProgress(float swipeProgress) {
        isCurrent = true;
        progress = swipeProgress;
    }

    private void init() {
       //初始化非当前歌词的画笔
        noCurrentPaint = new TextPaint();
        noCurrentPaint.setAntiAlias(true);
        noCurrentPaint.setColor(Color.parseColor("#ffffff"));
        noCurrentPaint.setTextAlign(Paint.Align.CENTER);
        //初始化当前歌词的画笔
        currentPaint = new TextPaint();
        currentPaint.setAntiAlias(true);
        currentPaint.setColor(Color.parseColor("#ffffff"));
        currentPaint.setTextAlign(Paint.Align.CENTER);
        //初始化当前歌词扫描的画笔(黄色)
        currentPassPaint = new TextPaint();
        currentPassPaint.setAntiAlias(true);
        currentPassPaint.setColor(Color.parseColor("#ffffff00"));
        currentPassPaint.setTextAlign(Paint.Align.CENTER);

    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //如果是当前的歌词,则需要画两边,一遍是普通的,一遍是扫描的
        if(isCurrent){
            canvas.drawText(lineStr, getWidth() / 2, getHeight() / 2 + textHeight / 2 - bottomY, currentPaint);
            canvas.save();
            canvas.clipRect(0, 0, getWidth() / 2 - textWidth / 2 + textWidth * progress, getHeight());
            canvas.drawText(lineStr,getWidth()/2,getHeight() / 2 + textHeight / 2-bottomY,currentPassPaint);
            canvas.restore();
        }else{
            canvas.drawText(lineStr,getWidth()/2,getHeight() / 2 + textHeight / 2-bottomY,noCurrentPaint);
        }
    }


    private String lineStr;
    private int textHeight;
    private int textWidth;
    private float bottomY = 0;

    private TextPaint currentPaint;
    private TextPaint currentPassPaint;
    private TextPaint noCurrentPaint;

    private float progress = 0.0f;
    public boolean isCurrent = true;
    private Context context;


}
Redrain仿酷狗音乐播放器     这篇文章只是对开源的说明!关于这个Redrain音乐盒的发布程序的说明和使用方法,见《Redrain仿酷狗音乐播放器开发完毕,发布测试程序》。    今天,我把这个项目的源代码上传。包括了可以编译工程所需的所有代码文件,已经软件的布局文件,但是没有包括软件需要的素材,这也是为了避免引起与酷狗播放器的版权问题。        我在这个项目中使用的是 UiLib 库而不是 DuiLib 库,UiLib库是DuiLib库的扩展版本,增加了一些动画控件的支持,扩展了部分控件,但是核心代码并没有改变,与DuiLib使用方法完全一样,也可以用UiLib库直接编译使用 DuiLib 库编写的代码。项目中的 UiLib 是我为了适应仿酷狗而专门修改过的,也修复过必要的 bug,所以如果你使用原版的 DuiLib 或者 UiLib 库去编译这个功能,最终的程序效果和我发布的不一样。关于 bug的修复请看博客中更早期的文章。我自己使用并且维护的DuiLib库和UiLib库的下载地址见博客:《Redrain个人维护并使用的DuiLib和UiLib库源码下载地址》       在这个项目源码中,同时包含了webkit内核浏览器控件、音乐播放类、换肤功能、拖拽功能、菜单等等。这个菜单的功能如下:         1、可以展现多级菜单         2、可内嵌自定义控件,并且控件可以向主窗体发送消息,如图的红色叹号就是个按钮控件,可以制作酷狗音乐的托盘菜单的播放暂停按钮和进度控制进度条。         3、菜单拥有阴影效果         4、菜单可以自定义前方显示小图标,并且可以控制图标的大小和是否显示         5、菜单可以根据是否拥有子菜单决定是否显示小箭头         6、菜单可以添加分割线         7、每个菜单项都可以单选和复选的功能         8、优化菜单的xml描述文件,编写方便容易,如果要写一个二级菜单,比如编写图片中的菜单测试4以及他的子菜单,只需如下代码就可以了         9、可以通过键盘的按钮控制菜单的选项         10、每个菜单项的高度宽度是任意调整的 在这个项目中,还有一些未实现的功能,但是我并不打算继续完成这些功能了:      1、退出程序时逐渐缩小的动画特效      2、各个菜单的响应      3、歌词功能(已经有网友做出来了,我就不另外做了)      4、嵌入桌面的桌面歌词 注意 此项目开源代码只是为了学习交流,不可用于商业程序,源码对使用者造成的损失,概不负责! 源码下载地址:点击打开链接 via http://blog.csdn.net/zhuhongshu/article/details/41037875
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值