Android 自定義View畫天氣預報折線圖

效果圖如下:
這裏寫圖片描述

剛開始嘗試用第三方畫曲線的框架來畫效果圖,後來發現曲線間的陰影當有負數的度數的時候畫不出來,而且不需要點擊放大、點點可點的效果,用框架顯得很臃腫,所以最後用自定義View來畫的折線圖。自定義畫折線圖的大致思路:這個圖是有多個四邊形組成的(4個點連接起來就是一個四邊形),兩邊延伸:添加四個多餘的點,將左右的邊距設置成負數即可。

代碼如下:

public class WeatherChartView extends View {

    /**
     * x軸集合
     */
    private float mXAxis[] ;

    /**
     * 白天y軸集合
     */
    private float mYAxisDay[] ;

    /**
     * 夜間y軸集合
     */
    private float mYAxisNight[] ;

    /**
     * x,y軸集合數
     */
    private int LENGTH ;

    /**
     * 白天溫度集合
     */
    private int mTempDay[] ;

    /**
     * 夜間溫度集合
     */
    private int mTempNight[] ;

    /**
     * 控件高
     */
    private int mHeight;

    /**
     * 字體大小
     */
    private float mTextSize;

    /**
     * 圓半徑
     */
    private float mRadius ;

    /**
     * 圓半徑今天
     */
    private float mRadiusToday ;

    /**
     * 文字移動位置距離
     */
    private float mTextSpace ;

    /**
     * 線的大小
     */
    private float mStokeWidth ;

    /**
     * 白天折線顏色
     */
    private int mColorDay = Color.parseColor("#ffffff");

    /**
     * 夜間折線顏色
     */
    private int mColorNight = Color.parseColor("#ffffff");;

    /**
     * 字體顏色
     */
    private int mTextColor = Color.parseColor("#ffffff");;

    /**
     * 屏幕密度
     */
    private float mDensity;

    /**
     * 控件邊的空白空間
     */
    private float mSpace;

    @SuppressWarnings("deprecation")
    public WeatherChartView(Context context, AttributeSet attrs) {
        super(context, attrs); 
        mDensity = getResources().getDisplayMetrics().density;
        mRadius = 3 * mDensity;
        mRadiusToday = 3 * mDensity;
        //mSpace = 3 * mDensity;
        mTextSpace = 10 * mDensity;
        mStokeWidth = 2 * mDensity;
        mTextSize = BreakRuleTools.dip2px(context, 12);
    }

