編輯:關於Android編程
先來看下效果:
控件內容比較簡單,就是一個普通的折線圖,上下分別帶有數字,點擊的時候顯示當天溫度的差值。
創建一個類繼承自View,並添加兩個構造方法:
public class TrendGraph extends View { public TrendGraph(Context context) { // 在java代碼中創建調用 super(context); } public TrendGraph(Context context, AttributeSet attrs) { // 在xml中創建調用 super(context, attrs); } }
因為這裡不需要考慮wrap_content的情況,所以onMeasure方法不需重寫,關鍵的是onDraw,而onDraw方法其實也不困難,只需要確定好每個點的具體位置就好,因為連線也是需要點的坐標,代碼比較啰嗦,可以略過:
@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); } } }
考慮到需要允許用戶進行簡單的設置,例如點的大小,文字大小等等,所以定義一些自定義屬性(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中引用其他資源的寫法,如@string/app_name。還有其他類型,可以自行查找文檔。
對自定義屬性進行解析得到,這個解析需要在上面定義的第二個構造方法中進行,代碼如下:
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方法則是通過傳入的值,轉換為具體的像素(px)值,也就免去我們手動轉換的麻煩。但是要注意,其中的defaultValue依然是px。
接著,就可以通過xml指定這些屬性,在布局中加入命名空間:
xmlns:app=http://schemas.android.com/apk/res-auto
則Android Studio會自動引入,並且可以補全得到,具體使用:
<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"/>
最後,添加一個事件監聽,在點擊View的時候進行回調:
① 定義接口:
public interface onItemClickListener{ void onItemClick(View view, Element element); }
② 在View中添加接口對象,並設置setter方法:
public class TrendGraph extends View { private onItemClickListener mOnItemClickListener; // 省略代碼 public void setOnItemClickListener(onItemClickListener onItemClickListener) { mOnItemClickListener = onItemClickListener; } }
③ 處理onTouchEvent,重寫該方法,代碼如下:
@Override public boolean onTouchEvent(MotionEvent event) { int viewWidth = getWidth(); int itemWidth = viewWidth / mElements.size(); int viewHeight = getHeight(); boolean isMove = false; // 界面中最外層為一個NestedScrollView,所以為了避免滑動時也觸發,加入變量處理 switch (event.getAction()) { case MotionEvent.ACTION_MOVE: isMove = true; break; case MotionEvent.ACTION_UP: if (!isMove){ // 判斷只有點擊時進行回調 int position = (int) (event.getX() / itemWidth); // 取得點擊的位置 mOnItemClickListener.onItemClick(this, mElements.get(position)); // 回調 } break; } return true; }
④ 在Activity中,進行監聽設置,並處理:
historyGraph.setOnItemClickListener(this); @Override public void onItemClick(View view, TrendGraph.Element element) { int dt = (int) (element.getUp() - element.getDown()); Snackbar.make(root, "當天溫差為:" + dt + "℃", Snackbar.LENGTH_SHORT).show(); }
總結
效果完成!如果有疑問歡迎大家交流討論,希望本文對大家開發Android能有所幫助。
現在,玩微信的朋友有很多,我們用微信不僅可以和朋友家人聊天聯絡,還可以隨時隨地發朋友圈,那麼微信朋友圈怎麼發文字,下面下載吧小編教你一個簡單的方法來用微信朋
我們都知道取消標題欄有兩種方式,一種是在Java代碼中取消,另一種通過設置styles.xml文件中的Theme即可;如下圖:第一種:第二種:但是運行在Android 5
1.Handler是什麼?Handler是Android給我們提供的一套更新UI的機制,也封裝了一套消息處理的機制,可以發送消息,也可以通過它處理消息。eg:Handle
android中提供了4中動畫: AlphaAnimation 透明度動畫效果 ScaleAnimation 縮放動畫效果 TranslateAnimation 位移動畫
status_t AudioSystem::setStreamVolum