Android音乐播放器开发小记——功能实现2

项目源码
https://github.com/dogmeng/littleyunmusic
第二部分 自定义控件的实现
主要有主页滑动条MoveLine,播放页面PlayRoundView,歌词页面LrcView,及一些简单的自定义输入框LoginEditText和自定义圆形或圆角矩形CircleImageView的实现.
MoveLine和PlayRoundView的实现过程都用到了贝塞尔曲线(二阶和三阶),相关文章也很多,这里不再一一说明.重点介绍控件的实现思想.
MoveLine:随手指滑动的距离,由弧形变直线再变弧形.也就是说,滑动距离影响控制弧形的三个点的位置.
如图,弧形部分即为moveline

Android音乐播放器开发小记——功能实现2_第1张图片
图片发自App

Android音乐播放器开发小记——功能实现2_第2张图片
图片发自App

Android音乐播放器开发小记——功能实现2_第3张图片
图片发自App

Android音乐播放器开发小记——功能实现2_第4张图片
图片发自App

在MoveLine的onDraw方法中:

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        mPath.reset();
//渐变
        mShader = new LinearGradient(startX, 0, startX, controlY/2, tabColor, themeColor, Shader.TileMode.CLAMP);
        mPaint.setShader(mShader);
//绘制弧形
        mPath.moveTo(startX, 0);
        mPath.quadTo((endX-startX)/2+startX, controlY, endX, 0);
        canvas.drawPath(mPath, mPaint);     
    }
//由外界传入,开始位置,结束位置,和y轴的缩放比例
    public void setPosition(float startX,float endX,float speed){
        this.startX = startX;
        this.endX = endX;
        this.controlY = height*speed;
        invalidate();
    }

