編輯:關於Android編程
最近由於項目需要,研究了一些統計圖的做法,開始時,看了很多博文,大部分都是引用第三方的庫,雖然簡單,
易上手,但是功能太死板,有很多要求都是不能滿足的,所以經過研究,自己使用View中的canvas重新繪圖制作
統計圖。首先上幾張的效果圖吧。



點擊這裡下載(0分下載)
一、demo的結構
一個activity中嵌套了三個fragment(v4),是用viewpager對頁面進行滑動,下面是整個項目的結構:

二、核心代碼
首先是MainActivity,這個demo中只使用到一個activity,現在一個activity中鑲嵌多個fragment很火,
很多Android應用都在使用這個布局,例如微信,QQ等。這樣做節省了空間上的浪費。
package com.example.statisticalchart;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends FragmentActivity {
private ViewPager viewPager;
private List fragments;
private FragmentPagerAdapter adapter;
// 設置是否顯示動畫,為了防止在創建時就開啟動畫,用以下三個參數做了判斷,只有當看到視圖後才會顯示動畫
public static int flag1 = 2;
public static int flag2 = 1;
public static int flag3 = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
viewPager = (ViewPager) findViewById(R.id.record_viewpager);
fragments = new ArrayList();
RecordPager1 recordPager1 = new RecordPager1();
RecordPager2 recordPager2 = new RecordPager2();
RecordPager3 recordPager3 = new RecordPager3();
fragments.add(recordPager1);
fragments.add(recordPager2);
fragments.add(recordPager3);
adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
};
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (position == 0 && flag1 == 1) {
flag1 = 2;
fragments.get(0).onResume();
flag1 = 3;
}
if (position == 1 && flag2 == 1) {
flag2 = 2;
fragments.get(1).onResume();
flag2 = 3;
}
if (position == 2 && flag3 == 1) {
flag3 = 2;
fragments.get(2).onResume();
flag3 = 3;
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
}
package com.example.statisticalchart;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class HistogramView extends View {
private Paint xLinePaint;// 坐標軸 軸線 畫筆:
private Paint hLinePaint;// 坐標軸水平內部 虛線畫筆
private Paint titlePaint;// 繪制文本的畫筆
private Paint paint;// 矩形畫筆 柱狀圖的樣式信息
private int[] progress = { 2000, 5000, 6000, 8000, 500, 6000, 9000 };// 7
// 條,顯示各個柱狀的數據
private int[] aniProgress;// 實現動畫的值
private final int TRUE = 1;// 在柱狀圖上顯示數字
private int[] text;// 設置點擊事件,顯示哪一條柱狀的信息
private Bitmap bitmap;
// 坐標軸左側的數標
private String[] ySteps;
// 坐標軸底部的星期數
private String[] xWeeks;
private int flag;// 是否使用動畫
private HistogramAnimation ani;
public HistogramView(Context context) {
super(context);
init();
}
public HistogramView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
ySteps = new String[] { "10k", "7.5k", "5k", "2.5k", "0" };
xWeeks = new String[] { "周一", "周二", "周三", "周四", "周五", "周六", "周日" };
text = new int[] { 0, 0, 0, 0, 0, 0, 0 };
aniProgress = new int[] { 0, 0, 0, 0, 0, 0, 0 };
ani = new HistogramAnimation();
ani.setDuration(2000);
xLinePaint = new Paint();
hLinePaint = new Paint();
titlePaint = new Paint();
paint = new Paint();
// 給畫筆設置顏色
xLinePaint.setColor(Color.DKGRAY);
hLinePaint.setColor(Color.LTGRAY);
titlePaint.setColor(Color.BLACK);
// 加載畫圖
bitmap = BitmapFactory
.decodeResource(getResources(), R.drawable.column);
}
public void start(int flag) {
this.flag = flag;
this.startAnimation(ani);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight() - dp2px(50);
// 繪制底部的線條
canvas.drawLine(dp2px(30), height + dp2px(3), width - dp2px(30), height
+ dp2px(3), xLinePaint);
int leftHeight = height - dp2px(5);// 左側外周的 需要劃分的高度:
int hPerHeight = leftHeight / 4;// 分成四部分
hLinePaint.setTextAlign(Align.CENTER);
// 設置四條虛線
for (int i = 0; i < 4; i++) {
canvas.drawLine(dp2px(30), dp2px(10) + i * hPerHeight, width
- dp2px(30), dp2px(10) + i * hPerHeight, hLinePaint);
}
// 繪制 Y 周坐標
titlePaint.setTextAlign(Align.RIGHT);
titlePaint.setTextSize(sp2px(12));
titlePaint.setAntiAlias(true);
titlePaint.setStyle(Paint.Style.FILL);
// 設置左部的數字
for (int i = 0; i < ySteps.length; i++) {
canvas.drawText(ySteps[i], dp2px(25), dp2px(13) + i * hPerHeight,
titlePaint);
}
// 繪制 X 周 做坐標
int xAxisLength = width - dp2px(30);
int columCount = xWeeks.length + 1;
int step = xAxisLength / columCount;
// 設置底部的數字
for (int i = 0; i < columCount - 1; i++) {
// text, baseX, baseY, textPaint
canvas.drawText(xWeeks[i], dp2px(25) + step * (i + 1), height
+ dp2px(20), titlePaint);
}
// 繪制矩形
if (aniProgress != null && aniProgress.length > 0) {
for (int i = 0; i < aniProgress.length; i++) {// 循環遍歷將7條柱狀圖形畫出來
int value = aniProgress[i];
paint.setAntiAlias(true);// 抗鋸齒效果
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(sp2px(15));// 字體大小
paint.setColor(Color.parseColor("#6DCAEC"));// 字體顏色
Rect rect = new Rect();// 柱狀圖的形狀
rect.left = step * (i + 1);
rect.right = dp2px(30) + step * (i + 1);
int rh = (int) (leftHeight - leftHeight * (value / 10000.0));
rect.top = rh + dp2px(10);
rect.bottom = height;
canvas.drawBitmap(bitmap, null, rect, paint);
// 是否顯示柱狀圖上方的數字
if (this.text[i] == TRUE) {
canvas.drawText(value + "", dp2px(15) + step * (i + 1)
- dp2px(15), rh + dp2px(5), paint);
}
}
}
}
private int dp2px(int value) {
float v = getContext().getResources().getDisplayMetrics().density;
return (int) (v * value + 0.5f);
}
private int sp2px(int value) {
float v = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (v * value + 0.5f);
}
/**
* 設置點擊事件,是否顯示數字
*/
public boolean onTouchEvent(MotionEvent event) {
int step = (getWidth() - dp2px(30)) / 8;
int x = (int) event.getX();
for (int i = 0; i < 7; i++) {
if (x > (dp2px(15) + step * (i + 1) - dp2px(15))
&& x < (dp2px(15) + step * (i + 1) + dp2px(15))) {
text[i] = 1;
for (int j = 0; j < 7; j++) {
if (i != j) {
text[j] = 0;
}
}
if (Looper.getMainLooper() == Looper.myLooper()) {
invalidate();
} else {
postInvalidate();
}
}
}
return super.onTouchEvent(event);
}
/**
* 集成animation的一個動畫類
*
* @author 李垭超
*/
private class HistogramAnimation extends Animation {
protected void applyTransformation(float interpolatedTime,
Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f && flag == 2) {
for (int i = 0; i < aniProgress.length; i++) {
aniProgress[i] = (int) (progress[i] * interpolatedTime);
}
} else {
for (int i = 0; i < aniProgress.length; i++) {
aniProgress[i] = progress[i];
}
}
invalidate();
}
}
}
package com.example.statisticalchart;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class LineChartView extends View {
private Paint rectPaint;// 設置左側為白色,顯示數表
private Paint hLinePaint;// 坐標軸水平內部 虛線畫筆
private Paint titlePaint;// 繪制文本的畫筆
private Paint linePaint;
private Paint paint;// 矩形畫筆 柱狀圖的樣式信息
private int[] text;// 折線的轉折點
int x, y, preX, preY;
// 坐標軸左側的數標
private Bitmap mBitmap;
// 坐標軸底部的星期數
private String[] str = { "62", "72", "82", "92", "102", "112", "122",
"132", "142" };
private HistogramAnimation ani;
private int flag;
public LineChartView(Context context) {
super(context);
init(context, null);
}
public LineChartView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
text = new int[] { 6, 5, 5, 4, 5, 3, 2, 3, 1, 1 };
ani = new HistogramAnimation();
ani.setDuration(4000);
rectPaint = new Paint();
titlePaint = new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
linePaint = new Paint();
//
titlePaint.setAntiAlias(true);
Rect bundle1 = new Rect();
Rect bundle2 = new Rect();
hLinePaint = new Paint();
int perWidth = getWidth() / 10;// 將寬度分為10部分
int hPerHeight = getHeight() / 10;// 將高度分為10部分
rectPaint.setColor(Color.WHITE);
canvas.drawRect(0, 0, dp2px(30), getHeight(), rectPaint);// 畫一塊白色區域
Path path = new Path();// 折線圖的路徑
mBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Bitmap.Config.ARGB_8888);
Canvas mCanvas = new Canvas(mBitmap);
for (int i = 0; i < 10; i++) {// 畫x線,並在左側顯示相應的數值
hLinePaint.setTextAlign(Align.CENTER);
hLinePaint.setColor(Color.WHITE);
y = i * hPerHeight;
if (i == 2) {
hLinePaint.setStrokeWidth(4);
for (int j = 0; j < 10; j++) {
canvas.drawLine(dp2px(30) + j * perWidth, y, dp2px(28)
+ (j + 1) * perWidth, y, hLinePaint);
}
titlePaint.setTextSize(sp2px(20));
titlePaint.getTextBounds(str[i - 1], 0, str[i - 1].length(),
bundle1);
canvas.drawText(str[i - 1], dp2px(25) - bundle1.width(), i
* hPerHeight + (bundle1.height() / 2), titlePaint);
} else {
hLinePaint.setStrokeWidth(1);
canvas.drawLine(dp2px(30), y, getWidth(), y, hLinePaint);
if (i != 0) {
titlePaint.setTextSize(sp2px(15));
titlePaint.getTextBounds(str[i - 1], 0,
str[i - 1].length(), bundle2);
canvas.drawText(str[i - 1], dp2px(25) - bundle2.width(), i
* hPerHeight + (bundle2.height() / 2), titlePaint);
}
}
x = i * perWidth + dp2px(30);
if (i == 0) {
path.moveTo(x, text[i] * hPerHeight);
} else {
path.lineTo(x, text[i] * hPerHeight);
}
linePaint.setColor(Color.parseColor("#bb2222"));
linePaint.setAntiAlias(true);
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(dp2px(1));
if (i != 0) {
mCanvas.drawCircle(x, text[i] * hPerHeight, dp2px(3), linePaint);
}
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
mCanvas.drawPath(path, paint);
}
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
paint.setStyle(Paint.Style.FILL);
mCanvas.drawRect(preX + dp2px(30), 0, getWidth(), getHeight(), paint);
canvas.drawBitmap(mBitmap, 0, 0, null);
// Log.i("tag", "onDraw()1111");
}
private int dp2px(int value) {
float v = getContext().getResources().getDisplayMetrics().density;
return (int) (v * value + 0.5f);
}
private int sp2px(int value) {
float v = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (v * value + 0.5f);
}
public void start(int flag) {
startAnimation(ani);
this.flag = flag;
}
/**
* 集成animation的一個動畫類
*
* @author
*/
private class HistogramAnimation extends Animation {
@Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f && flag == 2) {
preX = (int) ((getWidth() - dp2px(30)) * interpolatedTime);
} else {
preX = getWidth();
}
invalidate();
}
}
}
package com.example.statisticalchart;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class PinChart extends View {
static Canvas c;
private Paint[] mPaints;
private RectF mBigOval;
float[] mSweep = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private int preWidth;
private mAnimation ani;
private int flag;
private int centerX;
private int centerY;
int valueX;
int valueY;
public static float[] humidity = { 110, 60, 50, 50, 40, 30, 10, 10 };
private String str[] = { "數據24%", "數據19%", "數據21%", "其他18%", "數據3%",
"數據3%", "數據4%", "數據6%" };
private final String color[] = { "#2cbae7", "#ffa500", "#ff5b3b",
"#9fa0a4", "#6a71e5", "#f83f5d", "#64a300", "#64ef85" };
public PinChart(Context context) {
super(context);
initView();
}
public PinChart(Context context, AttributeSet atr) {
super(context, atr);
initView();
}
private void initView() {
ani = new mAnimation();
ani.setDuration(2000);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);// 設置背景顏色(透明)
mPaints = new Paint[humidity.length];
for (int i = 0; i < humidity.length; i++) {
mPaints[i] = new Paint();
mPaints[i].setAntiAlias(true);
mPaints[i].setStyle(Paint.Style.FILL);
mPaints[i].setColor(Color.parseColor(color[i]));
}
int cicleWidth = getWidth() - dp2px(60);
centerX = getWidth() / 2;
centerY = dp2px(10) + cicleWidth / 2;
preWidth = (getWidth() - dp2px(40)) / 4;
int half = getWidth() / 2;
mBigOval = new RectF();// 餅圖的四周邊界
mBigOval.top = dp2px(10);
mBigOval.left = half - cicleWidth / 2;
mBigOval.bottom = dp2px(10) + cicleWidth;
mBigOval.right = half + cicleWidth / 2;
float start = -180;
Rect bounds = new Rect();
for (int i = 0; i < humidity.length; i++) {
canvas.drawArc(mBigOval, start, mSweep[i], true, mPaints[i]);
if (humidity[i] > 45) {
mPaints[i].setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.SRC_OVER));
mPaints[i].setAntiAlias(true);
mPaints[i].setColor(Color.WHITE);
mPaints[i].getTextBounds(str[i], 0, str[i].length(), bounds);
mPaints[i].setTextSize(sp2px(15));
measureText(start + 180, humidity[i], cicleWidth / 3, i);
canvas.drawText(str[i], valueX - mPaints[i].measureText(str[i])
/ 2, valueY + bounds.height() / 2, mPaints[i]);
}
start += humidity[i];
int j = 1;
int k;
if (i < 4) {
j = 0;
k = i;
} else {
j = 1;
k = i - 4;
}
mPaints[i] = new Paint();
mPaints[i].setAntiAlias(true);
mPaints[i].setStyle(Paint.Style.FILL);
mPaints[i].setColor(Color.parseColor(color[i]));
canvas.drawRect(new RectF(dp2px(20) + preWidth * k, cicleWidth
+ dp2px(j * 30 + 20), dp2px(20) + preWidth * (k + 1),
cicleWidth + dp2px(50 + j * 30)), mPaints[i]);
mPaints[i].setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.SRC_OVER));
mPaints[i].setAntiAlias(true);
mPaints[i].setColor(Color.WHITE);
mPaints[i].getTextBounds(str[i], 0, str[i].length(), bounds);
mPaints[i].setTextSize(sp2px(15));
canvas.drawText(str[i], dp2px(20) + preWidth * k + preWidth / 2
- mPaints[i].measureText(str[i]) / 2, cicleWidth
+ dp2px(j * 30 + 20)
+ (dp2px(30) / 2 + bounds.height() / 2), mPaints[i]);
}
}
/**
* 顯示相應區域字開始的x,y坐標
*
* @param start
* @param angle
* @param radius
* @param i
*/
private void measureText(float start, float angle, int radius, int i) {
float temp = start + (angle / 2);
if (temp < 90) {
valueX = (int) (centerX - Math.abs(radius
* Math.sin((temp / 180) * Math.PI)));
valueY = (int) (centerY - Math.abs(radius
* Math.cos((temp / 180) * Math.PI)));
} else if (temp > 90 && temp < 180) {
temp = 180 - temp;
valueX = centerX
+ (int) Math
.abs((radius * Math.cos((temp / 180) * Math.PI)));
valueY = centerY
- (int) Math
.abs((radius * Math.sin((temp / 180) * Math.PI)));
} else if (temp > 180 && temp < 270) {
temp = temp - 180;
valueX = centerX
+ (int) Math
.abs((radius * Math.cos((temp / 180) * Math.PI)));
valueY = centerY
+ (int) Math
.abs((radius * Math.sin((temp / 180) * Math.PI)));
} else {
temp = 360 - temp;
valueX = centerX
- (int) Math
.abs((radius * Math.cos((temp / 180) * Math.PI)));
valueY = centerY
+ (int) Math
.abs((radius * Math.sin((temp / 180) * Math.PI)));
}
}
private int sp2px(int value) {
float v = getResources().getDisplayMetrics().scaledDensity;
return (int) (value * v + 0.5f);
}
private int dp2px(int value) {
float v = getResources().getDisplayMetrics().density;
return (int) (value * v + 0.5f);
}
public void start(int flag) {
startAnimation(ani);
this.flag = flag;
}
class mAnimation extends Animation {
@Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f && flag == 2) {
for (int i = 0; i < humidity.length; i++) {
mSweep[i] = humidity[i] * interpolatedTime;
}
} else if (flag == 1) {
for (int i = 0; i < humidity.length; i++) {
mSweep[i] = 0;
}
} else {
for (int i = 0; i < humidity.length; i++) {
mSweep[i] = humidity[i];
}
}
invalidate();
}
}
}
Android -- RecyclerView的使用
說明:RecyclerView是support-v7包中的新組件,你可以使用該組件替代ListView和CridView,從命名可以看出RecyclerView會自動回
Android文件夾管理器源碼實現
一、資源管理器介紹現在在一些移動終端上面都會有自帶的資源管理器,其實其並非是Android系統自帶,而是手機產商與app開發商的合作而導致融合,借助第三方的開發軟件預裝在
Android開源項目- 匯總
Android 開源項目第一篇——個性化控件(View)篇包括ListView、ActionBar、Menu、ViewPager、Gallery、
微信怎麼分享音樂 微信朋友圈怎麼分享音樂
在朋友圈,總能看到好友分享的音樂,自己也想來試試,微信怎麼分享音樂?小編將給大家介紹一下微信朋友圈的音樂是怎麼分享的,不懂的朋友可以了解下這裡我們以蘋果iP