Android-自定义View-竞赛进度条

来源:互联网 发布:2016欧洲杯数据 编辑:程序博客网 时间:2024/04/30 22:51

写在前面

这是一个显示用户当前排位的进度条,使用贝塞尔曲线,上面是用户排位,当我运动时,会前进,走过的部分颜色变蓝色,未走过的部分未灰色。当用户走到曲线尽头时,会刷新,数据点后移,这样可以永远向前移动。效果如下:

这里写图片描述

黄色点未当前用户,可实时移动。左上角为运动距离,模拟用户移动。当黄色点移动到最右,所有点在曲线上向后移动。

详细实现

初始化贝塞尔曲线数据

    private void initPointList(){        mPointFList.clear();        mPointFList.add(new PointF(mMarginLeftRight,mHeight/2));        mPointFList.add(new PointF(mWidth- mMarginLeftRight,mHeight/2));        mBezierLineDataList = getLineData(mPointFList);    }

getLineData();

    /**获取每一段曲线所需要的点集*/    private List<BezierLineData> getLineData(List<PointF> pointList){        float t = 0.5f;        List<BezierLineData> lineDataList = new ArrayList<>();        PointF startP;        PointF endP;        PointF cp1;        PointF cp2;        BezierLineData lineData;        for (int i = 0; i<pointList.size() - 1;i ++){            startP = pointList.get(i);            endP = pointList.get(i+1);            cp1 = new PointF();            cp1.x = startP.x + (endP.x-startP.x) * t;            cp1.y = mMarginTopBottom - DensityUtil.dip2px(mContext,30f);            cp2 = new PointF();            cp2.x = startP.x + (endP.x-startP.x) * (1 - t);            cp2.y = mHeight - mMarginTopBottom + DensityUtil.dip2px(mContext,30f);            lineData = new BezierLineData(startP,endP,cp1,cp2);            lineDataList.add(lineData);        }        return lineDataList;    }

