1. 程式人生 > >Android 自定義控制元件 (一) ,柱狀圖 ,Canvas 繪製 柱狀圖 ,支援觸控操作

Android 自定義控制元件 (一) ,柱狀圖 ,Canvas 繪製 柱狀圖 ,支援觸控操作

專案中,經常會用到統計圖表,個性化展示資料,增加趣味性,之前也用過百度Echarts來展示,效果很不錯,包括一些互動操作,不得不說,echarts幫我我們實現了絕大多數的需求,體積小不說,實現方式也很簡單,後來想了想,為什麼不用安卓Canvas繪製呢,畢竟是安卓開發攻城獅,下面就用Canvas繪製一個自帶動畫和觸控事件的柱狀圖

先來張效果圖:

       效果還是不錯的 , 這裡我主要是添加了動畫,以便於更加直觀的瞭解柱狀圖的整個繪製過程  

       自定義View當然少不了測量寬高,確定繪製區域了,老樣子,還是在onLayout ( ) 中獲取控制元件的尺寸,確定下邊界 , 我這裡是預留了些距離

 @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            startX = getPaddingLeft() + basePadding;
            endX = getMeasuredWidth() - getPaddingRight() - basePadding;
            startY = getMeasuredHeight() - getPaddingBottom() - basePadding * 3;
            endY = getPaddingTop() + basePadding * 4;
        }
    }

        繪製區域確定以後 ,不著急繪製 , 先來分析下好了 ,Canvas繪製矩形應該是輕車熟路,主要靠的還是動畫 , 這樣在繪製圖表的時候,整個繪製過程才會更加細膩,平滑 ;  這裡我用的是 ValueAnimator ,  ValueAnimator.ofFloat(0, 1.0f) 意思是從0到1漸變 ,想要獲取實時的動畫值,可以通過設定動畫的更新監聽ValueAnimator.AnimatorUpdateListener , 拿到當前的mAnimatedValue , 每個矩形的最大值 * mAnimatedValue 就是當前矩形的高度了

private void darwRect(Canvas canvas) {
        float dx = getDx();
        float dy = (startY - endY - basePadding) / 100;
        for (int i = 0; i < datas.size(); i++) {
            RectF rectf = new RectF(startX + dx * i, startY - datas.get(i).y * dy * (isUserAnimator ? mAnimatedValue : 1), startX + dx * (i + 1), startY);
            rectPaint = new Paint(basePaint);
            if (datas.get(i).y > 90 || datas.get(i).y <= 10) {
                rectPaint.setColor(ContextCompat.getColor(getContext(), labelColors[2]));
                touchColors.put(i, labelColors[2]);
            } else if (datas.get(i).y >= 60) {
                rectPaint.setColor(ContextCompat.getColor(getContext(), labelColors[1]));
                touchColors.put(i, labelColors[1]);
            } else {
                rectPaint.setColor(ContextCompat.getColor(getContext(), labelColors[0]));
                touchColors.put(i, labelColors[0]);
            }
            canvas.drawRect(rectf, rectPaint);
        }
    }

       柱狀圖繪製還是挺簡單的 , 但是現在問題來了 , 如果使用者在手指觸控的過程中, 想得到些UI上的反饋 , 一個柱狀圖肯定有若干個矩形組成的 , 比如說觸控時我想知道自己點了哪一個矩形 ,那應該怎麼做呢 ?

       監聽觸控操作,當然是重寫View的onTouchEvent方法了 , 但是現在還有有一個問題 , 就是如何確定手指處於哪一個矩形上面?

       首先,我們得確定下每個矩形的寬度了, 這裡我是繪製了9個矩形,所以單個矩形的寬度就是 getDx() 即 (endX - startX) / 9 ; onTouchEvent中可以拿到當前觸控的xy座標 , 每次ACTION_MOVE事件中 , 可以計算moveX的值 (moveX - startX) / getDx() 就是當前處於哪一個矩形上面 , 這裡我們需要取 int 型別的數值 , 剛好與資料來源的index吻合 ,如果資料長度過短,可能會索引越界,可以對index進行判斷 if (index >= datas.size()) index = datas.size() - 1;

private void drawOnTouch(Canvas canvas) {
        //這裡獲取int整型數值 ,剛好與資料來源的索引吻合 ,如果資料長度過短,可能會索引越界,可以對index進行判斷
        int index = (int) ((moveX - startX) / getDx());
        if (index >= datas.size()) index = datas.size() - 1;
        float y = datas.get(index).y;

        float dx = getDx();
        float dy = (startY - endY - basePadding) / 100;

        float x0 = startX + index * dx;
        float x1 = x0 + 0.5f * dx;
        float y1 = startY - y * dy;

        //畫矩形
        canvas.drawRect(x0, y1, x0 + dx, startY, touchPaint);
        Paint p = new Paint(touchPaint);
        p.setTextSize(dip2px(15));
        p.setColor(ContextCompat.getColor(getContext(), touchColors.get(index)));
        canvas.drawText(String.valueOf(y), x1, y1 - basePadding / 2, p);

//        canvas.drawLine(x1, startY, x1, endY, touchPaint);//輔助線 Y
//        canvas.drawLine(startX + 2 * basePadding, y1, endX, y1, touchPaint);//輔助線 X
//
//        //畫指示點
//        Paint paint = new Paint(touchPaint);
//        paint.setColor(Color.WHITE);
//        paint.setStrokeWidth(dip2px(9.5f));
//        canvas.drawPoint(x1, y1, paint);//畫圓點
//
//        paint.setColor(Color.BLACK);
//        paint.setStrokeWidth(dip2px(6.5f));
//        canvas.drawPoint(x1, y1, paint);//畫圓點
    }

  到這裡柱狀圖的繪製就完成了 , 主要還是確定觸控的索引和平滑繪製 ,