简单的模仿,复杂的有点懒,以后无聊再继续写。
这里只简单模仿下ui,至于数据没处理,默认都给个固定的步数,所以步数的线条没处理

第一步
就是简单分析下,都要做啥。
最下边画个灰色的背景,扣掉一个小箭头
完事中间有个固定的黑圈,
再然后就是画日子和步数线条了。我们这里默认宽度分成8份。
最后就是处理触摸事件,移动控件即可,这里自然就是修改要画的东西的x坐标拉。
那就一步一步的来,首先造50条数据。然后就开画
画灰色背景
比较简单了,弄个箭头,然后从canvas里clip掉就ok拉
//draw the date background with an arrow at the center bottom
private fun drawDateBg(canvas: Canvas){
val arrow=space/3;
canvas.save()
val path=Path()
path.moveTo(width/2f,height-arrow)
path.lineTo(width/2f-arrow,height-0f)
path.lineTo(width/2f+arrow,height-0f)
path.close()
canvas.clipPath(path,Region.Op.DIFFERENCE)
paint.color=Color.parseColor("#55000000")
canvas.drawRect(0f,height-radius*2-space,width.toFloat(),height.toFloat(),paint)
canvas.restore()
}
画黑圈
//draw the black circle
paint.color=Color.BLACK
var y=height-radius-space/2
canvas.drawCircle(width/2f,y,radius,paint)
画日期和步数线条
从正中心开画,往左边的是有效数据,最中间的是当天的。
然后再往右边画4个灰色的无效数据。
//draw date
val centerX=width/2f+offsetX+currentChange;
y+=txtBounds.height()/2f
for(i in 0 until datas.size){
val stepBean=datas.get(i)
val x=centerX-intervalRange*i
canvas.drawLine(x,height/10f,x,height-radius*2-space-height/10f,paintLine)
paint.color= Color.BLACK
canvas.drawText(stepBean.getDate(),x,y,paint)
if(x>width/2f-radius-txtBounds.width()/2f&&x<width/2f+radius+txtBounds.width()/2f){
centerP=i
drawCenterDate(stepBean.getDate(),x,y,canvas)
}
if(x<0){
// println("centerX======$centerX===${intervalRange}*${i}")
}
}
val calendar=Calendar.getInstance()
paint.color=Color.GRAY
for(i in 1 ..4){
if(centerX+i*intervalRange>width){
break
}
calendar.add(Calendar.DAY_OF_YEAR,1)
canvas.drawText(StepBean(0, calendar.time).getDate(),centerX+i*intervalRange,y,paint)
}
画在黑圈范围内的白色日期
从动图可以看到,日期文字在黑圈里边的时候是白色的,这个就是以前学的,文字渐变的原理
其实黑色文字照样画,就是上边一步的代码,
完事我们再画个白色的盖在上边,因为裁掉了黑色圆圈以外的部分,所以白色文字可能只有一部分。
private fun drawCenterDate(date:String,x:Float,y:Float,canvas: Canvas){
paint.color=Color.WHITE
canvas.save()
val path=Path().apply { addCircle(width/2f,y-txtBounds.height()/2f,radius,Path.Direction.CCW) }
canvas.clipPath(path)
canvas.drawText(date,x,y,paint)
canvas.restore()
}
处理滑动事件
如果你不想处理fling事件的话,可以直接在ontouch里处理就完事了。逻辑和GestureDetector一样的
就是记录下x方向移动的距离,完事修改onDraw里的x位置就完事了。
完整代码
比较粗糙,就是简单实现下,等以后再增加步数的处理
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.TypedValue
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.widget.OverScroller
import java.util.*
class AllStepsShow : View {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
val datas= arrayListOf<StepBean>()
val paint=Paint(Paint.ANTI_ALIAS_FLAG)
var txtBounds=Rect()
var intervalRange=0;
var radius=20f//the black circle radius
var space=radius*2
var offsetX=0f//the total changed x value
var oldX=0f//the motionevent action down x value
var currentChange=0f//from one touch down ,the change value
val paintLine=Paint(Paint.ANTI_ALIAS_FLAG).apply {
color=Color.BLUE
strokeWidth=8f;
style=Paint.Style.FILL_AND_STROKE
this.strokeCap=Paint.Cap.ROUND
}
var startFling=false;
val overScroller=OverScroller(this.context)
var lastX=0//remember the last x value of scroller
private val gestureListener=object : GestureDetector.SimpleOnGestureListener() {
override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
println("onFling=============${velocityX}")
startFling=true;
lastX=0
overScroller.fling(0,0,velocityX.toInt(),0,-width/2,width/2,0,0)
postInvalidateOnAnimation()
return super.onFling(e1, e2, velocityX, velocityY)
}
override fun onDown(e: MotionEvent): Boolean {
oldX=e.getX();
startFling=false;
offsetX+=currentChange
println("down==============${offsetX}=======$currentChange")
return super.onDown(e)
}
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
println("onScroll======${e1.getX()}/${e2.getX()}==$distanceX")
currentChange=e2.getX()-oldX;
postInvalidateOnAnimation()
return super.onScroll(e1, e2, distanceX, distanceY)
}
}
override fun computeScroll() {
super.computeScroll()
if(startFling){
if(overScroller.computeScrollOffset()){
val current=overScroller.currX
currentChange+=current-lastX
lastX=current
postInvalidateOnAnimation()
}else{
println("computeScroll=================stop")
//fling结束以后进行位置修正
startFling=false;
lastX=0
correctPosition()
}
}
}
init {
val calendar=Calendar.getInstance()
repeat(50){
datas.add(StepBean(100, calendar.time))
calendar.add(Calendar.DAY_OF_YEAR,-1)
}
paint.apply {
style=Paint.Style.FILL
textAlign=Paint.Align.CENTER
textSize=TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,15f,resources.displayMetrics)
getTextBounds("31",0,2,txtBounds)
radius=textSize
space=radius*2
}
println("text size=============${paint.textSize}===${txtBounds.width()}/${txtBounds.height()}==${radius}")
initSomething()
}
lateinit var gesture:GestureDetector
private fun initSomething(){
gesture= GestureDetector(context,gestureListener)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if(changed){
intervalRange=(right-left)/8
}
}
var centerP=0;//the center item's index
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//draw the date bg
drawDateBg(canvas)
//draw the black circle
paint.color=Color.BLACK
var y=height-radius-space/2
canvas.drawCircle(width/2f,y,radius,paint)
//draw date
val centerX=width/2f+offsetX+currentChange;
y+=txtBounds.height()/2f
for(i in 0 until datas.size){
val stepBean=datas.get(i)
val x=centerX-intervalRange*i
canvas.drawLine(x,height/10f,x,height-radius*2-space-height/10f,paintLine)
paint.color= Color.BLACK
canvas.drawText(stepBean.getDate(),x,y,paint)
if(x>width/2f-radius-txtBounds.width()/2f&&x<width/2f+radius+txtBounds.width()/2f){
centerP=i
drawCenterDate(stepBean.getDate(),x,y,canvas)
}
if(x<0){
// println("centerX======$centerX===${intervalRange}*${i}")
}
}
val calendar=Calendar.getInstance()
paint.color=Color.GRAY
for(i in 1 ..4){
if(centerX+i*intervalRange>width){
break
}
calendar.add(Calendar.DAY_OF_YEAR,1)
canvas.drawText(StepBean(0, calendar.time).getDate(),centerX+i*intervalRange,y,paint)
}
}
private fun drawCenterDate(date:String,x:Float,y:Float,canvas: Canvas){
paint.color=Color.WHITE
canvas.save()
val path=Path().apply { addCircle(width/2f,y-txtBounds.height()/2f,radius,Path.Direction.CCW) }
canvas.clipPath(path)
canvas.drawText(date,x,y,paint)
canvas.restore()
}
//draw the date background with an arrow at the center bottom
private fun drawDateBg(canvas: Canvas){
val arrow=space/3;
canvas.save()
val path=Path()
path.moveTo(width/2f,height-arrow)
path.lineTo(width/2f-arrow,height-0f)
path.lineTo(width/2f+arrow,height-0f)
path.close()
canvas.clipPath(path,Region.Op.DIFFERENCE)
paint.color=Color.parseColor("#55000000")
canvas.drawRect(0f,height-radius*2-space,width.toFloat(),height.toFloat(),paint)
canvas.restore()
}
override fun onTouchEvent(event: MotionEvent): Boolean {
gesture?.onTouchEvent(event)
// val x=event.getX()
// when(event.action){
// MotionEvent.ACTION_DOWN->{
// oldX=x;
// offsetX+=currentChange
// }
// MotionEvent.ACTION_MOVE->{
// currentChange=x-oldX;
// }
// MotionEvent.ACTION_UP,MotionEvent.ACTION_CANCEL->{
// currentChange=x-oldX;
// println("currentX======${width/2f+offsetX+currentChange-intervalRange*centerP}===${width/2}")
// currentChange-=width/2f+offsetX+currentChange-intervalRange*centerP-width/2
// }
// }
// postInvalidateOnAnimation()
println("onTouchEvent x=$x====old=${oldX}=====${event.action}")
if(event.action==MotionEvent.ACTION_UP||event.action==MotionEvent.ACTION_CANCEL){
if(!startFling){
correctPosition()//如果没有进行fling操作,那么手指抬起的时候就进行位置修正
}
}
return true
}
//手指松开以后,处理下最终的位置,保证有个item在最中间,位置进行修正
private fun correctPosition(){
//根据centerP的最后位置,减去中心坐标,就是它的偏移量,把这部分去掉,它就回到最中间了。
currentChange-=width/2f+offsetX+currentChange-intervalRange*centerP-width/2
postInvalidateOnAnimation()
}
}
网友评论