์ฑ์์ง ๊บพ์์ ํ ์ฐจํธ๋ฅผ ๊ทธ๋ฆฌ๋ ค๊ณ ํฉ๋๋ค. ๋ ๋ช
ํํ๊ฒ ๊ทธ๋ฆผ์ ๋ณผ ์ ์์ต๋๋ค.
๊ทธ๋์ ๋๋ ์์ ๋ฅผ ๋ณด์๊ณ ๊ทธ๋ค์ IFillFormatter ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ์ธํธ์ ์ฑ์์ง ์ค์ด ๋๋๋ y์ถ ์์น๋ฅผ ์ป์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ๊ณ ์ ๋ ์์น์๊ณ ์ ํ ์ญ๋์ ์ด์ง ์์์ต๋๋ค. ์ด ์์น๋ ๋ฐ์ดํฐ ์ธํธ์ ๊ฐ ์์์ ๋ฐ๋ผ ์ด๋ป๊ฒ ๋ฌ๋ผ์ง ์ ์์ต๋๊น? ์๋ฅผ ๋ค์ด ์ธ๋ฑ์ค == 0์ด๋ฉด ์ฑ์์ง ์์น A์ ๋์ด ์๊ณ ์ธ๋ฑ์ค == 1์ด๋ฉด ์ฑ์์ง ์์น์ ๋ค๋ฅธ ๋ ๊ฐ์ด ์์ต๋๋ค.
๋๋ ์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ ์ฐจํธ๋ฅผ ๊ทธ๋ฆฌ๋ ๋ค๋ฅธ ์ ์์ด ์์ต๋๋ค.
์ฑ์ฐ๊ธฐ ์ฌ๊ฐํ์ ๊ทธ๋ฆฌ๋ drawLinearFill
์ดํด๋ณด์ญ์์ค. generateFilledPath
๋ฐ getFillLinePosition
๋ด์ ์ฑ์ฐ๊ธฐ ์ฌ๊ฐํ์ ๊ณ์ฐํฉ๋๋ค. ์ด์์ ์ผ๋ก๋ ์ค ์ฌ์ด์ rect๋ฅผ ๊ฐ์ ธ์ค๋ ค๋ฉด ํด๋น ๋ฉ์๋๋ฅผ ์ฌ์ ์ํด์ผ ํฉ๋๋ค.
LineChartRenderer
๋ฅผ ์๋ธํด๋์ฑํ๊ณ IFillFormatter
๊ตฌํํ๋ ํด๋์ค์ ๋ณ์๋ฅผ ์ถ๊ฐํ์ฌ @liuxuan30 ์ ์ ์์ ๋ฐ๋ผ ์ด๋ฅผ ๋ฌ์ฑํ์ต๋๋ค. ๋ ๋ฒ์งธ ๋ฐ์ดํฐ ์ธํธ๋ฅผ ์ฑ์ฐ๊ธฐ ๋ผ์ธ์ ๋ํ ํ์๊ธฐ๋ก ์ ๋ฌํ๊ณ ๊ทธ์ ๋ฐ๋ผ ๊ฒฝ๋ก๋ฅผ ๊ทธ๋ฆฝ๋๋ค(์ ํ์๋ง ํด๋นํ์ง๋ง ๋ฒ ์ง์ด์ ๋ํด์๋ ๋์ผํ ์์
์ ์ํํ ์ ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค).
class AreaFillFormatter: IFillFormatter {
var fillLineDataSet: LineChartDataSet?
init(fillLineDataSet: LineChartDataSet) {
self.fillLineDataSet = fillLineDataSet
}
public func getFillLinePosition(dataSet: ILineChartDataSet, dataProvider: LineChartDataProvider) -> CGFloat {
return 0.0
}
public func getFillLineDataSet() -> LineChartDataSet {
return fillLineDataSet ?? LineChartDataSet()
}
}
class CustomLineChartRenderer: LineChartRenderer {
override open func drawLinearFill(context: CGContext, dataSet: ILineChartDataSet, trans: Transformer, bounds: XBounds) {
guard let dataProvider = dataProvider else { return }
let areaFillFormatter = dataSet.fillFormatter as? AreaFillFormatter
let filled = generateFilledPath(
dataSet: dataSet,
fillMin: dataSet.fillFormatter?.getFillLinePosition(dataSet: dataSet, dataProvider: dataProvider) ?? 0.0,
fillLineDataSet: areaFillFormatter?.getFillLineDataSet(),
bounds: bounds,
matrix: trans.valueToPixelMatrix)
if dataSet.fill != nil
{
drawFilledPath(context: context, path: filled, fill: dataSet.fill!, fillAlpha: dataSet.fillAlpha)
}
else
{
drawFilledPath(context: context, path: filled, fillColor: dataSet.fillColor, fillAlpha: dataSet.fillAlpha)
}
}
fileprivate func generateFilledPath(dataSet: ILineChartDataSet, fillMin: CGFloat, fillLineDataSet: ILineChartDataSet?, bounds: XBounds, matrix: CGAffineTransform) -> CGPath
{
let phaseY = animator?.phaseY ?? 1.0
let isDrawSteppedEnabled = dataSet.mode == .stepped
let matrix = matrix
var e: ChartDataEntry!
var fillLineE: ChartDataEntry?
let filled = CGMutablePath()
e = dataSet.entryForIndex(bounds.min)
fillLineE = fillLineDataSet?.entryForIndex(bounds.min)
if e != nil
{
if let fillLineE = fillLineE
{
filled.move(to: CGPoint(x: CGFloat(e.x), y: CGFloat(fillLineE.y * phaseY)), transform: matrix)
}
else
{
filled.move(to: CGPoint(x: CGFloat(e.x), y: fillMin), transform: matrix)
}
filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)), transform: matrix)
}
// Create the path for the data set entries
for x in stride(from: (bounds.min + 1), through: bounds.range + bounds.min, by: 1)
{
guard let e = dataSet.entryForIndex(x) else { continue }
if isDrawSteppedEnabled
{
guard let ePrev = dataSet.entryForIndex(x-1) else { continue }
filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(ePrev.y * phaseY)), transform: matrix)
}
filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)), transform: matrix)
}
// Draw a path to the start of the fill line
e = dataSet.entryForIndex(bounds.range + bounds.min)
fillLineE = fillLineDataSet?.entryForIndex(bounds.range + bounds.min)
if e != nil
{
if let fillLineE = fillLineE
{
filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(fillLineE.y * phaseY)), transform: matrix)
}
else
{
filled.addLine(to: CGPoint(x: CGFloat(e.x), y: fillMin), transform: matrix)
}
}
// Draw the path for the fill line (backwards)
if let fillLineDataSet = fillLineDataSet {
for x in stride(from: (bounds.min + 1), through: bounds.range + bounds.min, by: 1).reversed()
{
guard let e = fillLineDataSet.entryForIndex(x) else { continue }
if isDrawSteppedEnabled
{
guard let ePrev = fillLineDataSet.entryForIndex(x-1) else { continue }
filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(ePrev.y * phaseY)), transform: matrix)
}
filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)), transform: matrix)
}
}
filled.closeSubpath()
return filled
}
}
ํ๋ก์ ํธ๋ฅผ ๋ง์คํฐํ๊ธฐ ์ํ ์๋ฒฝํ ์:)
์ด ์๋ฃจ์ ์ ์ด๋ป๊ฒ ํด๊ฒฐ/์ฌ์ฉํฉ๋๊น?
@rob-k ์ด์ ๋ํ ์ฌ์ฉ ์๋ฅผ ์ ๊ณตํด ์ฃผ์๊ฒ ์ต๋๊น?
๋ฌผ๋ก ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.
let maxDataSet = LineChartDataSet()
let minDataSet = LineChartDataSet()
// ... fill the data sets
// Set the data
self.lineChart.data = LineChartData(dataSets: [maxDataSet, minDataSet])
// Set the custom line chart renderer
self.lineChart.renderer = CustomLineChartRenderer(dataProvider: self.lineChart, animator: self.lineChart.chartAnimator, viewPortHandler: self.lineChart.viewPortHandler)
maxDataSet.drawFilledEnabled = true
maxDataSet.fillFormatter = AreaFillFormatter(fillLineDataSet: minDataSet)
๋๋ ์ด๊ฒ์ ์๋์ผ๋ก ์ค์นํ๊ณ ์์ ์ ๋ฐ๋ผ ๊ฐ ๋จ๊ณ๋ฅผ ์ํํ๋๋ฐ ์ฌ์ ํ @rob-k๊ฐ ์๋ํ์ง ์๋ ๊ฒ์ฒ๋ผ ๋ณด์ ๋๋ค.
@rob-k ํธ์ถ๋์ง ์๋ CustomLineChartRenderer์ ๊ธฐ๋ฅ ์ค ํ๋๋ฅผ ์ค๋จ์ ์ ์ฌ์ฉํ์ฌ ๋๋ฒ๊ทธํ๋ ค๊ณ ํ์ต๋๋ค.
LineChartRenderer
์ค์ ์ ํฌํจํ๋๋ก ๋ด ์๊ฒฌ์ ์
๋ฐ์ดํธํ์ต๋๋ค.
์ฌ์ ํ ์ด์ด ์๋ ์ฌ๋ @rob-k
ucbDataSet
์ lcbDataSet
์ฌ์ด์ ์์์ ์ํ๋ฉด ๋ ๋ฒ์งธ ๋ฐ์ดํฐ ์ธํธ๋ฅผ ์ฒซ ๋ฒ์งธ ๋ฐ์ดํฐ ์ธํธ์ ํฌ๋งทํฐ์ ์ ๋ฌํด์ผ ํฉ๋๋ค. ๋ฐ์ดํฐ ์ธํธ๋ฅผ ์์ฒด ํฌ๋งทํฐ์ ์ ๋ฌํ๊ณ ์์ต๋๋ค. ๋ค์์ ์๋ํ์ญ์์ค.
ucbDataSet.fillFormatter = AreaFillFormatter(fillLineDataSet: lcbDataSet)
(๋๋ ๊ทธ ๋ฐ๋)
์ด๋ฏธ ๋ด๊ฐ ์ค๋ช
ํ๋ ค๊ณ ์๋ํ ๊ฒ์
ํธ์ถ๋์ง ์๋ CustomLineChartRenderer ํด๋์ค์ ํจ์ ์ค ํ๋๋ฅผ ์ค๋จ์ ์ ์ฌ์ฉํ์ฌ ๋๋ฒ๊ทธํ๋ ค๊ณ ํ์ต๋๋ค.
์ฝ๋์์ ๋ฒ ์ง์ด ๋ชจ๋๋ฅผ ์ฌ์ฉํ๊ณ ์์์ ์ ์ ์์ต๋๋ค.
lineChartDataSet.mode = .horizontalBezier
ucbDataSet.mode = .horizontalBezier
lcbDataSet.mode = .horizontalBezier
๋ด๊ฐ ์ ๊ณตํ ์ฝ๋๋ ์ ํ ๋ชจ๋์์๋ง ์๋ํฉ๋๋ค. ๋ชจ๋๋ฅผ .linear
ํ๊ฑฐ๋ drawHorizontalBezier
์ฌ์ ์ํ๊ณ ๊ทธ์ ๋ฐ๋ผ ์ฝ๋๋ฅผ ์กฐ์ ํด์ผ ํฉ๋๋ค.
@rob-k ๋ฐฉ๊ธ ์๋ํ์ต๋๋ค. ์ฐพ์์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
CombinedChart๋ฅผ ์ฌ์ฉํ์ฌ ์ด๊ฒ์ ๊ตฌํํ ์ ์์ต๋๊น?
CombinedChart๋ฅผ ์ฌ์ฉํ์ฌ ์ด๊ฒ์ ๊ตฌํํ ์ ์์ต๋๊น?
ํด๊ฒฐ์ฑ
์ ์ฐพ์์ต๋๊น? CombinedChart๋ LinearChartRender๊ฐ ์๋ CombinedChartRender๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ์ฝ๋ฐฑ์ drawLinearFill
์๋๋ผ ๊ธฐ๋ณธ Render๋ฅผ ์ฌ์ฉํฉ๋๋ค.
drawLinearFill
๋ฉ์๋๊ฐ ์กด์ฌํ์ง ์์ต๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ๊ฒ์ํ ๋ ๋ง์ ์๋ก์ด ๋ ๋๋ง์ด ํ์ํฉ๋๋ค.
CombinedChart๋ฅผ ์ฌ์ฉํ์ฌ ์ด๊ฒ์ ๊ตฌํํ ์ ์์ต๋๊น?
์๋ฃจ์ ๊ฒฐํฉ ์ฐจํธ : https://github.com/PhilJay/MPAndroidChart/issues/338
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
LineChartRenderer
๋ฅผ ์๋ธํด๋์ฑํ๊ณIFillFormatter
๊ตฌํํ๋ ํด๋์ค์ ๋ณ์๋ฅผ ์ถ๊ฐํ์ฌ @liuxuan30 ์ ์ ์์ ๋ฐ๋ผ ์ด๋ฅผ ๋ฌ์ฑํ์ต๋๋ค. ๋ ๋ฒ์งธ ๋ฐ์ดํฐ ์ธํธ๋ฅผ ์ฑ์ฐ๊ธฐ ๋ผ์ธ์ ๋ํ ํ์๊ธฐ๋ก ์ ๋ฌํ๊ณ ๊ทธ์ ๋ฐ๋ผ ๊ฒฝ๋ก๋ฅผ ๊ทธ๋ฆฝ๋๋ค(์ ํ์๋ง ํด๋นํ์ง๋ง ๋ฒ ์ง์ด์ ๋ํด์๋ ๋์ผํ ์์ ์ ์ํํ ์ ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค).