当前位置:   article > 正文

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

android pk进度条

写在前面

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

这里写图片描述

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

详细实现

初始化贝塞尔曲线数据

    private void initPointList(){
        mPointFList.clear();

        mPointFList.add(new PointF(mMarginLeftRight,mHeight/2));
        mPointFList.add(new PointF(mWidth- mMarginLeftRight,mHeight/2));

        mBezierLineDataList = getLineData(mPointFList);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

绘制贝塞尔曲线

    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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

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

    /**
     * 设置曲线数据
     * @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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

每次只显示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);
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

绘制圆点

    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);
            }

        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

更新用户进度

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

    /**
     * 更新运动进度
     * @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
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

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;
            }
        }
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

一些全局变量

    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<>();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/804450
推荐阅读
相关标签
  

闽ICP备14008679号