    public WeatherChartView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mHeight == 0) {
            // 設置控件高度,x軸集合
            setHeightAndXAxis();
        }
        // 計算y軸集合數值
        computeYAxisValues();
        // 畫白天折線圖
        drawChart(canvas, mColorDay, mTempDay, mYAxisDay, 0);
        // 畫夜間折線圖
        drawChart(canvas, mColorNight, mTempNight, mYAxisNight, 1);
    }

    /**
     * 計算y軸集合數值
     */
    private void computeYAxisValues() {
        // 存放白天最低溫度
        int minTempDay = mTempDay[0];
        // 存放白天最高溫度
        int maxTempDay = mTempDay[0];
        for (int item : mTempDay) {
            if (item < minTempDay) {
                minTempDay = item;
            }
            if (item > maxTempDay) {
                maxTempDay = item;
            }
        }

        // 存放夜間最低溫度
        int minTempNight = mTempNight[0];
        // 存放夜間最高溫度
        int maxTempNight = mTempNight[0];
        for (int item : mTempNight) {
            if (item < minTempNight) {
                minTempNight = item;
            }
            if (item > maxTempNight) {
                maxTempNight = item;
            }
        }

        // 白天,夜間中的最低溫度
        int minTemp = minTempNight < minTempDay ? minTempNight : minTempDay;
        // 白天,夜間中的最高溫度
        int maxTemp = maxTempDay > maxTempNight ? maxTempDay : maxTempNight;

        // 份數(白天,夜間綜合溫差)
        float parts = maxTemp - minTemp;
        // y軸一端到控件一端的距離
        float length = mSpace + mTextSize + mTextSpace + mRadius;
        // y軸高度
        float yAxisHeight = mHeight - length * 2;

        // 當溫度都相同時(被除數不能爲0)
        if (parts == 0) {
            for (int i = 0; i < LENGTH; i++) {
                mYAxisDay[i] = yAxisHeight / 2 + length;
                mYAxisNight[i] = yAxisHeight / 2 + length;
            }
        } else {
            float partValue = yAxisHeight / parts;
            for (int i = 0; i < LENGTH; i++) {
                mYAxisDay[i] = mHeight - partValue * (mTempDay[i] - minTemp) - length;
                mYAxisNight[i] = mHeight - partValue * (mTempNight[i] - minTemp) - length;
            }
        }
    }

    /**
     * 畫折線圖
     *
     * @param canvas 畫布
     * @param color  畫圖顏色
     * @param temp   溫度集合
     * @param yAxis  y軸集合
     * @param type   折線種類:0,白天;1,夜間
     */
    private void drawChart(Canvas canvas, int color, int temp[], float[] yAxis, int type) {
        color = Color.parseColor("#ffffff");
        // 線畫筆
        Paint linePaint = new Paint();
        // 抗鋸齒
        linePaint.setAntiAlias(true);
        // 線寬
        linePaint.setStrokeWidth(mStokeWidth);
        linePaint.setColor(color);
        // 空心
        linePaint.setStyle(Paint.Style.STROKE);

        // 點畫筆
        Paint pointPaint = new Paint();
        pointPaint.setAntiAlias(true);
        pointPaint.setColor(color);

        // 字體畫筆
        Paint textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setColor(mTextColor);
        textPaint.setTextSize(mTextSize);
        // 文字居中
        textPaint.setTextAlign(Paint.Align.CENTER);

        int alpha1 = 102;
        int alpha2 = 255;
        for (int i = 0; i < LENGTH; i++) {
            // 畫線
            if (i < LENGTH - 1) {
                // 昨天
                if (i == -1) {
                    linePaint.setAlpha(alpha1);
                    // 設置虛線效果
                    linePaint.setPathEffect(new DashPathEffect(new float[]{2 * mDensity, 2 * mDensity}, 0));
                    // 路徑
                    Path path = new Path();
                    // 路徑起點
                    path.moveTo(mXAxis[i], yAxis[i]);
                    // 路徑連接到
                    path.lineTo(mXAxis[i + 1], yAxis[i + 1]);
                    canvas.drawPath(path, linePaint);
                } else {
                    if(type == 0) {
                        linePaint.setAlpha(76);
                        linePaint.setPathEffect(null);

                        linePaint.setStyle(Paint.Style.FILL);//設置實心
                        Path path = new Path();                     //Path對象
                        path.moveTo(mXAxis[i], mYAxisDay[i]);                           //起始點
                        path.lineTo(mXAxis[i + 1], mYAxisDay[i + 1]);                           //連線到下一點
                        path.lineTo(mXAxis[i + 1], mYAxisNight[i + 1]);                      //連線到下一點
                        path.lineTo(mXAxis[i], mYAxisNight[i]);                      //連線到下一點
                        path.lineTo(mXAxis[i], mYAxisDay[i]);                      //連線到下一點
                        canvas.drawPath(path, linePaint);                   //繪製任意多邊形
                    }
                    //canvas.drawLine(mXAxis[i], yAxis[i], mXAxis[i + 1], yAxis[i + 1], linePaint);
                }
            }

            // 畫點
            if (i != 1) {
                // 昨天
                if (i == 0 || i == LENGTH - 1) {
                    /*pointPaint.setAlpha(alpha1);
                    canvas.drawCircle(mXAxis[i], yAxis[i], mRadius, pointPaint);*/
                } else {
                    pointPaint.setAlpha(alpha2);
                    canvas.drawCircle(mXAxis[i], yAxis[i], mRadius, pointPaint);
                }
                // 今天
            } else {
                pointPaint.setAlpha(alpha2);
                canvas.drawCircle(mXAxis[i], yAxis[i], mRadiusToday, pointPaint);
            }

            // 畫字
            // 昨天
            if (i == 0 || i == LENGTH - 1) {
                /*textPaint.setAlpha(alpha1);
                drawText(canvas, textPaint, i, temp, yAxis, type);*/
            } else {
                textPaint.setAlpha(alpha2);
                drawText(canvas, textPaint, i, temp, yAxis, type);
            }
        }
    }

    /**
     * 繪製文字
     *
     * @param canvas    畫布
     * @param textPaint 畫筆
     * @param i         索引
     * @param temp      溫度集合
     * @param yAxis     y軸集合
     * @param type      折線種類:0,白天;1,夜間
     */
    private void drawText(Canvas canvas, Paint textPaint, int i, int[] temp, float[] yAxis, int type) {
        switch (type) {
            case 0:
                // 顯示白天氣溫
                canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] - mRadius - mTextSpace, textPaint);
                break;
            case 1:
                // 顯示夜間氣溫
                canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] + mTextSpace + mTextSize, textPaint);
                break;
        }
    }

    /**
     * 設置高度,x軸集合
     */
    private void setHeightAndXAxis() {
        mHeight = getHeight();
        // 控件寬
        int width = getWidth();
        int i = LENGTH - 2;
        // 每一份寬
        float w = width / (i*2);

        for(int j =0;j<LENGTH;j++){
            if(j == 0){
                mXAxis[j] = 0;
            } else if(j == LENGTH -1){
                mXAxis[j] = width;
            } else{
                mXAxis[j] = w * (2*j -1);
            }
        }
       /* mXAxis[0] = 0;
        mXAxis[1] = w;
        mXAxis[2] = w * 3;
        mXAxis[3] = w * 5;
        mXAxis[4] = w * 7;
        mXAxis[5] = w * 9;
        mXAxis[6] = width;*/
       /* mXAxis[5] = w * 11;
        mXAxis[6] = w * 13;*/
    }

    /**
     * 設置白天溫度
     *
     * @param tempDay 溫度數組集合
     */
    public void setTempDay(int[] tempDay) {
        mTempDay = tempDay;
        LENGTH = mTempDay.length;

        mXAxis = new float[LENGTH];
        mYAxisDay = new float[LENGTH];
        mYAxisNight = new float[LENGTH];
        /*mTempDay = new int[LENGTH];
        mTempNight = new int[LENGTH];*/
    }

    /**
     * 設置夜間溫度
     *
     * @param tempNight 溫度數組集合
     */
    public void setTempNight(int[] tempNight) {
        mTempNight = tempNight;
    }

    /**
     * 設置白天曲線的顏色
     */
    public void setColorDay(){}
}

佈局代碼:

<com.pingan.carowner.weather.view.WeatherChartView
            android:id="@+id/line_char"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            android:layout_below="@id/weather_over_view_item3"
            android:layout_centerInParent="true"/>

代碼引用:

// 設置白天溫度曲線
mChartView = (WeatherChartView) findViewById(R.id.line_char);
mChartView.setTempDay(highTemp);//highTemp 高溫度集合
// 設置夜間溫度曲線
mChartView.setTempNight(lowTemp);//lowTemp 低溫集合
mChartView.invalidate();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章