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<Integer> values;
private List<String> 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<Integer> values) {
this.values = values;
selectPosition = 0;
}
public void setTitle(List<String> 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 增加属性
<declare-styleable name="StripeStaticsView">
<attr name="itemWidth" format="dimension" />
<attr name="itemColor" format="color" />
<attr name="itemSelectColor" format="color" />
<attr name="itemSpaceWidth" format="dimension" />
<attr name="maxValue" format="integer" />
<attr name="titleTextSize" format="dimension" />
<attr name="titleTextLineSpace" format="dimension" />
<attr name="titleTextColor" format="color" />
<attr name="titleTextSelectColor" format="color" />
<attr name="contentBGColor" format="color" />
<attr name="titleBGColor" format="color" />
</declare-styleable>
应用
<xxx.StripeStaticsView
android:id="@+id/stripeStaticsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/statics_stripe_view_margin_top"
app:contentBGColor="#7dded4"
app:itemColor="#80ffffff"
app:itemSelectColor="@android:color/white"
app:itemSpaceWidth="5dp"
app:itemWidth="30dp"
app:maxValue="100"
app:titleBGColor="@android:color/white"
app:titleTextColor="@color/txt_describe"
app:titleTextLineSpace="8dp"
app:titleTextSelectColor="@color/colorPrimary"
app:titleTextSize="10sp" />
// 设置值
void setValue(List<Integer> values)
// 设置值对应的题目(注:数量要对应)void setTitle(List<String> titles)
// 设置题目和内容后刷新控件
void refresh()
// 控件增加在高低滑动控件中时,当手在该控件左右滑动时不会触发高低滑动,添强体验,如 ListView
void setParentViewGroup(ViewGroup parentViewGroup)