美文网首页Android开发程序员
仿三星S健康步数日趋势图

仿三星S健康步数日趋势图

作者: 有点健忘 | 来源:发表于2018-10-19 10:31 被阅读19次

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


20181019_100951.gif

第一步

就是简单分析下,都要做啥。
最下边画个灰色的背景,扣掉一个小箭头
完事中间有个固定的黑圈,
再然后就是画日子和步数线条了。我们这里默认宽度分成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()
    }

}

相关文章

网友评论

    本文标题:仿三星S健康步数日趋势图

    本文链接:https://www.haomeiwen.com/subject/frfyzftx.html