Android開(kāi)發(fā)之天氣趨勢(shì)折線圖
先來(lái)看下效果:

控件內(nèi)容比較簡(jiǎn)單,就是一個(gè)普通的折線圖,上下分別帶有數(shù)字,點(diǎn)擊的時(shí)候顯示當(dāng)天溫度的差值。
創(chuàng)建一個(gè)類繼承自View,并添加兩個(gè)構(gòu)造方法:
public class TrendGraph extends View {
public TrendGraph(Context context) { // 在java代碼中創(chuàng)建調(diào)用
super(context);
}
public TrendGraph(Context context, AttributeSet attrs) { // 在xml中創(chuàng)建調(diào)用
super(context, attrs);
}
}
因?yàn)檫@里不需要考慮wrap_content的情況,所以onMeasure方法不需重寫(xiě),關(guān)鍵的是onDraw,而onDraw方法其實(shí)也不困難,只需要確定好每個(gè)點(diǎn)的具體位置就好,因?yàn)檫B線也是需要點(diǎn)的坐標(biāo),代碼比較啰嗦,可以略過(guò):
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mElements == null || mElements.size() == 0) {
return;
}
double max_up = getMaxUp();
double min_down = getMinDown();
canvas.setDrawFilter(mDrawFilter);
mPaint.setStrokeWidth(lineWeith);
float width = getWidth();
float grap = width / mElements.size();
float textSize = mTextPaint.getTextSize();
int textMargin = circleRadius * 2;
float margin_top = textSize + 2 * textMargin;
Log.d(TAG, "onDraw: " + margin_top + "|" + textSize);
float height = getHeight() - 2 * margin_top;
for (int i = 0; i < mElements.size() - 1; i++) {
float startX = i * grap + grap / 2;
float stopX = (i + 1) * grap + grap / 2;
float startY = (float) (max_up - mElements.get(i).getUp()) / (float) (max_up -
min_down) * height + margin_top;
float stopY = (float) (max_up - mElements.get(i + 1).getUp()) / (float) (max_up -
min_down) * height + margin_top;
canvas.drawText((int) mElements.get(i).getUp() + "℃", startX - textSize, startY -
textMargin, mTextPaint);
canvas.drawCircle(startX, startY, circleRadius, mPaint);
canvas.drawLine(startX, startY, stopX, stopY, mPaint);
if (i == mElements.size() - 2) {
canvas.drawText((int) mElements.get(i + 1).getUp() + "℃", stopX - textSize, stopY
- textMargin, mTextPaint);
canvas.drawCircle(stopX, stopY, circleRadius, mPaint);
}
startY = (float) (max_up - mElements.get(i).getDown()) / (float) (max_up - min_down) *
height + margin_top;
stopY = (float) (max_up - mElements.get(i + 1).getDown()) / (float) (max_up -
min_down) * height + margin_top;
canvas.drawText((int) mElements.get(i).getDown() + "℃", startX - textSize, startY +
textSize + textMargin, mTextPaint);
canvas.drawCircle(startX, startY, circleRadius, mPaint);
canvas.drawLine(startX, startY, stopX, stopY, mPaint);
if (i == mElements.size() - 2) {
canvas.drawText((int) mElements.get(i + 1).getDown() + "℃", stopX - textSize,
stopY + textSize + textMargin, mTextPaint);
canvas.drawCircle(stopX, stopY, circleRadius, mPaint);
}
}
}
考慮到需要允許用戶進(jìn)行簡(jiǎn)單的設(shè)置,例如點(diǎn)的大小,文字大小等等,所以定義一些自定義屬性(res/values/attr.xml):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TrendGraph">
<attr name="lineWidth" format="dimension"/>
<attr name="circleRadius" format="dimension" />
<attr name="textSize" format="dimension" />
<attr name="textColor" format="reference" />
</declare-styleable>
</resources>
format指該屬性的格式,指定為dimension則是尺寸,取值單位是dp、sp或px等等,而reference則是引用,即一般在xml中引用其他資源的寫(xiě)法,如@string/app_name。還有其他類型,可以自行查找文檔。
對(duì)自定義屬性進(jìn)行解析得到,這個(gè)解析需要在上面定義的第二個(gè)構(gòu)造方法中進(jìn)行,代碼如下:
public TrendGraph(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.TrendGraph);
circleRadius = array.getDimensionPixelSize(R.styleable.TrendGraph_circleRadius, 5);
lineWeith = array.getDimensionPixelSize(R.styleable.TrendGraph_lineWidth, 3);
mTextPaint.setTextSize(array.getDimensionPixelSize(R.styleable.TrendGraph_textSize, 35));
mTextPaint.setColor(array.getColor(R.styleable.TrendGraph_textColor, Color.BLACK));
array.recycle();
}
getDimensionPixelSize方法則是通過(guò)傳入的值,轉(zhuǎn)換為具體的像素(px)值,也就免去我們手動(dòng)轉(zhuǎn)換的麻煩。但是要注意,其中的defaultValue依然是px。
接著,就可以通過(guò)xml指定這些屬性,在布局中加入命名空間:
xmlns:app=http://schemas.android.com/apk/res-auto
則Android Studio會(huì)自動(dòng)引入,并且可以補(bǔ)全得到,具體使用:
<com.fndroid.byweather.views.TrendGraph
android:id="@+id/tg"
android:layout_width="match_parent"
app:textColor="@color/colorAccent"
app:textSize="22sp"
app:circleRadius="2dp"
android:layout_height="200dp"/>
最后,添加一個(gè)事件監(jiān)聽(tīng),在點(diǎn)擊View的時(shí)候進(jìn)行回調(diào):
① 定義接口:
public interface onItemClickListener{
void onItemClick(View view, Element element);
}
② 在View中添加接口對(duì)象,并設(shè)置setter方法:
public class TrendGraph extends View {
private onItemClickListener mOnItemClickListener;
// 省略代碼
public void setOnItemClickListener(onItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
}
③ 處理onTouchEvent,重寫(xiě)該方法,代碼如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
int viewWidth = getWidth();
int itemWidth = viewWidth / mElements.size();
int viewHeight = getHeight();
boolean isMove = false; // 界面中最外層為一個(gè)NestedScrollView,所以為了避免滑動(dòng)時(shí)也觸發(fā),加入變量處理
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
isMove = true;
break;
case MotionEvent.ACTION_UP:
if (!isMove){ // 判斷只有點(diǎn)擊時(shí)進(jìn)行回調(diào)
int position = (int) (event.getX() / itemWidth); // 取得點(diǎn)擊的位置
mOnItemClickListener.onItemClick(this, mElements.get(position)); // 回調(diào)
}
break;
}
return true;
}
④ 在Activity中,進(jìn)行監(jiān)聽(tīng)設(shè)置,并處理:
historyGraph.setOnItemClickListener(this);
@Override
public void onItemClick(View view, TrendGraph.Element element) {
int dt = (int) (element.getUp() - element.getDown());
Snackbar.make(root, "當(dāng)天溫差為:" + dt + "℃", Snackbar.LENGTH_SHORT).show();
}
總結(jié)
效果完成!如果有疑問(wèn)歡迎大家交流討論,希望本文對(duì)大家開(kāi)發(fā)Android能有所幫助。
- 詳解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自定義控件實(shí)現(xiàn)折線圖
- Android自定義可左右滑動(dòng)和點(diǎn)擊的折線圖
- Android自定義View簡(jiǎn)易折線圖控件(二)
- Android開(kāi)發(fā)RecyclerView實(shí)現(xiàn)折線圖效果
相關(guān)文章
Android中的popupwindow進(jìn)入和退出的動(dòng)畫(huà)效果
這篇文章主要介紹了Android中的popupwindow進(jìn)入和退出的動(dòng)畫(huà),需要的朋友可以參考下2017-04-04
Android中搜索圖標(biāo)和文字居中的EditText實(shí)例
本篇文章主要介紹了Android中搜索圖標(biāo)和文字居中的EditText實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-06-06
Android ExpandableListView單選以及多選實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Android ExpandableListView單選以及多選的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android RecyclerView的Item點(diǎn)擊事件實(shí)現(xiàn)整理
這篇文章主要介紹了Android RecyclerView的Item點(diǎn)擊事件實(shí)現(xiàn)整理的相關(guān)資料,需要的朋友可以參考下2017-01-01
Android中控制和禁止ScrollView自動(dòng)滑動(dòng)到底部的方法
這篇文章主要給大家介紹了關(guān)于Android中控制和禁止ScrollView自動(dòng)滑動(dòng)到底部的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10
Android開(kāi)發(fā)中ViewPager實(shí)現(xiàn)多頁(yè)面切換效果
ViewPager用于實(shí)現(xiàn)多頁(yè)面的切換效果,該類存在于Google的兼容包里面,所以在引用時(shí)記得在BuilldPath中加入“Android-support-v4.jar”。具體詳情大家可以參考下本文2016-11-11

