柠檬跑步app怎样连接网络

请选择您要上传的头像图片,完成后关闭此弹框即可。
&&&&您所提交的评论已被保存,登录后即可发表
&&&&您已经选择过了。
应用大小:54.6 MB
类别:体育
当前版本:1.0.1
语言:简体中文
运行环境:需要 iOS 8.0 或更高版本。与 iPhone、iPad 和 iPod touch 兼容。
下载此APP的用户也下载
简介:「柠檬跑步」 —— 摆脱时间、地点、人物的羁绊...
柠檬跑步应用说明
「柠檬跑步」 —— 摆脱时间、地点、人物的羁绊,让我们跑起来跑步中经常遇到的问题:- 一人跑步太孤单,只能拿音乐来慰藉- 组局同跑不容易,不是我忙就Ta忙- 约跑全程无交流,感觉还是一人跑「虚拟约跑」摆脱时间、地点、人物的羁绊,与跑者约跑起来。通过语音播报互动提升约跑体验「运动记录」精准跑步路线记录,帮你记录每一公里「数据管理」云端存储数据,不用担心设备更换造成数据丢失「跑步社交」跑房功能设计,为跑者提供交流社群注意事项 & Attention-由于跑步功能具有后台语音提醒,切换到后台播报时仍会保持GPS连接。语音播报可以帮助用户在切换至后台时会播报速度等相关参数,但是GPS在后台持续运行会影响电池续航时间。-持续使用跑步GPS在后台可能会衰减您的电池寿命。Continued use of GPS running in the background can dramatically decrease battery life.如需要帮助或获取更多信息,请关注或联系:微博@柠檬跑微信公众号「柠檬跑步」邮箱
柠檬跑步怎么样
| | | | | | |
| | | | | | | |
|||||||||||||||
iTunes分类排行榜
||||||||||||||||||
京公网安备98
苹果园为iOS用户提供和下载,最新的、、、等,分享最权威的资讯、、及解决办法,拥有最火爆的,苹果园一家专注解决iOS所求的网站。
15:10:15.860---- 15:10:15.860----0柠檬跑步是一款专为爱好跑步的朋友们打造的手机应用,用户在这里能够的了解与保存自己的数据,查看自己的情况,实用!
柠檬跑步软件功能:
- 一人跑步太孤单,只能拿来慰藉
- 组局同跑不容易,不是我忙就Ta忙
- 约跑全程无,感觉还是一个人跑
「虚拟约跑」
摆脱、地点、人物的羁绊,与跑者约跑起来。通过播报提升约跑体验
准确跑步路线记录,帮你记录每一公里
「数据管理」
云端存储数据,不用担心设备更换造成数据丢失
跑房功能设计,为跑者提供交流社群
更新说明:
[准确gps]准确记录运动路线
[云储存数据]运动数据更
[社交新方式]跑房创新跑步玩法
[虚拟约跑]让跑步不再孤独
安卓版下载
苹果版下载
电脑版下载
本站仅创建用户沟通交流的信息平台,所展示的游戏/软件内容来均来自于第三方用户上传分享,资源仅作为用户间分享讨论之用,除开发商授权外不以盈利为目的。如果侵犯了您的权益,请反馈,我们将第一时间处理。
1葫芦侠我的世界v1.1.8趣味娱乐
2导学号v2.11.1学习理财
3礼包助手v2.2系统工具
4哔咔哔咔漫画v1.4.4系统工具
5奋科影院v1.0.0影音播放
6喵街v2.1.1生活服务
7借贷宝v2.5.5学习理财
8盒子鱼英语v3.5.4学习理财
9看看电视V1.0.1影音播放
10河西力量v1.0.5生活服务
热门软件:
最新软件:
拍照摄影:柠檬跑步:跑步轨迹回放动画实现(与咕咚类似) - 简书
柠檬跑步:跑步轨迹回放动画实现(与咕咚类似)
7月-12--39.gif
1、轨迹动画流畅,慢-快-慢;
2、渐变色尽量与地图渐变API的效果一致;
3、拖动地图,动画消失,显示完整渐变轨迹。
1、高德地图并没有提供相应效果的API,但是可以通过经纬度坐标,转换未屏幕坐标,因此可以自定义一个View来实现轨迹动画的效果。(注意:在自定义的View上画轨迹,一定是要在地图缩放完成后执行,有对应的回调方法,API可查)
2、自定义控件这里有两种思路,可以继承自View,也可以继承SurfaceView。他们的区别相信大家都清楚,我的解决方案中使用了自定义的属性动画,所以我是通过View来实现的。欢迎大家提供SurfaceView的解决方案,共同学习。
3、渐变肯定是用Shader来进行实现,但是这里有一个误区,不能对整个运动轨迹的path设置渐变,Shader的渐变不会跟着你的轨迹走,所以只能分段设置渐变色,相信高德也是这么搞的。
4、动画效果的实现是用的属性动画,这里也有多种实现方式,最初我用了一种比较愚蠢的方案,对每一段path设置动画,通过AnimationSet进行队列展示,但是没考虑到界面渲染效率的问题,导致界面卡顿。
View的渲染大家都清楚,每次invalidate都会导致界面重画,所以我的方案也很简单,通过动画进度,可以算出当前动画执行到的path,根据比例截取,进行绘制。
5、至于相关的相应事件就没什么了,实现方案很多,个人认为最简单的就是在自定义View中通过onTouchEvent处理逻辑并进行回调。
三、相关代码
1、轨迹动画相关数据的工具类
package com.lemon.running.
import android.graphics.LinearG
import android.graphics.P
import android.graphics.PathM
import android.graphics.P
import android.graphics.S
import com.lemon.running.LemonA
import java.util.ArrayL
* Created by viva on 17/7/5.
public class RecordPathAnimUtil {
private final long MAX_ANIM_DURATION = 5 * 1000;//动画最大执行时间
private final long MIN_ANIM_DURATION = 2 * 1000;
private final int SCREEN_WIDTH_DEBUG = 1080;//当前调试手机的屏幕宽度,作为计算动画执行时间的标准,无实际意义
private int SCREEN_WIDTH_RELEASE;//用户使用手机屏幕的实际宽度
private long ANIM_DURATION = MIN_ANIM_DURATION;//动画执行总时间
private final float PATH_SCREEN_LENGTH_1_KM = 2000.0f;
private ArrayList&RecordPathBean& recordPathL
private PathMeasure pathM
private Path totalP
public RecordPathAnimUtil(){
recordPathList = new ArrayList&&();
SCREEN_WIDTH_RELEASE = ScreenUtils.getScreenWidth(LemonApplication.getContext());
public long getANIM_DURATION() {
return ANIM_DURATION;
public void setANIM_DURATION(long ANIM_DURATION) {
this.ANIM_DURATION = ANIM_DURATION;
public ArrayList&RecordPathBean& getRecordPathList() {
return recordPathL
* 创建坐标点对应的path 渐变
* @param start
* @param end
* @param startColor
* @param endColor
public void addPath(Point start,Point end,int startColor,int endColor){
if (totalPath == null){
totalPath = new Path();
totalPath.moveTo(start.x,start.y);
totalPath.lineTo(end.x,end.y);
totalPath.lineTo(end.x,end.y);
Path path = new Path();
path.moveTo(start.x,start.y);
path.lineTo(end.x,end.y);
pathMeasure = new PathMeasure(path,false);
Shader shader = new LinearGradient(start.x, start.y, end.x, end.y,new int[]{startColor,endColor},null, Shader.TileMode.CLAMP);
RecordPathBean recordPathBean = new RecordPathBean(path,pathMeasure.getLength(),shader);
recordPathBean.setEndPoint(end);
recordPathBean.setEndColor(endColor);
recordPathList.add(recordPathBean);
recordPathBean.setIndex(recordPathList.size() - 1);
* 所有path的总长度
public float getAllPathLength(){
float pathLength = 0;
if (recordPathList != null){
for (int i = 0,count = recordPathList.size();i &i++){
pathLength += recordPathList.get(i).getPathLength();
caculateAnimDuration(pathLength);
return pathL
* 计算动画执行的总时长
* @param pathLength
private void caculateAnimDuration(float pathLength){
float pathScreenLength1KmRelease = SCREEN_WIDTH_RELEASE * PATH_SCREEN_LENGTH_1_KM / SCREEN_WIDTH_DEBUG;
float durationScale = pathLength / pathScreenLength1KmR
if (durationScale &= 1)
long durationRelease = (long) (durationScale * MIN_ANIM_DURATION);
if (durationRelease &= MAX_ANIM_DURATION){
setANIM_DURATION(MAX_ANIM_DURATION);
setANIM_DURATION(durationRelease);
public Path getTotalPath() {
return totalP
public class RecordPathBean{
private P//路径
private S//画笔渐变
private float pathL
private Point endP
private int endC
public RecordPathBean(Path path,float pathLength,Shader shader){
this.path =
this.pathLength = pathL
this.shader =
public Path getPath() {
public Shader getShader() {
public float getPathLength() {
return pathL
public int getIndex() {
public void setIndex(int index) {
this.index =
public Point getEndPoint() {
return endP
public void setEndPoint(Point endPoint) {
this.endPoint = endP
public int getEndColor() {
return endC
public void setEndColor(int endColor) {
this.endColor = endC
2、自定义控件
package com.lemon.running.ui.
import android.animation.A
import android.animation.TypeE
import android.animation.ValueA
import android.content.C
import android.graphics.B
import android.graphics.BitmapF
import android.graphics.C
import android.graphics.C
import android.graphics.P
import android.graphics.P
import android.graphics.PathM
import android.util.AttributeS
import android.view.MotionE
import android.view.V
import android.view.animation.AccelerateDecelerateI
import com.lemon.running.R;
import com.lemon.running.utils.RecordPathAnimU
import java.util.ArrayL
* Created by viva on 17/6/20.
public class RecordPathView extends View {
private Paint paint, iconP
private Path dstPath, totalP
private PathMeasure mPathMeasure, mDstPathM
private boolean isDrawRecordPath =
private float pathL
private Bitmap startIcon, endIcon, middleI
private float[] pathStartPoint = new float[2];
private float[] pathEndPoint = new float[2];
private float[] dstPathEndPoint = new float[2];
private float value = 0;
private long ANIM_DURATION;
private ArrayList&RecordPathAnimUtil.RecordPathBean& recordPathL
private OnAnimEnd onAnimE
private int animI
public RecordPathView(Context context) {
super(context);
this.context =
public RecordPathView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context =
public RecordPathView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context =
private void init() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.argb(0, 0, 0, 0));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
iconPaint = new Paint();
iconPaint.setAntiAlias(true);
dstPath = new Path();
startIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.outside_run_record_start_point);
endIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.outside_run_record_stop_point);
middleIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.speed_view_point);
public void setPath(RecordPathAnimUtil recordPathAnimUtil) {
if (recordPathAnimUtil == null)
if (!isDrawRecordPath) {
pathLength = recordPathAnimUtil.getAllPathLength();
ANIM_DURATION = recordPathAnimUtil.getANIM_DURATION();
recordPathList = recordPathAnimUtil.getRecordPathList();
totalPath = recordPathAnimUtil.getTotalPath();
mPathMeasure = new PathMeasure(totalPath, false);
mPathMeasure.getPosTan(0, pathStartPoint, null);//轨迹的起点
mPathMeasure.getPosTan(mPathMeasure.getLength(), pathEndPoint, null);//轨迹的终点
if (recordPathList == null || recordPathList.size() == 0)
startPathAnim();
isDrawRecordPath =
public void setOnAnimEnd(OnAnimEnd onAnimEnd) {
this.onAnimEnd = onAnimE
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (recordPathList == null || recordPathList.size() == 0)
if (animIndex & 0){
for (int i = 0; i & animI i++) {
RecordPathAnimUtil.RecordPathBean recordPathBean = recordPathList.get(i);
paint.setColor(recordPathBean.getEndColor());
paint.setShader(recordPathBean.getShader());
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(recordPathBean.getPath(), paint);
paint.setShader(null);
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(recordPathBean.getEndPoint().x, recordPathBean.getEndPoint().y, 5, paint);
paint.setStyle(Paint.Style.STROKE);
paint.setShader(recordPathList.get(animIndex).getShader());
paint.setStrokeWidth(10);
canvas.drawPath(dstPath, paint);
canvas.drawBitmap(startIcon, pathStartPoint[0] - startIcon.getWidth() / 2, pathStartPoint[1] - startIcon.getHeight() / 2, iconPaint);
if (value &= 1) {
canvas.drawBitmap(endIcon, pathEndPoint[0] - endIcon.getWidth() / 2, pathEndPoint[1] - endIcon.getHeight() / 2, iconPaint);
canvas.drawBitmap(middleIcon, dstPathEndPoint[0] - middleIcon.getWidth() / 2, dstPathEndPoint[1] - middleIcon.getHeight() / 2, iconPaint);
private void caculateAnimPathData(){
float length = value * pathL
float caculateLength = 0;
float offsetLength = 0;
for (int i = 0,count = recordPathList.size();i &i++){
caculateLength += recordPathList.get(i).getPathLength();
if (caculateLength & length){
animIndex =
offsetLength = caculateLength -
dstPath.reset();
PathMeasure pathMeasure = new PathMeasure(recordPathList.get(animIndex).getPath(),false);
pathMeasure.getSegment(0, recordPathList.get(animIndex).getPathLength() - offsetLength, dstPath, true);
mDstPathMeasure = new PathMeasure(dstPath, false);
mDstPathMeasure.getPosTan(mDstPathMeasure.getLength(), dstPathEndPoint, null);
private void startPathAnim() {
ValueAnimator animator = ValueAnimator.ofObject(new DstPathEvaluator(), 0, mPathMeasure.getLength());
animator.setDuration(ANIM_DURATION);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
value = (float) animation.getAnimatedValue();
caculateAnimPathData();
invalidate();
animator.addListener(new Animator.AnimatorListener() {
public void onAnimationStart(Animator animation) {
public void onAnimationEnd(Animator animation) {
if (onAnimEnd != null)
onAnimEnd.animEndCallback();
public void onAnimationCancel(Animator animation) {
public void onAnimationRepeat(Animator animation) {
animator.start();
class DstPathEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
public interface OnAnimEnd {
void animEndCallback();
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
if (Math.abs(event.getX() - x) & 0 || Math.abs(event.getY() - y) & 0) {
if (onAnimEnd != null)
onAnimEnd.animEndCallback();
注:本文只是提供一种实现方案,其实针对于跑步路径过长的情况,这样处理还是会有跳帧的问题。毕竟代码是死的,人是活的,针对这种情况也有多种优化方案,需要与产品、设计的要求找到一个平衡。
我们自己测试的情况,当小段path的数量大概到达2000的时候,就会跳帧。那么在渲染的时候就需要两种解决方案,可以从渐变上考虑,可以从绘制方式上考虑等。
优化方案请看下一篇文章:
最后给我们产品做个广告吧:
WechatIMG26.jpeg
用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Android 获取 View 宽高的常用正确方式,避免为零 - 掘金相信有很多朋友...
用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Android 获取 View 宽高的常用正确方式,避免为零 - 掘金 相信有很多...
用到的组件1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SDWebImage多个缩略图缓存组件 UICKeyChainStore存放用户账号密码组件 Reachability监测网络状态 DateTools友好...
发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注
09:45字数 61697阅读 3316评论 2喜欢 85 用到的组件 1、通过CocoaPods安装 项目名称 项目信息 AFNetworking 网络请求组件 FM...
用到的组件1、通过CocoaPods安装项目名称项目信息AFNetworking网络请求组件FMDB本地数据库组件SDWebImage多个缩略图缓存组件UICKeyChainStore存放用户账号密码组件Reachability监测网络状态DateTools友好化时间MBP...
忘了是从什么时候开始,我习惯默默努力,不对结果抱有期待。我想要是还没有得到,要么说明当下的我还不适合,要么说明我不够努力。 想起高考前,我万分焦虑,不仅担心高考,还担心考完之后家人不同意我想报的专业。当我把自己心中的苦闷向语文老师倾诉之后,她说,还没考你就在担心选什么专业,...
前两天和某人争执了一番,主要原因在于理财观念的不同:他更注重于实际,认为财富积累只能通过攒工资,以及努力工作涨工资的方式;而我认为在攒到第一笔初始资金后,可以通过投资理财的方式,实现家庭资金的稳定增长。带来的后果就是:他对于攒第一笔初始资金的态度,并不像我那么积极和在乎,偶...
1 最近琼瑶奶奶对身后事做出了交代,预约自己的“尊严死”。她的公开信中,有这样一段话: “你们不论多么不舍,不论面对什么压力,都不能勉强留住我的躯壳,让我变成“求生不得,求死不能”的卧床老人!那样,你们才是“大不孝”!” 琼瑶年近八十,健康状况良好。之所以在网络发表公开信,...
时常我会在想什么是好的爱情,亦或者我要什么样的爱情,其实就是让你自在不做作的人,你可以想笑就笑,可以想哭就哭,你可以放下你所有的圆滑,只是看着对方就特别安心。。 而恰恰这个人,无限的包容你做自己,哪怕你呆萌又大条的有点傻,哪怕你脆弱敏感又有些自恋,可是在他的眼里,你就是一个...
每次写都要重新去找代码 好烦啊 两个小时又搭进去了 先说导入,需要两个文件: 程序如下: 再说导出,只需要一个文件: 程序如下: 一般的导入导出程序就需要这两个文件了,文件夹那么多,看着就头疼,网上的教程也是乱七八糟,好无奈...柠檬跑步-小米应用商店 手机应用 &
&柠檬跑步查看详细 软件大小:28.02 M版本号:1.9.0更新时间:包名:com.lemon.runningappId:430911权限详情:? (基于网络的)粗略位置? 精准的(GPS)位置? 查看网络状态? 查看 Wi-Fi 状态? 创建蓝牙连接? 蓝牙管理? 拍摄照片和视频? 更改 Wi-Fi 状态? 完全的互联网访问权限? 检索当前运行的应用程序? 更改您的音频设置? 装载和卸载文件系统? 读取手机状态和身份? 开机时自动启动? 录音? 修改/删除 SD 卡中的内容? 控制振动器? 防止手机休眠手机屏幕截图
应用介绍跑步中经常遇到的问题:
- 一人跑步太孤单,只能拿音乐来慰藉
- 组局同跑不容易,不是我忙就Ta忙
- 约跑全程无交流,感觉还是一个人跑
「虚拟约跑」
摆脱时间、地点、人物的羁绊,与跑者约跑起来。通过语音播报互动提升约跑体验
「运动记录」
精准跑步路线记录,帮你记录每一公里
「数据管理」
云端存储数据,不用担心设备更换造成数据丢失
「跑步社交」
跑房功能设计,为跑者提供交流社群
如需要帮助或获取更多信息,请关注或联系:
微博@柠檬跑
微信公众号「柠檬跑步」
邮箱
QQ群 新版特性1.9 changelog
程序猿大哥一脸傲娇地跟我保证,&产品性能更加稳定了&,还挑衅道,&欢迎来找茬儿!&
1、优化了动态发布的流程
2、优化了部分页面的交互细节
3、修复了一些已知bug你所举报的信息将被发送至工信部12321举报平台您需要登陆小米帐号才可以安装应用到手机已经将“新浪微博”推送到您的手机请在手机上完成安装}

我要回帖

更多关于 柠檬网络电视 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信