中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在Android中繪制一個曲線圖

發布時間:2021-03-10 16:32:12 來源:億速云 閱讀:260 作者:Leah 欄目:移動開發

這期內容當中小編將會給大家帶來有關怎么在Android中繪制一個曲線圖,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

使用方式

// 初始化數據表格相關
with(mTableView) {
  // 配置坐標系
  setupCoordinator("日", "人", /*這里是橫坐標的值*/0f, 5f, 10f, 15f, 20f, 25f, 30f)
  // 添加曲線, 確保縱坐標的數值位數相等
  addWave(ContextCompat.getColor(this@MainActivity, R.color.colorYellow), false,
      0f, 10f, 30f, 54f, 30f, 100f, 10f)
  addWave(ContextCompat.getColor(this@MainActivity, R.color.colorGreen), false,
      0f, 30f, 20f, 20f, 46f, 25f, 5f)
  addWave(ContextCompat.getColor(this@MainActivity, R.color.colorPink), false,
      0f, 30f, 20f, 50f, 46f, 30f, 30f)
  addWave(Color.parseColor("#8596dee9"), true,
      0f, 15f, 10f, 10f, 40f, 20f, 5f)
}

實現思路

  1. 橫坐標是固定的, 縱坐標需要跟隨曲線傳入的數值去動態的調整

  2. 繪制坐標軸: 縱橫交錯的網格

  3. 根據用戶傳入坐標數值去繪制坐標軸上的數值

  4. 給X軸和Y軸添加單位信息

  5. 根據用戶傳入的具體的數值繪制曲線(這里不采用Bezier, 不容易精確的控制頂點的位置)

  6. 繪制填充效果

  7. 添加屬性動畫

代碼實現

/**
 * Created by FrankChoo on 2017/12/29.
 * Email: frankchoochina@gmail.com
 * Version: 1.0
 * Description: 表格自定義View
 */
public class TableView extends View {

  private List<WaveConfigData> mWaves;// 數值集合
  // 坐標軸的數值
  private int mCoordinateYCount = 8;
  private float[] mCoordinateXValues;// 外界傳入
  private float[] mCoordinateYValues;// 動態計算
  // 坐標的單位
  private String mXUnit;
  private String mYUnit;
  // 所有曲線中所有數據中的最大值
  private float mGlobalMaxValue;// 用于確認是否需要調整坐標系
  private Paint mCoordinatorPaint;
  private Paint mTextPaint;
  private Paint mWrapPaint;
  // 坐標軸上描述性文字的空間大小
  private int mTopUnitHeight;// 頂部Y軸單位高度
  private int mBottomTextHeight;
  private int mLeftTextWidth;
  // 網格尺寸
  private int mGridWidth, mGridHeight;
  private float mAnimProgress;

  public TableView(Context context) {
    this(context, null);
  }

