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
- Android-自定义View-竞赛进度条
- android 圆形进度条 自定义view
- android 自定义view 圆形进度条
- Android自定义View---圆形进度条
- Android 仪表进度条 自定义View
- Android自定义view 圆环进度条
- Android-------自定义View圆形进度条
- Android自定义view(圆形进度条)
- Android自定义view圆形进度条
- Android自定义view圆形进度条
- Android自定义View-圆形进度条
- Android自定义View画圆+进度条+自定义View梯形
- Android自定义View之实现环形进度条
- 自定义View实现Android圆形进度条
- android 自定义View实现进度条增长
- Android自定义View圆环带文字进度条
- Android 圆弧形进度条 自定义View
- Android 圆弧形进度条 自定义View
- 13-14-15-16-面向对象、继承、封装、struct和class
- SDUT-3526 团战可以输,提莫必须死(BFS)
- unity开发之七:unity2017自带高通ar使用方法(填坑)
- 括号匹配算法的java实现
- php laravel 阿里云对象存储 多图上传 前端手机端页面遍历输出
- Android-自定义View-竞赛进度条
- getchar
- 解决Docker启动服务器链接失败-Job for docker.service failed because the control process exited error code
- Map的value最大长度值
- 递归实现费氏数列:0,1,1,2,3,5,8,13,21,34,55,89,... ...
- java通过JDBC链接SQLServer2012
- 倒计时封装 程序进入后台不会暂停计时器
- React Native入门(十一)之屏幕适配
- Error:Unable to find method 'com.android.build.gradle.api.BaseVariant.getOutputs()Ljava/util/List;'.