Android条形统计图实现
16lz
2021-12-04
StripeStaticsView.java
import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.os.Handler;import android.os.Message;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import com.careeach.sport.R;import java.util.List;/** * 条形统计图 */public class StripeStaticsView extends View { private List values; private List titles; private int height; private int width; private Paint stripePaint; private Paint stripeSelectedPaint; // 选中状态 private Paint titleTextPaint; // 标题字 private Paint bodyBackGroundPaint; private Paint titleBackGroundPaint; private float itemHeight; // 一个值对于高度 //ATTR private int itemColor; // 条形颜色 private float itemWidth; // 条形宽度 private float itemSpaceWidth; // 条形间隙 private int itemSelectColor; private int maxValue; private float centerX; private float startX; // 第一条条形起始X座标 private float moveWidth = 0f; private float touchStarX = 0; private float titleHeight; // 标题高度 private float titleTextY; // 标题Y坐标 private float titleTextSize; // 标题字体大小 private float titleTextLineSpace; // 标题行间隙 private int titleTextColor; // 标题字颜色 private int titleTextSelectColor; // 标题字颜色 private int bodyBackGroundColor; // 主背景 private int titleBackGroundColor; // 标题背景 private boolean isMove = false; private boolean isUp = false; private OnStripeStaticsListener onStripeStaticsListener; private int selectPosition = -1; private TouchEventCountThread touchEventCountThread; private final int HANDLER_WHAT_CLICK = 1; private final int HANDLER_WHAT_SELECT_CHANGE = 2; private ViewGroup parentViewGroup; public StripeStaticsView(Context context) { super(context); } public StripeStaticsView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs); } public StripeStaticsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } public StripeStaticsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context, attrs); } private void init(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StripeStaticsView); itemColor = typedArray.getColor(R.styleable.StripeStaticsView_itemColor, Color.BLACK); itemSelectColor = typedArray.getColor(R.styleable.StripeStaticsView_itemSelectColor, Color.WHITE); itemSpaceWidth = typedArray.getDimension(R.styleable.StripeStaticsView_itemSpaceWidth, Color.BLACK); itemWidth = typedArray.getDimension(R.styleable.StripeStaticsView_itemWidth, dip2px(20)); maxValue = typedArray.getInt(R.styleable.StripeStaticsView_maxValue, 100); titleTextSize = typedArray.getDimension(R.styleable.StripeStaticsView_titleTextSize, sp2px(12)); titleTextLineSpace = typedArray.getDimension(R.styleable.StripeStaticsView_titleTextLineSpace, dip2px(10)); titleTextColor = typedArray.getColor(R.styleable.StripeStaticsView_titleTextColor, Color.WHITE); titleTextSelectColor = typedArray.getColor(R.styleable.StripeStaticsView_titleTextSelectColor, Color.WHITE); bodyBackGroundColor = typedArray.getColor(R.styleable.StripeStaticsView_contentBGColor, Color.WHITE); titleBackGroundColor = typedArray.getColor(R.styleable.StripeStaticsView_titleBGColor, Color.WHITE); typedArray.recycle(); stripePaint = new Paint(); stripePaint.setColor(itemColor); stripePaint.setAntiAlias(true); stripePaint.setStyle(Paint.Style.FILL); stripeSelectedPaint = new Paint(); stripeSelectedPaint.setColor(itemSelectColor); stripeSelectedPaint.setAntiAlias(true); stripeSelectedPaint.setStyle(Paint.Style.FILL); titleTextPaint = new Paint(); titleTextPaint.setColor(titleTextColor); titleTextPaint.setTextSize(titleTextSize); titleTextPaint.setAntiAlias(true); bodyBackGroundPaint = new Paint(); bodyBackGroundPaint.setColor(bodyBackGroundColor); bodyBackGroundPaint.setStyle(Paint.Style.FILL); titleBackGroundPaint = new Paint(); titleBackGroundPaint.setColor(titleBackGroundColor); titleBackGroundPaint.setStyle(Paint.Style.FILL); touchEventCountThread = new TouchEventCountThread(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getMeasuredWidth(); height = getMeasuredHeight(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); centerX = (width - itemWidth) / 2; if (values != null && values.size() > 0) { startX = ((width / 2) + (itemWidth / 2)) - ((itemWidth * values.size()) + ((values.size() - 1) * itemSpaceWidth)); } titleHeight = titleTextSize + (titleTextLineSpace * 2); itemHeight = (height - titleHeight) / maxValue; titleTextY = height - titleTextSize; // 主背景 canvas.drawRect(0f, 0, width, height - titleHeight, bodyBackGroundPaint); // 标题背景 canvas.drawRect(0f, height - titleHeight, width, height, titleBackGroundPaint); if (values != null && values.size() > 0 && titles != null && titles.size() == values.size()) { for (int i = 0; i < values.size(); i++) { int value = values.get(i); float left = startX + (itemWidth + itemSpaceWidth) * i + moveWidth; float top = height - titleHeight - (itemHeight * value); float right = left + itemWidth; float bottom = height - titleHeight; String title = titles.get(i); float textWidth = titleTextPaint.measureText(title, 0, title.length()); float textX = left + ((itemWidth - textWidth) / 2); // 判断是否中心 if (left >= centerX && left <= (width + itemWidth) / 2) { canvas.drawRect(left, top, right, bottom, stripeSelectedPaint); // 标题 titleTextPaint.setColor(titleTextSelectColor); canvas.drawText(title, textX, titleTextY, titleTextPaint); if (i == 0 || selectPosition != i) { Message message = handler.obtainMessage(); message.what = HANDLER_WHAT_SELECT_CHANGE; message.arg1 = i; message.sendToTarget(); } selectPosition = i; } else { canvas.drawRect(left, top, right, bottom, stripePaint); // 标题 titleTextPaint.setColor(titleTextColor); canvas.drawText(title, textX, titleTextY, titleTextPaint); } } } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touchStarX = event.getX(); isMove = false; isUp = false; postDelayed(touchEventCountThread, 100); if(parentViewGroup != null){ parentViewGroup.requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_MOVE: isMove = true; if (Math.abs(event.getX() - touchStarX) > 1) { moveWidth += event.getX() - touchStarX; postInvalidate(); touchStarX = event.getX(); } break; case MotionEvent.ACTION_UP: isUp = true; if (isMove) { aligning(); } if(parentViewGroup != null){ parentViewGroup.requestDisallowInterceptTouchEvent(false); } break; } //return super.onTouchEvent(event); return true; } /** * 对齐,让靠中间的条形移至正中间 */ private void aligning() { if(values == null || values.isEmpty()){ return; } Float tagX = null; Float tagXValue = null; for (int i = 0; i < values.size(); i++) { float left = startX + (itemWidth + itemSpaceWidth) * i + moveWidth; float distance = Math.abs(centerX - left); if (tagX == null || distance < tagX) { tagX = distance; tagXValue = centerX - left; } } if (tagX != null) { moveWidth += tagXValue; } isMove = false; postInvalidate(); } public void setValue(List values) { this.values = values; selectPosition = 0; } public void setTitle(List titles) { this.titles = titles; } public void refresh() { postInvalidate(); } private int dip2px(float dpValue) { final float scale = getContext().getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } private float sp2px(float spValue) { final float scale = getContext().getResources().getDisplayMetrics().scaledDensity; return (spValue * scale + 0.5f); } class TouchEventCountThread implements Runnable { @Override public void run() { if (!isMove && isUp && values != null && values.size() > 0 && titles != null && titles.size() == values.size()) { for (int i = 0; i < values.size(); i++) { float left = startX + (itemWidth + itemSpaceWidth) * i + moveWidth; float right = left + itemWidth; if (touchStarX >= left && right >= touchStarX) { selectTo(i); if (onStripeStaticsListener != null) { Message message = handler.obtainMessage(); message.what = HANDLER_WHAT_CLICK; message.arg1 = i; message.sendToTarget(); } break; } } } } } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case HANDLER_WHAT_CLICK: if(onStripeStaticsListener != null) { onStripeStaticsListener.onItemClick(msg.arg1); } break; case HANDLER_WHAT_SELECT_CHANGE: if(onStripeStaticsListener != null) { onStripeStaticsListener.onSelectChanged(msg.arg1); } break; } } }; public void selectTo(int position) { if (selectPosition == position) { return; } if (position > selectPosition) { moveWidth -= ((itemWidth + itemSpaceWidth) * (position - selectPosition)); } else { moveWidth += ((itemWidth + itemSpaceWidth) * (selectPosition - position)); } postInvalidate(); } public interface OnStripeStaticsListener { void onItemClick(int position); void onSelectChanged(int position); } public void setOnStripeStaticsListener(OnStripeStaticsListener onClickListener) { this.onStripeStaticsListener = onClickListener; } public void setParentViewGroup(ViewGroup parentViewGroup) { this.parentViewGroup = parentViewGroup; }}
attr.xml添加属性
使用
// 设置值void setValue(List values)// 设置值对应的标题(注:数量要对应)void setTitle(List titles)// 设置标题和内容后刷新控件void refresh()// 控件添加在上下滑动控件中时,当手在该控件左右滑动时不会触发上下滑动,添强体验,如ListViewvoid setParentViewGroup(ViewGroup parentViewGroup)
更多相关文章
- Android(Java):自定义控件
- android full screen
- Android(安卓)Tablayout 的使用
- scrollView的fillviewport
- Android(安卓)Tool——Hierachy Viewer可视化调试
- Android中自定义控件
- Android无障碍总结
- Android界面布局基本知识简述
- Android标题栏各种设置