以前覺得用安卓畫一個折線圖好難疮蹦,當然github上也有很多方便的框架惰瓜,幾行代碼就可以實現(xiàn)一個折線圖否副,而且各種樣式都有,但是別人的框架畢竟是為了滿足大眾的需求崎坊,沒有專一性备禀,正好前幾天接了一個私活,要用到折線圖奈揍,時間也比較充足曲尸,就試著自己寫一個玩玩。
經(jīng)過仔細分析小米天氣中的折線圖后男翰,發(fā)現(xiàn)其實寫一個折線圖是非常簡單的另患。先上兩張小米天氣的折線圖
如果我告訴你這兩個圖的實現(xiàn)思路是一樣的,而且都很容易奏篙,你會不會很震驚柴淘。
其實是第二種圖中“今天”那一項數(shù)據(jù)給提供了思路,沒錯秘通,recyclerview为严。
拿第一張圖片來分析
黑色部分框起來的就當做一個item,首先我們要把所有數(shù)據(jù)中的最大值和最小值取出來肺稀,如紅線畫住的部分第股,然后確定一個區(qū)間(y坐標),表示我們所有的點都要在這個區(qū)間內(nèi)顯示话原。圖中當值為176表示最大夕吻,在第一個紅線那個高度,44表示最小繁仁,在第二條直線那個高度涉馅,這兩個高度是我們指定的,那么70這個值對應的高度很明顯就可以計算出來黄虱,103的高度也可以計算出來稚矿,70和103的中間值,也就是圖中第一個紅圈圈住的地方的高度也可以確定出來捻浦,同理晤揣,第三個小圓圈也知道了,然后第一個小圓圈和第二個連線朱灿,第二個和第三個連線昧识,這就畫好了一個item的折線。多個item連接起來就是上面的效果盗扒。
第二張圖思路也是一樣的跪楞,只不過在一個item上畫了兩個折線,如圖中黑線框住的區(qū)域
思路就是這樣环疼,是不是很簡單了习霹,當然前提要掌握view的繪制,因為每一個item都是繼承自view炫隶,然后繪制的淋叶。
上代碼,總共就三個java文件,也不用布局文件伪阶,直接導進去就可以看效果煞檩。記得依賴recyclerview的庫哦。代碼注釋也寫得比較清楚栅贴,主要代碼就是那個item
item的代碼
package com.xk.linedemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;
/**
* Created by xuekai on 2016/11/10.
*/
public class Item extends View {
private int maxValue;//最高值
private int minValue;//最低值
private int currentValue = 15;//當前值
private int lastValue = 10;//上一個值
private int nextValue = 10;//下一個值
private Paint mPaint;
private int viewHeight;
private int viewWidth;
private int pointTopY = 130;//最高點的Y坐標 130
private int pointBottomY = 255;//最低點的Y坐標 255
private int pointX;//所有點的x坐標
private int pointY;//當前點的Y
private boolean drawLeftLine = true;//是否畫左邊的線
private boolean drawRightLine = true;//是否畫右邊的線
public Item(Context context) {
super(context);
init();
setBackgroundColor(0xffffffff);
}
public void setCurrentValue(int currentValue) {
this.currentValue = currentValue;
invalidate();
}
public void setMaxValue(int maxValue) {
this.maxValue = maxValue;
}
public void setMinValue(int minValue) {
this.minValue = minValue;
}
private void init() {
mPaint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initValues();
}
private void initValues() {
viewHeight = getMeasuredHeight();
viewWidth = getMeasuredWidth();
pointX = viewWidth / 2;
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
pointY = (int) ((pointBottomY - pointTopY) * 1f / (maxValue - minValue) * (maxValue - currentValue + minValue) + pointTopY);
drawLine(canvas);
drawGraph(canvas);
drawPoint(canvas);
drawValue(canvas);
Log.e("Item", "" + lastValue + " " + currentValue + " " + nextValue);
}
/**
* 畫數(shù)字
*
* @param canvas
*/
private void drawValue(Canvas canvas) {
mPaint.setTextSize(20);
mPaint.setColor(Color.parseColor("#ff333333"));
mPaint.setStrokeWidth(0);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
float baseLine1 = pointY - fontMetrics.bottom * 4;
canvas.drawText(currentValue + "", viewWidth / 2, baseLine1, mPaint);
}
public void setlastValue(int lastValue) {
this.lastValue = lastValue;
}
public void setNextValue(int nextValue) {
this.nextValue = nextValue;
}
/**
* 畫折線
*
* @param canvas
*/
private void drawGraph(Canvas canvas) {
mPaint.setPathEffect(null);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(0xff24C3F1);
mPaint.setStrokeWidth(3);
mPaint.setAntiAlias(true);
if (drawLeftLine) {
float middleValue = currentValue - (currentValue - lastValue) / 2f;
float middleY = ((pointBottomY - pointTopY) * 1f / (maxValue - minValue) * (maxValue - middleValue + minValue) + pointTopY);
canvas.drawLine(0, middleY, pointX, pointY, mPaint);
}
if (drawRightLine) {
float middleValue = currentValue - (currentValue - nextValue) / 2f;
float middleY = ((pointBottomY - pointTopY) * 1f / (maxValue - minValue) * (maxValue - middleValue + minValue) + pointTopY);
canvas.drawLine(pointX, pointY, viewWidth, middleY, mPaint);
}
}
/**
* 畫數(shù)字下面的小圓圈
*
* @param canvas
*/
private void drawPoint(Canvas canvas) {
mPaint.setColor(0xffffffff);
mPaint.setPathEffect(null);
mPaint.setStrokeWidth(2);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(pointX, pointY, 10, mPaint);
mPaint.setColor(0xff24C3F1);
mPaint.setStrokeWidth(2);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(pointX, pointY, 5, mPaint);
}
/**
* 畫最下面的一條線
*
* @param canvas
*/
private void drawLine(Canvas canvas) {
mPaint.setColor(0xffD3D3D3);
mPaint.setPathEffect(null);
mPaint.setStrokeWidth(2);
mPaint.setStyle(Paint.Style.FILL);
if (drawLeftLine) {
canvas.drawLine(0, 500, viewWidth / 2, 500, mPaint);//這兩個值表示下面那條橫線的Y坐標(第二四個參數(shù))
}
if (drawRightLine) {
canvas.drawLine(viewWidth / 2, 500, viewWidth, 500, mPaint);//這兩個值表示下面那條橫線的Y坐標(第二四個參數(shù))
}
}
public void setDrawLeftLine(boolean drawLeftLine) {
this.drawLeftLine = drawLeftLine;
}
public void setDrawRightLine(boolean drawRightLine) {
this.drawRightLine = drawRightLine;
}
}
折線view的代碼
package com.xk.linedemo;
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by xk on 2016/11/9 22:14.
*/
public class BrokenLineView extends RecyclerView {
private int maxValue;
private int minValue;
private List<Integer> data = new ArrayList<>();
private Adapter adapter;
public BrokenLineView(Context context) {
super(context);
init();
}
private void init() {
setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
adapter = new Adapter();
setAdapter(adapter);
}
public void setData(List<Integer> d) {
if (data != null) {
data.clear();
data.addAll(d);
Collections.sort(d);
minValue = d.get(0);
maxValue = d.get(d.size() - 1);
adapter.notifyDataSetChanged();
}
}
class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Item item = new Item(getContext());
item.setMinValue(minValue);
item.setMaxValue(maxValue);
LayoutParams layoutParams = new LayoutParams(200, ViewGroup.LayoutParams.MATCH_PARENT);//這個數(shù)字表示每一個item的寬度
item.setLayoutParams(layoutParams);
return new ViewHolder(item);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
if (position == 0) {
holder.item.setDrawLeftLine(false);
} else {
holder.item.setDrawLeftLine(true);
holder.item.setlastValue((data.get(position - 1)));
}
holder.item.setCurrentValue((data.get(position)));
if (position == data.size() - 1) {
holder.item.setDrawRightLine(false);
} else {
holder.item.setDrawRightLine(true);
holder.item.setNextValue((data.get(position + 1)));
}
}
@Override
public int getItemCount() {
return data.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
Item item;
public ViewHolder(View itemView) {
super(itemView);
this.item = (Item) itemView;
}
}
}
}
MainActivity的代碼
package com.xk.linedemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BrokenLineView brokenLineView = new BrokenLineView(this);
ArrayList<Integer> integers = new ArrayList<>();
integers.add(2);
integers.add(12);
integers.add(21);
integers.add(4);
integers.add(7);
integers.add(18);
integers.add(30);
brokenLineView.setData(integers);
setContentView(brokenLineView);
}
}