關於MPAndroidChart記錄幾個問題

近期需要有一個曲線圖表的需求,最前的圖表功能的自定義View寫,感覺性能上不過關,所以這次使用那個*炸天的Android圖表宇宙無敵開源庫MPAndroidChart,憑着中學生英語的水平大致看了下文檔。

還是自己好好擼源碼吧,MPAndroidChart其實已經封裝的很好了,API也很易懂,但是有一些細節需要改動源碼,希望可以幫到大家,先看看MPAndroidChart的包。

只選標了幾個,其實包名已經超清楚了,其實Utils裏就有很多幹貨,計算Text的高度等方法很好用,當然常做自定義控件的朋友應該都很熟悉了。

由於直接用的公司代碼,所以沒有整段的貼。。。。好了,開始擼代碼。

很簡單一個demo,這裏可能有兩個問題,我在看相關文檔的時候看見過有一些同學問過一個問題,Line模式的圖怎麼讓它一出來就呈現縮放的狀態,而且要支持手勢。這裏其實是用的ViewPortHandler類做的處理,這個類作者聲明必須要仔細閱讀文檔之後再使用,否則容易出現問題,真是嚇屎了好吧。ViewPortHandler內部包含一個Matrix對象,拿這個Matrix就可以對Line做一些操作了。此處參考了gogooing的處理方式。

private void setHandler(CombinedChart combinedChart) {
        final ViewPortHandler viewPortHandlerBar = combinedChart.getViewPortHandler();
        viewPortHandlerBar.setMaximumScaleX(3);//最大縮放因子。
        Matrix touchmatrix = viewPortHandlerBar.getMatrixTouch();
        final float xscale = 3;
        touchmatrix.postScale(xscale, 1f);
}

這個方法在數據加載設置完之後調用,然後還需要一步操作,因爲這個時候你會發現,你的線是從頭開始的,一般初始顯示都希望是顯示最後一根。

setHandler(mChartPrice);
mChartPrice.moveViewToX(Data.size() - 1);

哈哈,最後移動一下就好啦。

另外一個問題是,最左邊labels如果過長的話就會被擋住一半。

其實源碼是做過Label超出屏幕的處理,結果最後一個Label的判斷一直沒有走進去過,把i == mXAxis.mEntryCount - 1改爲i == mXAxis.mEntryCount就可以了,這裏不知道是不是有什麼問題,總之哪個判斷就是進不去。

/**
     * draws the x-labels on the specified y-position
     *
     * @param pos
     */
    protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
        ........//省略N行代碼
        for (int i = 0; i < positions.length; i += 2) {
            float x = positions[i];
            if (mViewPortHandler.isInBoundsX(x)) {
                String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis);
                //這個判斷裏是處理第一個和最後一個labels,當然也可以自己寫。
                if (mXAxis.isAvoidFirstLastClippingEnabled()) {
                    // avoid clipping of the last
                    if (i == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) {
                        float width = Utils.calcTextWidth(mAxisLabelPaint, label);
                        if (width > mViewPortHandler.offsetRight() * 2
                                && x + width > mViewPortHandler.getChartWidth())
                            x -= width / 2;
                        // avoid clipping of the first
                    } else if (i == 0) {
                            //第一個點不做操作
//                        float width = Utils.calcTextWidth(mAxisLabelPaint, label);
//                        x += width / 2;
                    }
                }
                drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees);
            }
        }
    }

顯示的問題解決了,還有是事件問題,MPAndroidChart提供的手勢很多,但是Highlight的手勢很單一,直接在Chart上滑動就可以觸發Highlight顯示,還有Chart縮放的時候好像是不能出現Highlight的,個人觀察是這樣,那就只能改源碼了,我們希望可以長按顯示Highlight,並且鬆開手Highlight消失。