因为本项目中的滑动是有页面中的viewpager控制的,所以就在viewpager的滑动监听中,来设置这几个参数

    class MainViewPagerListener implements ViewPager.OnPageChangeListener{
        private int lastPosition = -1;
        @Override
        public void onPageScrollStateChanged(int arg0) {
            // TODO Auto-generated method stub
            //0:挂起 1:正在滑动 2:滑动完毕
        }

        @Override
        public void onPageScrolled(int arg0, final float arg1, int arg2) {
            // TODO Auto-generated method stub
            //agr0:当前页面 arg1:当前页面偏移百分比 arg2:当前页面偏移的像素位置
            //右滑从1到0,position为小的
            if(arg2!=0&&arg2 < lastPosition){
                if(arg1>0.5f){
                    startX = (int) (sideWidth+ tabWidth*(arg0+1)-(tabWidth*(1-arg1)*2));
                    endX = (int) (sideWidth+ tabWidth*(arg0+2));
                    moveLine.setPosition(startX,endX, (2*arg1-1));
                }else if(arg1<=0.5f){
                    startX = (int) (sideWidth+ tabWidth*arg0);
                    endX = (int) (sideWidth+ tabWidth*(arg0+2)-(tabWidth*(1-2*arg1)));
                    moveLine.setPosition(startX,endX, (1-2*arg1));
                }
            }
            //左滑从0到1 突变为0,position为小的,突变为大的
            if(arg2!=0&&arg2 > lastPosition){
                if(arg1<0.5f){
                    startX =(int) (sideWidth+ tabWidth*arg0);
                    endX =(int) (sideWidth+tabWidth*(arg0+1)+(tabWidth*arg1*2));
                    moveLine.setPosition(startX,endX, (1-2*arg1));
                }else if(arg1>=0.5f){
                    endX = (int)(sideWidth+tabWidth*(arg0+2));
                    startX =(int) (sideWidth+ tabWidth*arg0+(tabWidth*(2*arg1-1)));
                    moveLine.setPosition(startX,endX, (2*arg1-1));
                }
            }
            lastPosition = arg2;
        }

PlayRoundView:整体是三个圆形的叠加,圆形的一半沿着随机的方向向外扩展,到一定距离后,出现不规则分布的小点点.在这里,圆形外扩的参数由属性动画来控制,然后对canvas进行随机旋转,这样坐标参数就比较简单,容易控制.


图片发自App

首先初始化画笔和圆形坐标

    private void init(){
        themeColor = ThemeManager.getCurrentColor(context);
//初始化圆形画笔
        mPaint = new Paint();
//初始化小点点画笔
        starPaint = new Paint();
        starPaint.setAntiAlias(true);
        starPaint.setStrokeWidth(10);
        starPaint.setStrokeCap(Cap.ROUND);
        starPaint.setColor(themeColor);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(themeColor);
        mPath = new Path();     
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        defaultwidth = wm.getDefaultDisplay().getWidth();
        defaultheight = wm.getDefaultDisplay().getHeight()*2/3;

        mRadius = defaultwidth/6;
        //初始化圆形坐标
        p5 = new PointF(mRadius * bezFactor,-mRadius);
        p6 = new PointF(0, -mRadius);
        p7 = new PointF(-mRadius * bezFactor, -mRadius);
        
        p0 = new PointF(0, mRadius);
        p1 = new PointF(mRadius * bezFactor, mRadius);
        p11 = new PointF(-mRadius * bezFactor, mRadius);

        p2 = new PointF(mRadius, mRadius * bezFactor);
        p3 = new PointF(mRadius, 0);
        p4 = new PointF(mRadius, -mRadius * bezFactor);

        p8 = new PointF(-mRadius, -mRadius * bezFactor);
        p9 = new PointF(-mRadius, 0);
        p10 = new PointF(-mRadius, mRadius * bezFactor);  
//设置小点点出现的区域
        starRect = new RectF(mRadius, -mRadius, defaultwidth/4, mRadius);
//初始化小点点的集合
        for(int i = 0;i<16;i++){
            starList.add(new PointF());
        }
    }

在onDraw()中进行绘制:

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
//移动坐标中心到圆心     
        canvas.translate(defaultwidth/2, width/2+defaultwidth/8);
        canvas.save();
//旋转坐标轴
        canvas.rotate(rotate);
//如果到达顶点则显示小点点
        if(showStar){
            for(int i = 0;i

属性动画中的move值

    class DragAnimator extends BaseAnimator{
        private float lastValue = 0;
        private int count = 0;
        public DragAnimator(View target, float startValue, float endValue,float thirdValue) {
            super(target, startValue, endValue,thirdValue);
            // TODO Auto-generated constructor stub
        }

        @Override
        protected void doAnim(float animatedValue) {
            // TODO Auto-generated method stub
            move = animatedValue;
            if(move-lastValue<0){
                showStar = true;
            }else{
                showStar = false;
            }
            lastValue = move;
            PlayRoundView.this.invalidate();
        }
    }

LrcView:歌词滚动控件,因目前未实现网路下载功能,所以歌词暂且用测试数据代替,来演示效果.


Android音乐播放器开发小记——功能实现2_第5张图片
图片发自App

歌词的滚动来自两个方面的控制,一是播放时,自动滚动到当前播放行,这个可以用一个handler每隔1秒发送message来检测当前播放时间和歌词所在行时间,如果匹配,就设置当前行为中心行.另一个是用手指滑动来设置当前行.onDraw的重点在于从中心行开始绘制,然后画上半部分和下半部分,这样可以避免绘制无用的页面外的行.绘制过程中根据滑动距离,不断移动横坐标的位置.当移动到下一行后,重置横坐标位置,然后继续绘制.整个过程就是这样反复进行.具体如下:

@Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//重置数据
            if(isFirst){
                distanceY = 0;
                isFirst = false;
            }
            if(isLrc()){
                if(distanceY>0){
                    isTop = true;
                    if(centerLine ==  currentLrc.size()-1){
                        return false;
                    }                   
                }else if(distanceY<0){
                    isTop = false;
                    if(centerLine == 0){
                        offset = 0;
                        m = 0;
                        return false;
                    }
                }
//总移动距离
                mOffset += distanceY;            
                offset = Math.abs(mOffset);
//中心行高度
                int x = (int) staticLayouts.get(centerLine).getHeight();            
                y =  Math.abs(offset-m);
//当移动距离大于两行之间的高度时,重新设置centerLine和y的值
                if(y -(x+textSize)>=0){                                 
                    if(isTop){
                        m = offset;
                        centerLine = centerLine+1 >= currentLrc.size()-1 ? currentLrc.size()-1:centerLine+1;
                    }else{
                        m = offset;
                        centerLine = centerLine-1 <= 0 ? 0:centerLine-1;
                    }
                    y = 0; 
                }else{
                    if(!isTop){
                        y=-y;
                    }
                }
                invalidate();
            }           
            return true;
        }
@Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        canvas.translate(getPaddingLeft(), height/2);
        if(isLrc()){            
                //画中间的 y为正上移,y为负,下移         
                canvas.save();
                canvas.translate(0, -y);
                mContentPaint.setTextSize((float)(textSize*1.1));   
                mContentPaint.setColor(themeColor);
                staticLayouts.get(centerLine).draw(canvas);
                canvas.restore();           
                mContentPaint.setTextSize(textSize);    
                mContentPaint.setColor(getResources().getColor(R.color.toolbarTextColor));
                //画上面的
                if(centerLine>0){
                    int num = centerLine -1;
                    int top = 0;
                    while(top=0){
                        canvas.save();
                        top += 120+staticLayouts.get(num).getHeight();
                        canvas.translate(0, -top-y);
                        staticLayouts.get(num--).draw(canvas);
                        canvas.restore();
                    }                   
                }
                //画下面的
                if(centerLine
}

你可能感兴趣的:(Android音乐播放器开发小记——功能实现2)