效果图
核心代码
- 自定义View:FunnelChart
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created by benz on 2020/12/22.
*/
public class FunnelChart extends View {
private static final int SPACE = 3;
private ArrayList<Path> mPaths = new ArrayList<>();
private ArrayList<Paint> mPaints = new ArrayList<>();
private List<FunnelData> mData;
private OnValueTouchListener mTouchListener;
public FunnelChart(Context context) {
this(context, null);
}
public FunnelChart(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FunnelChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setTouchListener(OnValueTouchListener touchListener) {
mTouchListener = touchListener;
}
public void renderData(List<FunnelData> data) {
if (data == null || data.isEmpty()) {
return;
}
mData = data;
mPaints.clear();
for (int i = 0; i < mData.size(); i++) {
Paint mPaint = new Paint();
mPaint.setColor(mData.get(i).color);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setDither(true);
mPaint.setAntiAlias(true);
mPaints.add(mPaint);
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mData == null || mData.isEmpty()) {
Paint p = new Paint();
p.setColor(Color.parseColor("#ffffff"));
p.setStyle(Paint.Style.FILL);
p.setDither(true);
p.setAntiAlias(true);
canvas.drawPaint(p);
canvas.save();
return;
}
mPaths.clear();
float lastX = 0;
float lastY = 0;
float value1 = mData.get(0).value;
for (int i = 0; i < mData.size(); i++) {
Path mPath = new Path();
mPath.moveTo(lastX, lastY);
mPath.lineTo(lastX + mData.get(i).value * 1f / value1 * getWidth(), lastY);
if (i < mData.size() - 1) {
float value2 = mData.get(i + 1).value;
mPath.lineTo(getWidth() / 2f + value2 / value1 * getWidth() / 2f, getHeight() * 1f / mData.size() * (i + 1) - SPACE);
mPath.lineTo(getWidth() / 2f - value2 / value1 * getWidth() / 2f, getHeight() * 1f / mData.size() * (i + 1) - SPACE);
lastX = getWidth() / 2f - value2 / value1 * getWidth() / 2f;
} else {
mPath.lineTo(getWidth() / 2f, getHeight() * 1f / mData.size() * (i + 1) - SPACE);
mPath.lineTo(getWidth() / 2f, getHeight() * 1f / mData.size() * (i + 1) - SPACE);
lastX = getWidth() / 2f;
}
mPath.close();
mPaths.add(mPath);
lastY = getHeight() * 1f / mData.size() * (i + 1) + SPACE;
canvas.drawPath(mPaths.get(i), mPaints.get(i));
}
canvas.save();
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);//默认实现,可以不写
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mTouchListener != null) {
float x = event.getX();
float y = event.getY();
for (int i = 0; i < mPaths.size(); i++) {
Path path = mPaths.get(i);
RectF rectF = new RectF();
path.computeBounds(rectF, true);
if (rectF.contains(x, y)) {
mTouchListener.onValueTouch(mData.get(i));
break;
}
}
return true;
} else {
return super.onTouchEvent(event);
}
}
public interface OnValueTouchListener{
void onValueTouch(FunnelData funnelData);
}
}
- 自定义数据结构:FunnelData
/**
* Created by benz on 2020/12/22.
*/
public class FunnelData {
public float value;
public int color;
}
如何使用
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
FunnelChart funnelView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
funnelView = (FunnelChart) findViewById(R.id.funnelview);
funnelView.setTouchListener(new FunnelChart.OnValueTouchListener() {
@Override
public void onValueTouch(FunnelData funnelData) {
Toast.makeText(MainActivity.this, "点击了" + funnelData.value, Toast.LENGTH_SHORT).show();
}
});
List<FunnelData> data = new ArrayList<>();
int size = 3;
for (int i = 0; i < size; i++) {
FunnelData funnelData = new FunnelData();
funnelData.color = Color.parseColor(getRandColorCode());
funnelData.value = getRandomNumber(1000 / (i + 1), 800 / (i + 1));
data.add(funnelData);
}
funnelView.renderData(data);
}
private int getRandomNumber(int max, int min) {
Random random = new Random();
return random.nextInt(max) % (max - min + 1) + min;
}
private String getRandColorCode() {
String r, g, b;
Random random = new Random();
r = Integer.toHexString(random.nextInt(256)).toUpperCase();
g = Integer.toHexString(random.nextInt(256)).toUpperCase();
b = Integer.toHexString(random.nextInt(256)).toUpperCase();
r = r.length() == 1 ? "0" + r : r;
g = g.length() == 1 ? "0" + g : g;
b = b.length() == 1 ? "0" + b : b;
return "#" + r + g + b;
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C0C1C2">
<FunnelChart
android:id="@+id/funnelview"
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:background="#ffffff" />
</RelativeLayout>