完整代码如下
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import com.ast.smartlighter.R;
import com.ast.smartlighter.utils.UIUtils;
import java.util.ArrayList;
import java.util.List;
//仿微信运动步数折线分布图
public class WXSportStatistics extends View implements OnTouchListener {
float mHigh = 0;
float Width = 0;
List<String> listValue = new ArrayList<String>();
List<RectF> listRectF = new ArrayList<RectF>();
List<String> listDAY = new ArrayList<String>();
boolean isKcal = false;
Float MaxValue = 0f;
private Paint paintbg;
private Paint paintCircle;
private Paint paintLine;
private Paint paint;
private Paint paintText;
private Paint paintText2;
private Path path;
Shader mShader;
int color_w = Color.argb(200, 255, 255, 255);
int CircleR = 5;
//4fd4d0
int bottomH = 0;//距离底部高度,给文字显示用的
int topH = 0;//距离顶部部高度,给选中后的文字显示用的
Context context;
public int select = -1;
public int selectbottom = -1;
public WXSportStatistics(Context context, AttributeSet attrs) {
super(context, attrs);
CircleR = UIUtils.dip2px(context, 3);
bottomH = UIUtils.dip2px(context, 12);
topH = UIUtils.dip2px(context, 16);
this.context = context;
initView();
}
public WXSportStatistics(Context context) {
super(context);
CircleR = UIUtils.dip2px(context, 3);
bottomH = UIUtils.dip2px(context, 12);
topH = UIUtils.dip2px(context, 16);
this.context = context;
initView();
}
private void initView() {
setOnTouchListener(this);
mHigh = getHeight();
Width = getWidth();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(2);
paint.setColor(Color.rgb(255, 255, 255));
paint.setStyle(Paint.Style.FILL);
paintText = new Paint();
paintText.setAntiAlias(true);
paintText.setStrokeWidth(4);
paintText.setColor(getResources().getColor(R.color.white));//Color.rgb(255, 255, 255));
paintText.setStyle(Paint.Style.FILL);
paintText.setTextSize(16);
paintText2 = new Paint();
paintText2.setAntiAlias(true);
paintText2.setStrokeWidth(4);
paintText2.setColor(Color.WHITE);//.rgb(33, 35, 59));
paintText2.setStyle(Paint.Style.FILL);
paintText2.setTextSize(20);
paintLine = new Paint();
paintLine.setAntiAlias(true);
paintLine.setStrokeWidth(1);
paintLine.setColor(Color.rgb(255, 255, 255));
paintLine.setStyle(Paint.Style.STROKE);
PathEffect effects = new DashPathEffect(new float[]{6, 4, 6, 4}, 1);
paintLine.setPathEffect(effects);
paintbg = new Paint();
paintbg.setAntiAlias(true);
paintbg.setStrokeWidth(0);
paintbg.setColor(Color.rgb(255, 255, 255));
paintbg.setStyle(Paint.Style.FILL);
paintCircle = new Paint();
paintCircle.setAntiAlias(true);
paintCircle.setStrokeWidth(2);
paintCircle.setStyle(Paint.Style.FILL);
paintCircle.setColor(getResources().getColor(R.color.white));
path = new Path();
mShader = new LinearGradient(0, 0, 0, getHeight(), new int[]{color_w,
getResources().getColor(R.color.transparency)}, null, Shader.TileMode.CLAMP);
paintbg.setShader(mShader);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float High = mHigh - bottomH;
if (listValue != null && listValue.size() != 0 && listDAY != null && listDAY.size() != 0) {
for (int i = 0; i < listValue.size(); i++) {
float h = High - High * Float.parseFloat(listValue.get(i)) / MaxValue;
h = h + topH;
float w = (float) (Width / listValue.size() * (i + 0.5));
if (High * Float.parseFloat(listValue.get(i)) / MaxValue < CircleR) {
h = High - CircleR - 1;
}
if (h < CircleR) {
h = CircleR + 1;
}
if (h > High - CircleR) {
h = High - CircleR + 1;
}
if (MaxValue == 0)
h = High - CircleR - 1;
if (i == 0) {
path.moveTo(w, h);
} else if (i == listValue.size() - 1) {
path.lineTo(w, h);
path.lineTo(w, High);
path.lineTo((float) (Width / listValue.size() * 0.5), High);
path.lineTo((float) (Width / listValue.size() * 0.5),
High - High * Float.parseFloat(listValue.get(0)) / MaxValue);
path.close();
canvas.drawPath(path, paintbg);
} else {
path.lineTo(w, h);
}
if (i != listValue.size() - 1) {
float stopY = High - High * Float.parseFloat(listValue.get(i + 1)) / MaxValue + topH;
float stopX = (float) (Width / listValue.size() * (i + 1.5));
if (High * Float.parseFloat(listValue.get(i + 1)) / MaxValue < CircleR) {
stopY = High - CircleR - 1;
}
if (stopY < CircleR) {
stopY = CircleR + 1;
}
if (MaxValue == 0)
stopY = High - CircleR - 1;
if (stopY > High - CircleR) {
stopY = High - CircleR + 1;
}
canvas.drawLine(w, h, stopX, stopY, paint);
}
}
listRectF.clear();
for (int i = 0; i < listDAY.size(); i++) {
float w = (float) (Width / listValue.size() * (i + 0.5));
String day = listDAY.get(i);
if (listDAY.size() == 7) {
paintText.setTextSize(UIUtils.dip2px(getContext(), 10));
paintText2.setTextSize(UIUtils.dip2px(getContext(), 10));
int width = getTextWidth(paintText, day);
if (selectbottom == i) {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText2);
selectbottom = -1;
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText);
}
} else if (listDAY.size() == 12) {
paintText.setTextSize(UIUtils.dip2px(getContext(), 10));
paintText2.setTextSize(UIUtils.dip2px(getContext(), 10));
int width = getTextWidth(paintText, day);
if (selectbottom == i) {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText2);
selectbottom = -1;
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText);
}
} else if (listDAY.size() == 30) {
paintText.setTextSize(UIUtils.dip2px(getContext(), 10));
paintText2.setTextSize(UIUtils.dip2px(getContext(), 10));
if (i == 0 || i == 7 || i == 15 || i == 22 || i == 29) {
int width = getTextWidth(paintText, day);
if (selectbottom == i) {
if (i == 0) {
canvas.drawText(day, w, mHigh - 2, paintText2);
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText2);
}
selectbottom = -1;
} else {
if (i == 0) {
canvas.drawText(day, w, mHigh - 2, paintText);
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText);
}
}
}
} else if (listDAY.size() == 31) {
paintText.setTextSize(UIUtils.dip2px(getContext(), 10));
paintText2.setTextSize(UIUtils.dip2px(getContext(), 10));
if (i == 0 || i == 7 || i == 15 || i == 22 || i == 30) {
int width = getTextWidth(paintText, day);
if (selectbottom == i) {
if (i == 0) {
canvas.drawText(day, w, mHigh - 2, paintText2);
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText2);
}
selectbottom = -1;
} else {
if (i == 0) {
canvas.drawText(day, w, mHigh - 2, paintText);
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText);
}
}
}
} else if (listDAY.size() == 29) {
paintText.setTextSize(UIUtils.dip2px(getContext(), 10));
paintText2.setTextSize(UIUtils.dip2px(getContext(), 10));
if (i == 0 || i == 7 || i == 15 || i == 22 || i == 28) {
int width = getTextWidth(paintText, day);
if (selectbottom == i) {
if (i == 0) {
canvas.drawText(day, w, mHigh - 2, paintText2);
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText2);
}
selectbottom = -1;
} else {
if (i == 0) {
canvas.drawText(day, w, mHigh - 2, paintText);
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText);
}
}
}
} else if (listDAY.size() == 28) {
paintText.setTextSize(UIUtils.dip2px(getContext(), 10));
paintText2.setTextSize(UIUtils.dip2px(getContext(), 10));
if (i == 0 || i == 7 || i == 15 || i == 22 || i == 27) {
int width = getTextWidth(paintText, day);
if (selectbottom == i) {
if (i == 0) {
canvas.drawText(day, w, mHigh - 2, paintText2);
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText2);
}
selectbottom = -1;
} else {
if (i == 0) {
canvas.drawText(day, w, mHigh - 2, paintText);
} else {
canvas.drawText(day, w - width / 2, mHigh - 2, paintText);
}
}
}
}
}
for (int i = 0; i < listValue.size(); i++) {
float h = High - High * Float.parseFloat(listValue.get(i)) / MaxValue + topH;
float w = (float) (Width / listValue.size() * (i + 0.5));
if (High * Float.parseFloat(listValue.get(i)) / MaxValue < CircleR) {
h = High - CircleR - 1;
}
if (h < CircleR) {
h = CircleR + 1;
}
if (h > High - CircleR) {
h = High - CircleR + 1;
}
if (MaxValue == 0)
h = High - CircleR - 1;
canvas.drawCircle(w, h, CircleR, paint);
RectF f1 = new RectF();
float wrf = (float) (Width / listValue.size() / 2);
f1.set(w - wrf, 0, w + wrf, getHeight());
if (select == i) {
int width = getTextWidth(paintText2, Float.parseFloat(listValue.get(i)) + "");
int high = (int) getTextHigh(paintText2);
if (w - width / 2 < 0) {
canvas.drawText(Float.parseFloat(listValue.get(i)) + "", w, high, paintText2);
} else if (w + width / 2 > Width) {
canvas.drawText(Float.parseFloat(listValue.get(i)) + "", w - width, high, paintText2);
} else {
canvas.drawText(Float.parseFloat(listValue.get(i)) + "", w - width / 2, high,
paintText2);
}
select = -1;
}
listRectF.add(f1);
}
}
}
Animation popup_enter_bottom;
Animation popup_out_bottom;
SelectItem mselectItem;
int vid = 0;
public void setValue(final List<String> listValue, final boolean cal, final boolean anim,
final List<String> listDay, SelectItem mselectItem, int vid) {
this.mselectItem = mselectItem;
this.vid = vid;
this.listDAY = new ArrayList<String>();
;
this.listDAY.addAll(listDay);
this.isKcal = cal;
if (this.listValue != null && this.listValue.size() != 0 && anim) {
popup_out_bottom = AnimationUtils.loadAnimation(getContext(), R.anim.sacle_bottom_out);
startAnimation(popup_out_bottom);
popup_out_bottom.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation arg0) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationRepeat(Animation arg0) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation arg0) {
setVisibility(View.INVISIBLE);
play(listValue, cal, anim, 10);
}
});
} else {
play(listValue, cal, anim, 600);
}
}
private void play(final List<String> listValue, final boolean cal, boolean anim, int time) {
this.listValue = new ArrayList<String>();
this.listValue.addAll(listValue);
MaxValue = 0f;
post(new Runnable() {
@Override
public void run() {
for (String a : listValue) {
if (Float.parseFloat(a) > MaxValue)
MaxValue = Float.parseFloat(a);
}
initView();
invalidate();
}
});
if (anim) {
setVisibility(View.INVISIBLE);
popup_enter_bottom = AnimationUtils.loadAnimation(getContext(), R.anim.sacle_bottom_in);
popup_enter_bottom.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation arg0) {
setVisibility(View.VISIBLE);
}
@Override
public void onAnimationRepeat(Animation arg0) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation arg0) {
// TODO Auto-generated method stub
}
});
postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
startAnimation(popup_enter_bottom);
}
}, time);
}
}
@Override
public boolean onTouch(View arg0, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
float x = event.getX();
float y = event.getY();
for (int i = 0; i < listRectF.size(); i++) {
if (listRectF.get(i).contains(x, y)) {
if (mselectItem != null) {
select = i;
selectbottom = i;
mselectItem.onSelectItem(this.vid, i);
}
break;
}
}
}
return true;
}
public void ShowView() {
setValue(this.listValue, this.isKcal, false, this.listDAY, mselectItem, this.vid);
}
public interface SelectItem {
void onSelectItem(int vid, int item);
}
public int getTextWidth(Paint paint, String str) {
int iRet = 0;
if (str != null && str.length() > 0) {
int len = str.length();
float[] widths = new float[len];
paint.getTextWidths(str, widths);
for (int j = 0; j < len; j++) {
iRet += (int) Math.ceil(widths[j]);
}
}
return iRet;
}
public static float getTextHigh(Paint paint) {
Paint.FontMetrics fm = paint.getFontMetrics();
return fm.descent - fm.ascent;
}
}
用到的动画
R.anim.sacle_bottom_in
xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android" >
android:duration="500"
android:fillAfter="false"
android:fromXScale="1.0"
android:fromYScale="0.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="0%"
android:pivotY="100%"
android:toXScale="1.0"
android:toYScale="1.0" />
======================================================
R.anim.sacle_bottom_out
xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android" >
android:duration="300"
android:fillAfter="false"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="0%"
android:pivotY="100%"
android:toXScale="1.0"
android:toYScale="0.0" />
用到的工具类
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import com.ast.smartlighter.MyApplication;
import java.lang.reflect.Field;
public class UIUtils {
/**
* 获取屏幕宽度
*
* @param activity
* @return
* @Description:
*/
public static int getScreenWidth(Activity activity) {
if (activity == null) {
return 0;
}
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
return dm.widthPixels;
}
/**
* 获取屏幕高度
*
* @param activity
* @return
* @Description:
*/
public static int getScreenHeight(Activity activity) {
if (activity == null) {
return 0;
}
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
return dm.heightPixels;
}
/**
* 获取状态栏高度
*
* @param activity
* @return
* @Description:
*/
public static int getStatusBarHeight(Activity activity) {
int result = 0;
int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = activity.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public static int getStatusBarHeight(Context context) {
Class> c = null;
Object obj = null;
Field field = null;
int x = 0, statusBarHeight = 0;
try {
c = Class.forName("com.android.internal.R$dimen");
obj = c.newInstance();
field = c.getField("status_bar_height");
x = Integer.parseInt(field.get(obj).toString());
statusBarHeight = context.getResources().getDimensionPixelSize(x);
} catch (Exception e1) {
e1.printStackTrace();
}
return statusBarHeight;
}
/**
* 将px值转换为dip或dp值,保证尺寸大小不变
*
* @param pxValue
* @param scale (DisplayMetrics类中属性density)
* @return
*/
public static int px2dip(float pxValue) {
final float scale = MyApplication.getContext().getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 将dip或dp值转换为px值,保证尺寸大小不变
*
* @param dipValue
* @param scale (DisplayMetrics类中属性density)
* @return
*/
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
/**
* 将px值转换为sp值,保证文字大小不变
*
* @param pxValue
* @param fontScale (DisplayMetrics类中属性scaledDensity)
* @return
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
* 将sp值转换为px值,保证文字大小不变
*
* @param spValue
* @param fontScale (DisplayMetrics类中属性scaledDensity)
* @return
*/
public static int sp2px(float spValue) {
final float fontScale = MyApplication.getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
public static View inflate(int layoutId) {
return View.inflate(MyApplication.getContext(), layoutId, null);
}
public static <T extends Object> T checkNull(String msg, T t) {
if (t == null) {
throw new NullPointerException(msg + " can not be null!");
}
return t;
}
public static <T extends Object> T checkNull(T t) {
return checkNull("params", t);
}
/**
* @param bitmap 原图
* @param edgeLength 希望得到的正方形部分的边长
* @return 缩放截取正中部分后的位图。
*/
public static Bitmap centerSquareScaleBitmap(Bitmap bitmap, int edgeLength) {
if (null == bitmap || edgeLength <= 0) {
return null;
}
Bitmap result = bitmap;
int widthOrg = bitmap.getWidth();
int heightOrg = bitmap.getHeight();
if (widthOrg > edgeLength && heightOrg > edgeLength) {
//压缩到一个最小长度是edgeLength的bitmap
int longerEdge = (int) (edgeLength * Math.max(widthOrg, heightOrg) / Math.min(widthOrg, heightOrg));
int scaledWidth = widthOrg > heightOrg ? longerEdge : edgeLength;
int scaledHeight = widthOrg > heightOrg ? edgeLength : longerEdge;
Bitmap scaledBitmap;
try {
scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
} catch (Exception e) {
return null;
}
//从图中截取正中间的正方形部分。
int xTopLeft = (scaledWidth - edgeLength) / 2;
int yTopLeft = (scaledHeight - edgeLength) / 2;
try {
result = Bitmap.createBitmap(scaledBitmap, xTopLeft, yTopLeft, edgeLength, edgeLength);
scaledBitmap.recycle();
} catch (Exception e) {
return null;
}
}
return result;
}
//此方法,如果显示则隐藏,如果隐藏则显示
public static void hintKbOne(Context context) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
// 得到InputMethodManager的实例
if (imm.isActive()) {
// 如果开启
imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT,
InputMethodManager.HIDE_NOT_ALWAYS);
}
}
//隐藏键盘
public static void hidekeybroad(Context context, EditText et) {
InputMethodManager imm2 = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm2 != null) {
imm2.hideSoftInputFromWindow(et.getWindowToken(), 0);
}
}
}
//在代码中使用
android:id="@+id/wxsport"
android:layout_width="match_parent"
android:layout_height="match_parent" />
private void show(List<String> data, List<String> day) {
//设置数据源
wxsport.setValue(data, false, true, day, this, R.id.wxsport);
}
//每个点的点击事件
@Override
public void onSelectItem(int vid, int item) {
switch (vid) {
case R.id.wxsport:
wxsport.select = item;
wxsport.selectbottom = item;
wxsport.ShowView();
break;
default:
break;
}
}