  public TableView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public TableView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
    post(new Runnable() {
      @Override
      public void run() {
        showAnimator();
      }
    });
  }

  private void init() {
    // 初始化數據集合的容器
    mWaves = new ArrayList<>();
    // 坐標系的單位
    mBottomTextHeight = dp2px(40);// X軸底部字體的高度
    mLeftTextWidth = mBottomTextHeight;// Y軸左邊字體的寬度
    mTopUnitHeight = dp2px(30);// 頂部Y軸的單位
    // 初始化坐標軸Paint
    mCoordinatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    mCoordinatorPaint.setColor(Color.LTGRAY);
    // 初始化文本Paint
    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    mTextPaint.setColor(Color.GRAY);
    mTextPaint.setTextSize(sp2px(12));
    // 初始化曲線Paint
    mWrapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    mWrapPaint.setPathEffect(new CornerPathEffect(200f));
  }

  /**
   * 配置坐標軸信息
   *
   * @param xUnit       X 軸的單位
   * @param yUnit       Y 軸的單位
   * @param coordinateXValues X 坐標軸上的數值
   */
  public void setupCoordinator(String xUnit, String yUnit, float... coordinateXValues) {
    mXUnit = xUnit;
    mYUnit = yUnit;
    mCoordinateXValues = coordinateXValues;
  }

  /**
   * 添加一條曲線, 確保與橫坐標的數值對應
   *
   * @param color
   * @param isCoverRegion
   * @param values
   */
  public void addWave(int color, boolean isCoverRegion, float... values) {
    mWaves.add(new WaveConfigData(color, isCoverRegion, values));
    // 根據value的值去計算縱坐標的數值
    float maxValue = 0;
    for (float value : values) {
      maxValue = Math.max(maxValue, value);
    }
    if (maxValue < mGlobalMaxValue) return;
    mGlobalMaxValue = maxValue;
    // 保證網格的數值都為 5 的倍數
    float gridValue = mGlobalMaxValue / (mCoordinateYCount - 1);
    if (gridValue % 5 != 0) {
      gridValue += 5 - (gridValue % 5);
    }
    // 給縱坐標的數值賦值
    mCoordinateYValues = new float[mCoordinateYCount];
    for (int i = 0; i < mCoordinateYCount; i++) {
      mCoordinateYValues[i] = i * gridValue;
    }
    invalidate();
  }

  @Override
  protected void onDraw(Canvas canvas) {
    drawCoordinate(canvas);
    drawWrap(canvas);
  }

  public void showAnimator() {
    ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(1000);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        mAnimProgress = (float) animation.getAnimatedValue();
        invalidate();
      }
    });
    animator.start();
  }

  /**
   * 繪制坐標系
   */
  private void drawCoordinate(Canvas canvas) {
    Point start = new Point();
    Point stop = new Point();
    // 1. 繪制橫軸線和縱坐標單位
    int xLineCount = mCoordinateYValues.length;
    mGridHeight = (getHeight() - getPaddingTop() - getPaddingBottom() - mBottomTextHeight - mTopUnitHeight) / (xLineCount - 1);
    for (int i = 0; i < xLineCount; i++) {
      start.x = getPaddingLeft() + mLeftTextWidth;
      start.y = getHeight() - getPaddingBottom() - mBottomTextHeight - mGridHeight * i;
      stop.x = getRight() - getPaddingRight();
      stop.y = start.y;
      // 繪制橫軸線
      canvas.drawLine(start.x, start.y, stop.x, stop.y, mCoordinatorPaint);
      // 繪制縱坐標單位
      if (i == 0) continue;
      String drawText = String.valueOf((int) mCoordinateYValues[i]);
      Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
      float offsetY = ((fontMetrics.bottom - fontMetrics.top) / 2 + fontMetrics.bottom) / 2;
      float baseLine = start.y + offsetY;
      float left = getPaddingLeft() + mLeftTextWidth / 2 - mTextPaint.measureText(drawText) / 2;
      canvas.drawText(drawText, left, baseLine, mTextPaint);
      // 繪制Y軸單位
      if (i == xLineCount - 1) {
        drawText = mYUnit;
        baseLine = getPaddingTop() + mTopUnitHeight / 2;
        canvas.drawText(drawText, left, baseLine, mTextPaint);
      }
    }
    // 2. 繪制縱軸線和橫坐標單位
    int yLineCount = mCoordinateXValues.length;
    mGridWidth = (getWidth() - getPaddingLeft() - getPaddingRight() - mLeftTextWidth) / (yLineCount - 1);
    for (int i = 0; i < yLineCount; i++) {
      start.x = getPaddingTop() + mLeftTextWidth + mGridWidth * i;
      start.y = getPaddingTop() + mTopUnitHeight;
      stop.x = start.x;
      stop.y = getHeight() - mBottomTextHeight - getPaddingBottom();
      // 繪制縱軸線
      canvas.drawLine(start.x, start.y, stop.x, stop.y, mCoordinatorPaint);
      // 繪制橫坐標單位
      String drawText = String.valueOf((int) mCoordinateXValues[i]);
      Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
      float offsetY = ((fontMetrics.bottom - fontMetrics.top) / 2 + fontMetrics.bottom) / 2;
      float baseLine = getHeight() - getPaddingBottom() - mBottomTextHeight / 2 + offsetY;
      float left = start.x - mTextPaint.measureText(drawText) / 2;
      // 繪制X軸單位
      if (i == 0) {
        drawText = mXUnit;
        left = getPaddingLeft() + mLeftTextWidth / 2 - mTextPaint.measureText(drawText) / 2;
      }
      canvas.drawText(drawText, left, baseLine, mTextPaint);
    }
  }

  /**
   * 繪制曲線
   */
  private void drawWrap(Canvas canvas) {
    canvas.clipRect(new RectF(
        mLeftTextWidth,
        getPaddingTop() + mTopUnitHeight,
        (getRight() - getPaddingRight()) * mAnimProgress,
        getHeight() - getPaddingBottom() - mBottomTextHeight)
    );
    float yHeight = mGridHeight * (mCoordinateYCount - 1);
    for (WaveConfigData wave : mWaves) {
      Path path = new Path();
      path.moveTo(0, getHeight());
      float maxY = mCoordinateYValues[mCoordinateYCount - 1];// Y軸坐標的最大值
      for (int index = 1; index < wave.values.length; index++) {
        path.lineTo(
            mLeftTextWidth + mGridWidth * index,
            getHeight() - getPaddingBottom() - mBottomTextHeight
                - yHeight * (wave.values[index] / maxY)
        );
      }
      if (wave.isCoverRegion) {
        mWrapPaint.setStyle(Paint.Style.FILL);
        path.lineTo(getRight() - getPaddingRight(), getHeight());
        path.close();
      } else {
        mWrapPaint.setStyle(Paint.Style.STROKE);
        mWrapPaint.setStrokeWidth(10);
      }
      mWrapPaint.setColor(wave.color);
      canvas.drawPath(path, mWrapPaint);
    }
  }

  private int dp2px(float dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
        dp, getResources().getDisplayMetrics());
  }

  private int sp2px(float sp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
        sp, getResources().getDisplayMetrics());
  }

  public static class WaveConfigData {
    int color;
    boolean isCoverRegion;
    float values[];

    public WaveConfigData(int color, boolean isCoverRegion, float[] values) {
      this.color = color;
      this.isCoverRegion = isCoverRegion;
      this.values = values;
    }
  }
}

上述就是小編為大家分享的怎么在Android中繪制一個曲線圖了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

马公市| 葵青区| 佛冈县| 尼玛县| 本溪市| 嘉荫县| 九龙城区| 彭阳县| 鄢陵县| 鹿邑县| 晋州市| 新郑市| 阿拉善右旗| 琼海市| 新民市| 德格县| 察哈| 江山市| 应城市| 富源县| 灵寿县| 彭泽县| 淮阳县| 清丰县| 泰和县| 托克托县| 乳山市| 舒城县| 六安市| 东乌珠穆沁旗| 株洲市| 江陵县| 唐山市| 左贡县| 竹山县| 宁海县| 凉山| 株洲县| 涞水县| 德庆县| 正蓝旗|