Android自定義View簡(jiǎn)易折線圖控件(二)
繼續(xù)練習(xí)自定義View,這次帶來(lái)的是簡(jiǎn)易折線圖,支持坐標(biāo)點(diǎn)點(diǎn)擊監(jiān)聽(tīng),效果如下:

畫(huà)坐標(biāo)軸、畫(huà)刻度、畫(huà)點(diǎn)、連線。。x、y軸的數(shù)據(jù)范圍是寫(xiě)死的 1 <= x <= 7 ,1 <= y <= 70 。。寫(xiě)活的話(huà)涉及到坐標(biāo)軸刻度的動(dòng)態(tài)計(jì)算、坐標(biāo)點(diǎn)的坐標(biāo)修改,想想就頭大,這里只練習(xí)自定義View。
1、在res/values文件夾下新建attrs.xml文件,編寫(xiě)自定義屬性:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="LineChartView"> <attr name="textColor" format="color" /> <attr name="lineColor" format="color" /> <attr name="pointColor" format="color" /> </declare-styleable> </resources>
2、新建LineChartView繼承View,重寫(xiě)構(gòu)造方法:
public LineChartView(Context context) {
this(context, null);
}
public LineChartView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
3、在第三個(gè)構(gòu)造方法中獲取自定義屬性的值:
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LineChartView, defStyleAttr, 0); mTextColor = ta.getColor(R.styleable.LineChartView_textColor, 0xff381a59); mLineColor = ta.getColor(R.styleable.LineChartView_lineColor, 0xff8e29fa); mPointColor = ta.getColor(R.styleable.LineChartView_pointColor, 0xffff5100); mPointRadius = DensityUtils.dp2px(context, 3); ta.recycle();
4、創(chuàng)建畫(huà)圖所使用的對(duì)象,如Paint、Path:
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setStyle(Paint.Style.FILL); mTextPaint.setColor(mTextColor); mTextPaint.setTextSize(40); mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mLinePaint.setStyle(Paint.Style.STROKE); mLinePaint.setColor(mLineColor); mLinePaint.setStrokeWidth(DensityUtils.dp2px(context, 2)); mLinePaint.setStrokeCap(Paint.Cap.ROUND); mXyPath = new Path(); mPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPointPaint.setStyle(Paint.Style.FILL); mPointPaint.setColor(mPointColor); mPointCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPointCirclePaint.setStyle(Paint.Style.STROKE); mPointCirclePaint.setStrokeWidth(DensityUtils.dp2px(context, 2)); mPointCirclePaint.setColor(mLineColor);
5、重寫(xiě)onMeasure()方法,計(jì)算自定義View的寬高:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measuredDimension(widthMeasureSpec), measuredDimension(heightMeasureSpec));
}
private int measuredDimension(int measureSpec) {
int result;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = 500;
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
6、暴露一個(gè)設(shè)置x、y數(shù)據(jù)集合的方法:
/**
* 設(shè)置數(shù)據(jù)
*
* @param xList x軸數(shù)據(jù)集合
* @param yList y軸數(shù)據(jù)集合
*/
public void setDataList(List<Integer> xList, List<Integer> yList) {
if (xList == null || yList == null || xList.size() == 0 || yList.size() == 0) {
throw new IllegalArgumentException("沒(méi)有數(shù)據(jù)");
}
if (xList.size() != yList.size()) {
throw new IllegalArgumentException("x、y軸數(shù)據(jù)長(zhǎng)度不一致");
}
setPointData(xList, yList);
setPointAnimator();
}
/**
* 設(shè)置坐標(biāo)點(diǎn)的數(shù)據(jù)、坐標(biāo)
*
* @param xList x軸數(shù)據(jù)集合
* @param yList y軸數(shù)據(jù)集合
*/
private void setPointData(List<Integer> xList, List<Integer> yList) {
mPointList = new ArrayList<>();
for (int i = 0; i < xList.size(); i++) {
ChartPoint point = new ChartPoint();
//設(shè)置坐標(biāo)點(diǎn)的xy數(shù)據(jù)
point.setxData(xList.get(i));
point.setyData(yList.get(i));
//計(jì)算坐標(biāo)點(diǎn)的橫縱坐標(biāo)
point.setX(xyMargin + xList.get(i) * (getWidth() - 2 * xyMargin) / maxX);
point.setY(getHeight() - xyMargin - (getHeight() - 2 * xyMargin) * yList.get(i) / maxY);
mPointList.add(point);
}
}
/**
* 設(shè)置坐標(biāo)點(diǎn)移動(dòng)的動(dòng)畫(huà)
*/
private void setPointAnimator() {
for (int i = 0; i < mPointList.size(); i++) {
final ChartPoint point = mPointList.get(i);
ValueAnimator anim;
if (mLastPointList != null && mLastPointList.size() > 0) {
anim = ValueAnimator.ofInt(mLastPointList.get(i).getY(), point.getY());
} else {
anim = ValueAnimator.ofInt(getHeight() - xyMargin, point.getY());
}
anim.setDuration(500);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
point.setY(value);
invalidate();
}
});
anim.start();
}
//儲(chǔ)存坐標(biāo)點(diǎn)集合
mLastPointList = mPointList;
}
7、重寫(xiě)onDraw()方法,繪制坐標(biāo)軸、刻度,畫(huà)點(diǎn)連線,注意坐標(biāo)的計(jì)算:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mPointList == null || mPointList.size() == 0) {
return;
}
mXyPath.reset();
mXyPath.moveTo(xyMargin, 0);
mXyPath.lineTo(xyMargin, getHeight() - xyMargin);
mXyPath.lineTo(getWidth(), getHeight() - xyMargin);
canvas.drawPath(mXyPath, mLinePaint);//畫(huà)x、y坐標(biāo)軸
for (int i = 0; i < mPointList.size(); i++) {
//畫(huà)x軸刻度線
int x = xyMargin + (i + 1) * (getWidth() - 2 * xyMargin) / mPointList.size();
canvas.drawLine(x, getHeight() - xyMargin - graduatedLineLength, x, getHeight() - xyMargin, mLinePaint);
//畫(huà)y軸刻度線
int y = getHeight() - xyMargin - (i + 1) * (getHeight() - 2 * xyMargin) / mPointList.size();
canvas.drawLine(xyMargin, y, xyMargin + graduatedLineLength, y, mLinePaint);
//畫(huà)坐標(biāo)軸刻度文本
canvas.drawText(String.valueOf(mPointList.get(i).getxData()), x, getHeight() - mTextPaint.getTextSize() / 4, mTextPaint);
canvas.drawText(String.valueOf((i + 1) * 10), 0, y + mTextPaint.getTextSize() / 2, mTextPaint);
}
//畫(huà)連接線
for (int i = 0; i < mPointList.size(); i++) {
if (i != mPointList.size() - 1) {
ChartPoint lastP = mPointList.get(i);
ChartPoint nextP = mPointList.get(i + 1);
canvas.drawLine(lastP.getX(), lastP.getY(), nextP.getX(), nextP.getY(), mLinePaint);
}
}
//畫(huà)坐標(biāo)點(diǎn)
for (int i = 0; i < mPointList.size(); i++) {
ChartPoint point = mPointList.get(i);
canvas.drawCircle(point.getX(), point.getY(), mPointRadius, mPointPaint);
canvas.drawCircle(point.getX(), point.getY(), mPointRadius, mPointCirclePaint);
}
}
8、設(shè)置坐標(biāo)點(diǎn)點(diǎn)擊事件:
private OnPointClickListener mOnPointClickListener;
/**
* 坐標(biāo)點(diǎn)點(diǎn)擊監(jiān)聽(tīng)
*/
public interface OnPointClickListener {
/**
* @param index 當(dāng)前坐標(biāo)點(diǎn)在數(shù)據(jù)集中的下標(biāo)
* @param point 當(dāng)前坐標(biāo)點(diǎn)對(duì)象
*/
void onPointClick(int index, ChartPoint point);
}
public void setOnPointClickListener(OnPointClickListener onPointClickListener) {
mOnPointClickListener = onPointClickListener;
}
9、重寫(xiě)onTouchEvent()方法,判斷當(dāng)前點(diǎn)擊的點(diǎn)是不是在坐標(biāo)點(diǎn)范圍內(nèi):
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//判斷當(dāng)前點(diǎn)擊的點(diǎn)是否在坐標(biāo)點(diǎn)范圍內(nèi)
int curX = (int) event.getX();
int curY = (int) event.getY();
for (int i = 0; i < mPointList.size(); i++) {
ChartPoint point = mPointList.get(i);
double d1 = Math.pow(curX - point.getX(), 2);
double d2 = Math.pow(curY - point.getY(), 2);
//√ ̄(curX - cx)² + (curY - cy)² < R
if (Math.sqrt(d1 + d2) < mPointRadius + 10) {//為了方便點(diǎn)擊,把坐標(biāo)點(diǎn)范圍增大了10像素
if (mOnPointClickListener != null) {
mOnPointClickListener.onPointClick(i, point);
}
}
}
break;
}
return super.onTouchEvent(event);
}
10、在activity_main.xml布局文件中使用該View:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:lcv="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical" tools:context=".MainActivity"> <com.monkey.linechartview.LineChartView android:id="@+id/chartView" android:layout_width="250dp" android:layout_height="250dp" android:layout_marginTop="@dimen/activity_vertical_margin" lcv:lineColor="#8e29fa" lcv:pointColor="#ff5100" lcv:textColor="#000000" /> <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_vertical_margin" android:text="set data" android:textAllCaps="false" /> </LinearLayout>
11、在MainActivity.java中傳入數(shù)據(jù)集合,并設(shè)置坐標(biāo)點(diǎn)點(diǎn)擊監(jiān)聽(tīng):
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<Integer> xList = new ArrayList<>();
List<Integer> yList = new ArrayList<>();
for (int i = 0; i < 7; i++) {
xList.add(i + 1);
int y = (int) (Math.random() * 70 + 1);
yList.add(y);
}
chartView.setDataList(xList, yList);
}
});
chartView.setOnPointClickListener(new LineChartView.OnPointClickListener() {
@Override
public void onPointClick(int position, ChartPoint point) {
tv.setText("position:" + position + "\nx:" + point.getxData() + "\ny:" + point.getyData());
}
});
致此大致步驟完成了,發(fā)現(xiàn)和上一篇步驟差不多。。代碼已上傳github:
https://github.com/MonkeyMushroom/LineChartView/tree/master
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解Android圖表 MPAndroidChart折線圖
- MPAndroidChart開(kāi)源圖表庫(kù)的使用介紹之餅狀圖、折線圖和柱狀圖
- Android MPAndroidChart開(kāi)源庫(kù)圖表之折線圖的實(shí)例代碼
- Android自定義View實(shí)現(xiàn)折線圖效果
- Android繪制動(dòng)態(tài)折線圖
- Android HelloChart開(kāi)源庫(kù)圖表之折線圖的實(shí)例代碼
- Android開(kāi)發(fā)之天氣趨勢(shì)折線圖
- Android自定義控件實(shí)現(xiàn)折線圖
- Android自定義可左右滑動(dòng)和點(diǎn)擊的折線圖
- Android開(kāi)發(fā)RecyclerView實(shí)現(xiàn)折線圖效果
相關(guān)文章
Eclipse工程轉(zhuǎn)為兼容Android Studio模式的方法步驟圖文詳解
這篇文章主要介紹了Eclipse工程轉(zhuǎn)為兼容Android Studio模式的方法步驟,本文圖文并茂給大家介紹的非常詳細(xì),需要的朋友可以參考下2017-12-12
實(shí)例講解Android App使用自帶的SQLite數(shù)據(jù)庫(kù)的基本方法
這篇文章主要介紹了Android App使用自帶的SQLite數(shù)據(jù)庫(kù)的基本方法,SQLite是一個(gè)小巧的內(nèi)嵌型數(shù)據(jù)庫(kù),在數(shù)據(jù)庫(kù)需求不大的情況下使用SQLite其實(shí)非常有效,需要的朋友可以參考下2016-04-04
淺談Android手機(jī)聯(lián)系人開(kāi)發(fā)之增刪查改功能
這篇文章主要介紹了Android手機(jī)聯(lián)系人開(kāi)發(fā)之增刪查改功能,需要的朋友可以參考下2017-05-05
Android星級(jí)評(píng)分條的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Android星級(jí)評(píng)分條的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
Android編程簡(jiǎn)單實(shí)現(xiàn)撥號(hào)器功能的方法
這篇文章主要介紹了Android編程簡(jiǎn)單實(shí)現(xiàn)撥號(hào)器功能的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android撥號(hào)器功能的實(shí)現(xiàn)原理、步驟、操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-07-07
android獲取圖片尺寸的兩種方式及bitmap的縮放操作
這篇文章主要介紹了android獲取圖片尺寸的兩種方式及bitmap的縮放操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
Android藍(lán)牙通信聊天實(shí)現(xiàn)發(fā)送和接受功能
這篇文章主要為大家詳細(xì)介紹了Android藍(lán)牙通信聊天實(shí)現(xiàn)發(fā)送和接受功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07

