編輯:關於Android編程
項目中最近用到各種圖表,本來打算用第三方的,例如MPAndroid,這是一個十分強大的圖表庫,應用起來十分方便,但是最終發現和設計不太一樣,沒辦法,只能自己寫了。今天將寫好的柱狀圖的demo貼在這,該柱狀圖可根據數據的功能有一下幾點:
1. 根據數據的多少,動態的繪制柱狀圖柱子的條數;
2. 柱狀圖每條柱子的繪制都有動態的動畫效果;
3. 每條柱子有點擊事件,點擊時彈出提示框,顯示相關信息,規定時間後,彈窗自動消失。
好了,先上演示圖:
下邊貼出相關代碼:
自定義柱狀圖類:
package com.example.histogram; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.os.Handler; import android.text.TextPaint; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.example.histogram.UI.UI; import java.text.NumberFormat; /** * Created by ZHANGZDon 2016/6/16 0016. * 柱狀圖 */ public class HistoGram extends View implements Runnable { private Handler handler = new Handler(); // 用於延時更新,實現動畫 private float animHeight; // 進度條動畫高度 private Paint axisLinePaint; // 坐標軸畫筆 private Paint hLinePaint; // 內部水平虛線畫筆 private Paint textPaint; // 繪制文本的畫筆 private Paint recPaint; // 繪制柱狀圖陰影背景的畫筆 private Paint dataPaint; // 繪制柱狀圖的畫筆 private Paint textPaint2; // 繪制白色文本的畫筆 private Paint textPaint3; // 繪制坐標的畫筆 private Paint textPaint4; // 繪制x軸上的白色豎線的畫筆 private String[] xTitleString; // x軸刻度 private String[] yTitleString; // y軸刻度 private String[] data; // 接口返回的indicatordata,用於計算柱子高度 NumberFormat numberFormat; //用於格式化數字 private float currentHeight; // 當前柱狀圖應有的高度,應由計算得來 private int num = -1; // 畫多少條柱子,因為存在剛開機數據不足24條的情況 private float mRelativePxInHeight; private float mRelativePxInWidth; private OnChartClickListener listener; private int mDist; public void setNum(int num) { this.num = num; invalidate(); } public void setData(String[] data) { this.data = data; invalidate(); } public void setxTitleString(String[] title) { this.xTitleString = title; invalidate(); } public HistoGram(Context context) { this(context, null); } public HistoGram(Context context, AttributeSet attrs) { this(context, attrs, 0); } public void setTitle(String[] title) { this.xTitleString = title; } public HistoGram(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } /** * 進行相關初始化操作 * @param context * @param attrs */ private void init(Context context, AttributeSet attrs) { axisLinePaint = new Paint(); hLinePaint = new Paint(); textPaint = new Paint(); recPaint = new Paint(); dataPaint = new Paint(); textPaint2 = new Paint(); textPaint3 = new Paint(); textPaint4 = new Paint(); numberFormat = NumberFormat.getNumberInstance(); numberFormat.setMinimumFractionDigits(3); //設置打印時保留三位小數 axisLinePaint.setColor(Color.parseColor("#dbdde4")); //設置坐標軸的顏色為白色 hLinePaint.setARGB(51, 255, 255, 255); textPaint.setColor(Color.parseColor("#8593a1")); // textPaint.setTextSize(29); textPaint.setTextSize(UI.dip2px(getContext(), 12)); recPaint.setColor(Color.parseColor("#f2f5fc")); dataPaint.setColor(Color.CYAN); textPaint2.setColor(Color.WHITE); textPaint2.setTextSize(UI.dip2px(getContext(), 12)); textPaint3.setColor(Color.parseColor("#000000")); textPaint3.setTextSize(UI.dip2px(getContext(), 9)); textPaint4.setColor(Color.parseColor("#8593a1")); textPaint4.setTextSize(UI.dip2px(getContext(), 6)); axisLinePaint.setAntiAlias(true); hLinePaint.setAntiAlias(true); textPaint.setAntiAlias(true); recPaint.setAntiAlias(true); dataPaint.setAntiAlias(true); textPaint2.setAntiAlias(true); textPaint3.setAntiAlias(true); textPaint4.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if(data == null || xTitleString == null || num < 0 ) { return; } //繪制y軸刻度 Paint.FontMetrics metrics = textPaint3.getFontMetrics(); int decent = (int) metrics.descent; float width = getWidth(); float height = getHeight(); //根據原型圖得出,圖中每px高度在實際中的相對尺寸 mRelativePxInHeight = height / 470; //根據原型圖得出,圖中每px寬度在實際中的相對尺寸 mRelativePxInWidth = width / 690; textPaint3.setTextAlign(Paint.Align.RIGHT); //繪制縱坐標 yTitleString = new String[6]; yTitleString[5] = "0"; yTitleString[4] = "20"; yTitleString[3] = "40"; yTitleString[2] = "60"; yTitleString[1] = "80"; yTitleString[0] = "100"; for (int i = 0; i < yTitleString.length; i++) { canvas.drawText(yTitleString[i], 88 * mRelativePxInWidth, (72 + i * 56) * mRelativePxInHeight + decent, textPaint3); } //繪制x軸刻度 textPaint3.setTextAlign(Paint.Align.CENTER); textPaint4.setTextAlign(Paint.Align.CENTER); TextPaint textPaint = new TextPaint(); textPaint.setColor(Color.parseColor("#000000")); textPaint.setTextSize(UI.dip2px(getContext(), 9)); //計算柱子之間的間隔 //最左側位置100 * mRelativePxInWidth,最右側位置630 ePxInWidth, float totalWidth = 630 - 100; // 柱子與之子之間的間隔 mDist = (int) (totalWidth / (xTitleString.length + 1)); for (int i = 0; i < xTitleString.length; i++) { //繪制白色豎線 canvas.drawLine((100 + (i+1) * mDist) * mRelativePxInWidth, 348 * mRelativePxInHeight, (100 + (i+1) * mDist) * mRelativePxInWidth, 352 * mRelativePxInHeight, axisLinePaint); //繪制x軸文字 canvas.drawText(xTitleString[i], (100 + (i+1) * mDist) * mRelativePxInWidth, 370 * mRelativePxInHeight, textPaint3); } // 繪制矩形陰影 for (int i = 0; i < num; i++) { RectF rectF = new RectF(); // rectF.left = 111 * relativePxInWidth + i * 22 * relativePxInWidth; // rectF.right = 121 * relativePxInWidth + i * 22 * relativePxInWidth; rectF.left = 95 * mRelativePxInWidth + (i+1) * mDist * mRelativePxInWidth; rectF.right = 105 * mRelativePxInWidth +(i+1) * mDist * mRelativePxInWidth; rectF.top = 70 * mRelativePxInHeight; rectF.bottom = 338 * mRelativePxInHeight; canvas.drawRoundRect(rectF, 10, 10, recPaint); } // 繪制x軸坐標線 for (int i = 0; i < 6; i++) { canvas.drawLine(100 * mRelativePxInWidth, (66 + i * 56) * mRelativePxInHeight + decent, 630 * mRelativePxInWidth, (66 + i * 56) * mRelativePxInHeight + decent, axisLinePaint); } // 延時繪制,實現動畫效果。數字越大,延時越久,動畫效果就會越慢 handler.postDelayed(this, 1); for (int i = 0; i < num; i++) { RectF dataRectF = new RectF(); dataRectF.left = 95 * mRelativePxInWidth + (i + 1) * mDist * mRelativePxInWidth; dataRectF.right = 105 * mRelativePxInWidth + (i + 1) * mDist * mRelativePxInWidth; dataPaint.setColor(Color.parseColor("#3ac2d9")); //獲取柱子高度 currentHeight = Float.parseFloat(data[num - 1 - i]); if (currentHeight == 0) { dataRectF.top = 346 * mRelativePxInHeight; } else if (currentHeight == 100) { dataRectF.top = 70 * mRelativePxInHeight; } else { if (animHeight >= currentHeight) { dataRectF.top = 346 * mRelativePxInHeight - currentHeight / 100 * 276 * mRelativePxInHeight; } else { dataRectF.top = 346 * mRelativePxInHeight - 276 * mRelativePxInHeight * (animHeight / 100); } } dataRectF.bottom = 346 * mRelativePxInHeight; // 限制最高高度 if (dataRectF.top < 70 * mRelativePxInHeight) { dataRectF.top = 70 * mRelativePxInHeight; } canvas.drawRoundRect(dataRectF, 10, 10, dataPaint); } } //實現柱子增長的動畫效果 @Override public void run() { animHeight += 1; if (animHeight >= 276 * mRelativePxInHeight) { return; } else { invalidate(); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { //獲取點擊坐標 float x = event.getX(); float y = event.getY(); //判斷點擊點的位置 float leftx = 0; float rightx = 0; for (int i = 0; i < num; i++) { leftx = 95 * mRelativePxInWidth + (i+ 1) * mDist * mRelativePxInWidth - mDist/2 * mRelativePxInWidth; rightx = 105 * mRelativePxInWidth + (i+ 1) * mDist * mRelativePxInWidth + mDist/2 * mRelativePxInWidth; if (x < leftx) { continue; } if (leftx <= x && x <= rightx) { //獲取點擊的柱子區域的y值 float top = 346 * mRelativePxInHeight - Float.parseFloat(data[num - 1 - i])/ 100 * 276 * mRelativePxInHeight; float bottom = 346 * mRelativePxInHeight; if (y >= top && y <= bottom) { //判斷是否設置監聽 //將點擊的第幾條柱子,點擊柱子頂部的坐值,用於彈出dialog提示數據,還要返回百分比currentHeidht = Float.parseFloat(data[num - 1 - i]) if(listener != null) { Log.e("ss","x" + x +";y:" + y); listener.onClick(i + 1, leftx + mDist/2,top,Float.parseFloat(data[num - 1 - i])); } break; } } } break; } case MotionEvent.ACTION_MOVE: Log.e("touch", "ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e("touch", "ACTION_UP"); break; } return true; } /** * 柱子點擊時的監聽接口 */ public interface OnChartClickListener { void onClick(int num, float x, float y, float value); } /** * 設置柱子點擊監聽的方法 * @param listener */ public void setOnChartClickListener(OnChartClickListener listener) { this.listener = listener; } }
在xml文件中的應用:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.histogram.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="繁忙度指示圖(%)" android:textSize="15sp" android:textColor="#000000" /> <com.example.histogram.HistoGram android:id="@+id/staticview" android:layout_width="400dp" android:layout_height="500dp" android:layout_gravity="center_horizontal" android:layout_marginBottom="14dp" android:layout_marginTop="5dp"/> </LinearLayout>
在activity中的實現:
package com.example.histogram; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.PopupWindow; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private PopupWindow mPopupWindow; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final HistoGram histoGram = (HistoGram) findViewById(R.id.staticview); String[] data ={"100","20","40","20","80","20","60","30","5","20","60","30","5","5","20","60","30","5"}; final String[] title = {"1","2","3","4","5","6","7","8","9","6","7","8","9","9","6","7","8","9"}; histoGram.setNum(title.length); histoGram.setData(data); histoGram.setxTitleString(title); histoGram.setOnChartClickListener(new HistoGram.OnChartClickListener() { @Override public void onClick(int num, float x, float y, float value) { //顯示提示窗 View inflate = View.inflate(MainActivity.this, R.layout.popupwindow, null); TextView textView = (TextView) inflate.findViewById(R.id.main_tv); textView.setText(value + "%\n" + title[num - 1]); if(mPopupWindow != null) { mPopupWindow.dismiss(); } mPopupWindow = new PopupWindow(inflate,140, 60, true); mPopupWindow.setTouchable(true); Log.e("ss","num" + num +";x" + x+";y"+ y + ";value" + value +";(int)((- histoGram.getHeight()) + y - 65)" +(int)((- histoGram.getHeight()) + y - 65) + "histoGram.getHeight()" + histoGram.getHeight()); // 設置好參數之後再show // Toast.makeText(MainActivity.this, "num" + num +";x" + x+";y"+ y + ";value" + value // +";popupWindow.getWidth()"+ mPopupWindow.getWidth()+";"+ mPopupWindow.getHeight(), Toast.LENGTH_SHORT).show(); mPopupWindow.showAsDropDown(histoGram,(int)(x - 65),(int)((- histoGram.getHeight()) + y - 65) ); mPopupWindow.setBackgroundDrawable(getResources().getDrawable(R.mipmap.databg_busyness)); new Handler().postDelayed(new Runnable(){ public void run() { mPopupWindow.dismiss(); } }, 1000); } }); } }
以上所述是小編給大家介紹的Android自定義帶增長動畫和點擊彈窗提示效果的柱狀圖,實現一個模擬後台數據登入的效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對本站網站的支持!
前言:從開始接觸rn到現在終於能寫出點東西了,的確得為自己好好地點個贊 ,不管咋樣,學習還是得繼續啊,廢話少說了,在rn中我們也需要對屏幕進行適配,但是rn中的適配貌似比
通過本文帶大家一起看過UIL這個國內外大牛都追捧的圖片緩存類庫的緩存處理機制。看了UIL中的緩存實現,才發現其實這個東西不難,沒有太多的進程調度,沒有各種內存讀取控制機制
先看一下項目的結構 可以看出每個功能模塊包含 XXActivity,XXView (Interface),XXPresenter(interfac
Android特效專輯(六)——仿QQ聊天撒花特效,無形裝逼,最為致命 日後我所寫的特效專輯也會以一添加在這個專欄上,今天寫的這個特效,是關於聊天