這個庫的包名都可以很好的幫助理解功能,關於繪製的類都在renderer包下面,關於手勢很容易聯想帶listener,沒錯就是listener包下的BarLineChartTouchListener(折線圖是這個)。onTouch方法簡直不要太熟悉,手勢操作就在這了,200行左右很少,邏輯也很清楚。我們在MotionEvent.ACTION_MOVE裏面做改動。可以看到這個類裏面還有一個onLongPress方法,這個是系統類GestureDetector內部類SimpleOnGestureListener的一個長按事件的回調方法。現在我們找一個標誌位,來標記長按事件,事實上可以直接拿SimpleOnGestureListener裏的mLastGesture來用。直接添加5-8行處的代碼,這就解決了縮放狀態下不能是使用Highlight的問題。然後擡手的時候就是在ACTION_CANCEL、ACTION_POINTER_UP、ACTION_POINTER_UP中加入mChart.highlightValue(null);mLastGesture=ChartGesture.NONE;兩句代碼,把標誌位和Highlight復原即可。

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
            case MotionEvent.ACTION_MOVE:
                if (mLastGesture==ChartGesture.LONG_PRESS){
                    performHighlightDrag(event);
                    break;
                }
                if (mTouchMode == DRAG) {
                    mChart.disableScroll();
                    float x = mChart.isDragXEnabled() ? event.getX() - mTouchStartPoint.x : 0.f;
                    float y = mChart.isDragYEnabled() ? event.getY() - mTouchStartPoint.y : 0.f;
                    performDrag(event, x, y);
                } else if (mTouchMode == X_ZOOM || mTouchMode == Y_ZOOM || mTouchMode == PINCH_ZOOM) {
                    mChart.disableScroll();
                    if (mChart.isScaleXEnabled() || mChart.isScaleYEnabled())
                        performZoom(event);
                } else if (mTouchMode == NONE
                        && Math.abs(distance(event.getX(), mTouchStartPoint.x, event.getY(),
                        mTouchStartPoint.y)) > mDragTriggerDist) {
                    if (mChart.isDragEnabled()) {
                        boolean shouldPan = !mChart.isFullyZoomedOut() ||
                                !mChart.hasNoDragOffset();
                        if (shouldPan) {
                            float distanceX = Math.abs(event.getX() - mTouchStartPoint.x);
                            float distanceY = Math.abs(event.getY() - mTouchStartPoint.y);
                            // Disable dragging in a direction that's disallowed
                            if ((mChart.isDragXEnabled() || distanceY >= distanceX) &&
                                    (mChart.isDragYEnabled() || distanceY <= distanceX)) {
                                mLastGesture = ChartGesture.DRAG;
                                mTouchMode = DRAG;
                            }
                        } else {
                            if (mChart.isHighlightPerDragEnabled()) {
                                mLastGesture = ChartGesture.DRAG;
                                if (mChart.isHighlightPerDragEnabled())
                                    performHighlightDrag(event);
                            }
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                final VelocityTracker velocityTracker = mVelocityTracker;
                final int pointerId = event.getPointerId(0);
                velocityTracker.computeCurrentVelocity(1000, Utils.getMaximumFlingVelocity());
                final float velocityY = velocityTracker.getYVelocity(pointerId);
                final float velocityX = velocityTracker.getXVelocity(pointerId);
                if (Math.abs(velocityX) > Utils.getMinimumFlingVelocity() ||
                        Math.abs(velocityY) > Utils.getMinimumFlingVelocity()) {
                    if (mTouchMode == DRAG && mChart.isDragDecelerationEnabled()) {
                        stopDeceleration();
                        mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis();
                        mDecelerationCurrentPoint.x = event.getX();
                        mDecelerationCurrentPoint.y = event.getY();
                        mDecelerationVelocity.x = velocityX;
                        mDecelerationVelocity.y = velocityY;
                        Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by
                        // Google
                    }
                }
                if (mTouchMode == X_ZOOM ||
                        mTouchMode == Y_ZOOM ||
                        mTouchMode == PINCH_ZOOM ||
                        mTouchMode == POST_ZOOM) {

                    // Range might have changed, which means that Y-axis labels
                    // could have changed in size, affecting Y-axis size.
                    // So we need to recalculate offsets.
                    mChart.calculateOffsets();
                    mChart.postInvalidate();
                }
                mTouchMode = NONE;
                mChart.enableScroll();
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                endAction(event);
                break;
            case MotionEvent.ACTION_POINTER_UP:
                Utils.velocityTrackerPointerUpCleanUpIfNecessary(event, mVelocityTracker);
                mTouchMode = POST_ZOOM;
                break;
            case MotionEvent.ACTION_CANCEL:
                mTouchMode = NONE;
                endAction(event);
                break;
        }
        // perform the transformation, update the chart
        mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true);
        return true; // indicate event was handled
    }
@Override
    public void onLongPress(MotionEvent e) {
        mLastGesture = ChartGesture.LONG_PRESS;
        OnChartGestureListener l = mChart.getOnChartGestureListener();
        if (l != null) {
            l.onChartLongPressed(e);
        }
    }
先記錄這幾個問題- -。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章