绘制贝塞尔曲线

    private void drawBezier(Canvas canvas){        mLinePaint.setColor(mContext.getResources().getColor(R.color.competition_line_alpha_gray));        mLinePaint.setStyle(Paint.Style.STROKE);        mLinePaint.setStrokeWidth(DensityUtil.dip2px(mContext,1.5f));//设置线宽        mLinePaint.setAntiAlias(true);//去除锯齿        mLinePaint.setStrokeJoin(Paint.Join.ROUND);        mLinePaint.setStrokeCap(Paint.Cap.ROUND);        //绘制曲线        //Path lightLinePath = new Path();        mBezierPath.reset();        mBezierPath.moveTo(mBezierLineDataList.get(0).getStartP().x,mBezierLineDataList.get(0).getStartP().y);        for (int i=0;i<mBezierLineDataList.size();i++){            mBezierPath.cubicTo(                    mBezierLineDataList.get(i).getCp1().x,mBezierLineDataList.get(i).getCp1().y,                    mBezierLineDataList.get(i).getCp2().x,mBezierLineDataList.get(i).getCp2().y,                    mBezierLineDataList.get(i).getEndP().x, mBezierLineDataList.get(i).getEndP().y            );        }        float gradientFactor = 0f;//比例,根据用户当前位置设置渐变色的起始,用户左边为蓝色,右边为灰色        //求用户所在位置占总距离的比例        for (CompetitionData data: mCompetitionDataList){            if (data.getCompetitionUserType()== CompetitionUserType.ME) {                gradientFactor = data.getDistance()/mMaxDistance;                break;            }        }        LinearGradient linearGradient = new LinearGradient(                0,                0,                mWidth*gradientFactor,                0,                mGradientColor,                null,                Shader.TileMode.CLAMP        );        mLinePaint.setShader(linearGradient);        canvas.drawPath(mBezierPath,mLinePaint);    }

通过外部设置一些排行数据

    /**     * 设置曲线数据     * @param competitionDataList 竞赛数据列表     * 用户数据排在第一*/    public void setLineData(List<CompetitionData> competitionDataList){        //获取最大值        mCompetitionDataList = competitionDataList;        if (mCompetitionDataList.size() <= 4){            mCurDataIndex = mCompetitionDataList.size()-1;        }else {            mCurDataIndex = 4;        }        mMaxDistance = mCompetitionDataList.get(mCurDataIndex).getDistance();        mMaxDistance = mMaxDistance + mMaxDistance*0.1f;        getShowedDataList();//显示4个数据        postInvalidateDelayed(50);    }

CompetitionData类

/** * Created by allever on 17-9-20. */public class CompetitionData {    private float distance;//运动距离    private CompetitionLine.CompetitionUserType competitionUserType;//用户类型:ME当前用户, OTHER:其他用户    private int rank;//排名    public CompetitionData(CompetitionLine.CompetitionUserType competitionUserType , float distance, int rank){        this.rank = rank;        this.competitionUserType = competitionUserType;        this.distance = distance;    }    public float getDistance() {        return distance;    }    public void setDistance(float distance) {        this.distance = distance;    }    public CompetitionLine.CompetitionUserType getCompetitionUserType() {        return competitionUserType;    }    public void setCompetitionUserType(CompetitionLine.CompetitionUserType competitionUserType) {        this.competitionUserType = competitionUserType;    }    public int getRank() {        return rank;    }    public void setRank(int rank) {        this.rank = rank;    }}

每次只显示5个记录(我+其他比我大的最接近的四个用户)

    /**     * 只获取显示四个数据*/    private void getShowedDataList(){        mShowedDataList.clear();        mShowedDataList.add(mCompetitionDataList.get(0));//第一个,用户        int fromIndex = 1;        Log.d(TAG, "getShowedDataList: mCurDataIndex = " + mCurDataIndex);        int counter = 0;        for (int i=mCurDataIndex;i>0;i--){            if (counter < 4){                fromIndex = i;                counter ++;            }        }        Log.d(TAG, "getShowedDataList: fromIndex = " + fromIndex);        for (int j=fromIndex;j<=mCurDataIndex;j++){            if (j < mCompetitionDataList.size()){                mShowedDataList.add(mCompetitionDataList.get(j));                Log.d(TAG, "getShowedDataList: j = " + j);            }        }    }

绘制圆点

    private void drawMark(Canvas canvas){        if (mCompetitionDataList.size() == 0) return;        //绘制圆形与排行        Bitmap bitmap;        CompetitionData competitionData;        Log.d(TAG, "onDraw: mMaxDistance = " + mMaxDistance);        float t;//点在曲线上的比例        BezierLineData lineData;        PointF linePoint;        for (int i= 0; i<mShowedDataList.size(); i++){            competitionData = mShowedDataList.get(i);            lineData = mBezierLineDataList.get(0);//            t = competitionData.getDistance()/mMaxDistance;            //或取曲线上的点坐标            linePoint = BezierUtil.calculateBezierPointForCubic(                    t,                    lineData.getStartP(),                    lineData.getCp1(),                    lineData.getCp2(),                    lineData.getEndP());            if (competitionData.getCompetitionUserType() == CompetitionUserType.ME){                //是当前用户,绘制Bitmap                bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.run_video_piont);                canvas.drawBitmap(bitmap,                        linePoint.x - (bitmap.getWidth()/2),                        linePoint.y -(bitmap.getHeight()/2),                        mImagePaint);            }else {                //其他用户绘制圆点和排名                float r = DensityUtil.dip2px(mContext,10f);                mLinePaint.setStyle(Paint.Style.FILL);                canvas.drawCircle(                        linePoint.x,                        linePoint.y,                        r,mLinePaint);                //绘制排名                String rank = competitionData.getRank()+"";                float dx;                float dy = DensityUtil.dip2px(mContext,4f);                if (competitionData.getRank() <10){                    dx = DensityUtil.dip2px(mContext,3f);                }else if (competitionData.getRank() >= 10 && competitionData.getRank() < 100){                    dx = DensityUtil.dip2px(mContext,6f);                }else {                    dx = DensityUtil.dip2px(mContext,8f);                }                canvas.drawText(rank,0,rank.length(),                        linePoint.x - dx,                        linePoint.y + dy,                        mRankPaint);            }        }    }

更新用户进度

通过外部方法更新用户运动距离,当用户距离小于该曲线上显示用户的最大距离,仅触发重绘,否则触发滚动。

    /**     * 更新运动进度     * @param distance 用户当前运动距离*/    public void updateMyPoint(float distance){        CompetitionData myData = null;        for (int i=0; i<mCompetitionDataList.size();i++){            myData = mCompetitionDataList.get(i);            if (myData.getCompetitionUserType() == CompetitionUserType.ME){                myData.setDistance(distance);                mCompetitionDataList.set(i,myData);                break;            }        }        //postInvalidateDelayed(50);        if (myData == null) return;        if (myData.getDistance() > mCompetitionDataList.get(mCurDataIndex).getDistance()){            //获取下一目标者的运动距离            if (mCurDataIndex+1 < mCompetitionDataList.size()){                mNextDistance = mCompetitionDataList.get(mCurDataIndex+1).getDistance();                //触发滚动                //mHandler.sendEmptyMessageDelayed(MESSAGE_MOVE,100);                mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL,100);            }else {                //此时,用户超越当前列表所有数据                Log.d(TAG, "updateMyPoint: max = " + mMaxDistance+"\t" + "mNextMax = " + mNextDistance);                if (myData.getDistance() > mNextDistance){                    mNextDistance = myData.getDistance() + mMaxDistance * 0.3F;//当用户超越当前排行榜列表用户,再运动一定距离后,向后滚动一定距离                    //触发滚动                    //mHandler.sendEmptyMessageDelayed(MESSAGE_MOVE,100);                    mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL,100);                }else {                    postInvalidateDelayed(50);                }            }        }else {            postInvalidateDelayed(50);        }    }    public enum CompetitionUserType{        ME,OTHER    }

Handler,曲线上点数据不变,而总距离自增,从而造成数据点向后滚动。

    private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            //super.handleMessage(msg);            switch (msg.what){                case MESSAGE_SCROLL:                    if (mMaxDistance < mNextDistance){                        mMaxDistance ++ ;                        invalidate();                        sendEmptyMessage(MESSAGE_SCROLL);                    }else {                        mMaxDistance = mNextDistance;                        if (mCurDataIndex < mCompetitionDataList.size()-1){                            mCurDataIndex++;                        }                        getShowedDataList();//显示4个数据                        invalidate();                    }                    break;            }        }    };

一些全局变量

    private List<CompetitionData> mCompetitionDataList = new ArrayList<>();//全部数据    private List<CompetitionData> mShowedDataList = new ArrayList<>();//可以展示的数据,    private int mCurDataIndex = 0;//用来记录当前最大距离    private float mNextDistance;//下一目标者运动距离    //private float mDx = 0;//曲线每次移动偏移量    private List<PointF> mPointFList = new ArrayList<>();    private List<BezierLineData> mBezierLineDataList = new ArrayList<>();
阅读全文
0 0
原创粉丝点击