Android-自定義View-競賽進度條

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/devallever/article/details/78356124

寫在前面

這是一個顯示用戶當前排位的進度條,使用貝塞爾曲線,上面是用戶排位,當我運動時,會前進,走過的部分顏色變藍色,未走過的部分未灰色。當用戶走到曲線盡頭時,會刷新,數據點後移,這樣可以永遠向前移動。效果如下:

這裏寫圖片描述

黃色點未當前用戶,可實時移動。左上角爲運動距離,模擬用戶移動。當黃色點移動到最右,所有點在曲線上向後移動。

詳細實現

初始化貝塞爾曲線數據

    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<>();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章