MPAndroidChart系列源码解读(四)

由于个人能力有限,不能再跟着仔细看源码了,感觉每个类关联甚多,决定直接从chart下手了

public abstract class Chart<T extends ChartDataextends IDataSetextends Entry>>> extends
        ViewGroup
        implements ChartInterface {

 public Chart(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
  }

 protected void init() {

        setWillNotDraw(false);//如果自定义View重新onDraw方法不执行,调用该方法可以解决问题
        // setLayerType(View.LAYER_TYPE_HARDWARE, null);

        if (android.os.Build.VERSION.SDK_INT < 11)//图表的动画走版本分之
            mAnimator = new ChartAnimator();
        else
            mAnimator = new ChartAnimator(new AnimatorUpdateListener() {

                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // ViewCompat.postInvalidateOnAnimation(Chart.this);
                    postInvalidate();
                }
            });

        //下面这些相关类在前面两篇blog都或多或少的有提到
        // initialize the utils
        Utils.init(getContext());

        mDefaultFormatter = new DefaultValueFormatter(1);

        mViewPortHandler = new ViewPortHandler();

        mLegend = new Legend();

        mLegendRenderer = new LegendRenderer(mViewPortHandler, mLegend);

        mXAxis = new XAxis();

        // 初始化画笔配置
        mDescPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDescPaint.setColor(Color.BLACK);
        mDescPaint.setTextAlign(Align.RIGHT);
        mDescPaint.setTextSize(Utils.convertDpToPixel(9f));

        mInfoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mInfoPaint.setColor(Color.rgb(247, 189, 51)); // orange
        mInfoPaint.setTextAlign(Align.CENTER);
        mInfoPaint.setTextSize(Utils.convertDpToPixel(12f));

        mDrawPaint = new Paint(Paint.DITHER_FLAG);

        if (mLogEnabled)
            Log.i("", "Chart.init()");
    }
}

自定义ViewGroup,不同的Chart传入不同的数据Data,实现ChartInterface接口,在前面的博客有提到具体方法含义。setData方法调用更新数据集,刷新视图

 public void setData(T data) {

        if (data == null) {
            Log.e(LOG_TAG,
                    "Cannot set data for chart. Provided data object is null.");
            return;
        }

        // LET THE CHART KNOW THERE IS DATA
        mOffsetsCalculated = false;
        mData = data;

        // calculate how many digits are needed
        calculateFormatter(data.getYMin(), data.getYMax());

        for (IDataSet set : mData.getDataSets()) {
            if (Utils.needsDefaultFormatter(set.getValueFormatter()))
                set.setValueFormatter(mDefaultFormatter);
        }

        // let the chart know there is new data
        notifyDataSetChanged();

        if (mLogEnabled)
            Log.i(LOG_TAG, "Data is set.");
    }

setData的内部调用抽象方法notifyDataSetChanged方法,在具体的Chart类型具体实现,通过刷新引起onDraw方法的重绘,达到跟新视图的目的。clear方法清除数据,还定义了几个抽象方法,用于具体的Chart类型自己具体实现,比如calculateOffsets方法根据边界范围计算偏移量。

  /**
     * calculates the offsets of the chart to the border depending on the
     * position of an eventual legend or depending on the length of the y-axis
     * and x-axis labels and their position
     */
    protected abstract void calculateOffsets();

    /**
     * calcualtes the y-min and y-max value and the y-delta and x-delta value
     */
    protected abstract void calcMinMax();

    /**
     * calculates the required number of digits for the values that might be
     * drawn in the chart (if enabled), and creates the default-value-formatter
     */
    protected void calculateFormatter(float min, float max) {

        float reference = 0f;

        if (mData == null || mData.getXValCount() < 2) {

            reference = Math.max(Math.abs(min), Math.abs(max));
        } else {
            reference = Math.abs(max - min);
        }

        int digits = Utils.getDecimals(reference);
        mDefaultFormatter = new DefaultValueFormatter(digits);
    }

如果mData数据集为空,会绘制相应的文字告知用户。

   @Override
    protected void onDraw(Canvas canvas) {
        // super.onDraw(canvas);

        if (mData == null) {

            boolean hasText = !TextUtils.isEmpty(mNoDataText);
            boolean hasDescription = !TextUtils.isEmpty(mNoDataTextDescription);
            float line1height = hasText ? Utils.calcTextHeight(mInfoPaint, mNoDataText) : 0.f;
            float line2height = hasDescription ? Utils.calcTextHeight(mInfoPaint, mNoDataTextDescription) : 0.f;
            float lineSpacing = (hasText && hasDescription) ?
                    (mInfoPaint.getFontSpacing() - line1height) : 0.f;

            // if no data, inform the user

            float y = (getHeight() -
                    (line1height + lineSpacing + line2height)) / 2.f
                    + line1height;

            if (hasText) {
                canvas.drawText(mNoDataText, getWidth() / 2, y, mInfoPaint);

                if (hasDescription) {
                    y = y + line1height + lineSpacing;
                }
            }

            if (hasDescription) {
                canvas.drawText(mNoDataTextDescription, getWidth() / 2, y, mInfoPaint);
            }
            return;
        }
        //..................
    }

drawMarkers方法是用于触摸弹出效果显示触摸区域的值,具体这块的View自定义控件在上篇有提到。saveToPath方法保存图表的截图到Sdcard(实例在Simple中有)同类型方法略,ViewGroup大小发生变化会引起onSizeChanged回调,这里面会调用notifyDataSetChanged方法刷新视图,setHardwareAccelerationEnabled设置硬件加速

  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (mLogEnabled)
            Log.i(LOG_TAG, "OnSizeChanged()");

        if (w > 0 && h > 0 && w < 10000 && h < 10000) {

            mViewPortHandler.setChartDimens(w, h);

            if (mLogEnabled)
                Log.i(LOG_TAG, "Setting chart dimens, width: " + w + ", height: " + h);

            for (Runnable r : mJobs) {
                post(r);
            }

            mJobs.clear();
        }

        notifyDataSetChanged();

        super.onSizeChanged(w, h, oldw, oldh);
    }

    /**
     * Setting this to true will set the layer-type HARDWARE for the view, false
     * will set layer-type SOFTWARE.
     *
     * @param enabled
     */
    public void setHardwareAccelerationEnabled(boolean enabled) {

        if (android.os.Build.VERSION.SDK_INT >= 11) {

            if (enabled)
                setLayerType(View.LAYER_TYPE_HARDWARE, null);
            else
                setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        } else {
            Log.e(LOG_TAG,
                    "Cannot enable/disable hardware acceleration for devices below API level 11.");
        }
    }

onDetachedFromWindow方法调用unbindDrawables方法,取消View以及子View的所有监听,以免内存泄漏

  private void unbindDrawables(View view) {

        if (view.getBackground() != null) {
            view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
            ((ViewGroup) view).removeAllViews();
        }
    }

以上几篇blog对于个人来说就是打基础,以便于得心应手的玩转Chart,如果在没看过该库的大体结构,个人感觉是蒙的,不知从何下手,只知道官方simple照着抄,以上相关blog有任何问题欢迎指教留言,我也是个逗比中的菜逼,千万跟我较真!!

你可能感兴趣的:(Android)