赞
踩
这是一个显示用户当前排位的进度条,使用贝塞尔曲线,上面是用户排位,当我运动时,会前进,走过的部分颜色变蓝色,未走过的部分未灰色。当用户走到曲线尽头时,会刷新,数据点后移,这样可以永远向前移动。效果如下:
黄色点未当前用户,可实时移动。左上角为运动距离,模拟用户移动。当黄色点移动到最右,所有点在曲线上向后移动。
初始化贝塞尔曲线数据
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<>();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。