android开发(29) 自定义曲线,可拖动,无限加载
项目需要 做一个曲线,该曲线的数据时不断加载的。如下图,当不断向左拖动时,图形曲线要随着拖动移动,并在拖动到边界时需要加载更多数据。
先看步骤:
1.在Activity里放一个surfaceView
2.为surfaceView 添加监听器
surfaceHolder = surfaceView1.getHolder(); mMySurfaceCallback = new MySurfaceCallback(); surfaceHolder.addCallback(mMySurfaceCallback);
3.实现监听器。
class MySurfaceCallback implements android.view.SurfaceHolder.Callback { MyDraw mMyDraw; @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mMyDraw.onSurfaceChanged(); } @Override public void surfaceCreated(SurfaceHolder holder) { mMyDraw = new MyDraw(); mMyDraw.draw(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }
4.编写绘制图形的方法
class MyDraw { Paint p; Canvas canvas; int unit_x = 80; // 单位 宽度,根据要 绘制的个数不同而计算 int unit_y = 10;// 单位高度 ,固定 final int preInit_x = 50;// 开始位置 final int preInit_y = 550;// 最大的Y顶点。 Point origin; final int MAX_Fliing_X = unit_x * 20;// 最大20个 public MyDraw() { super(); p = new Paint(); // 创建画笔 origin = new Point(preInit_x, preInit_y);// 坐标系的原点 } /** * 拖动的范围,x > 0说明是 从左到右拖动。 x<0是从右向左拖动 * * @param x_changed */ public void onFliing(float x_changed) { float newX = origin.x + x_changed; if (newX > preInit_x) newX = preInit_x; int minNewX = -((result.length) * unit_x - surfaceView1.getWidth()); boolean isToEnd = false;// 是否到达了最后一个点。即拖到最右侧极限处。 if (newX < minNewX) { newX = minNewX; isToEnd = true; } int x = (int) newX; if (x == origin.x) return; origin = new Point(x, origin.y);//更改坐标系原点的位置 draw(); if (isToEnd) {// 触发 到达顶点的方法。 raiseScrollToEnd(); } } public void draw() { canvas = surfaceHolder.lockCanvas(); onDdraw(canvas); surfaceHolder.unlockCanvasAndPost(canvas); } public void onDdraw(Canvas c) { Log.i("PDWY", String.format("新原点位置 :(%s, %s)", origin.x, origin.y)); Rect r; int height = c.getHeight(); int width = c.getWidth(); c.drawColor(Color.BLACK); p.setColor(Color.RED); p.setStrokeWidth(2); p.setStyle(Paint.Style.STROKE); r = new Rect(2, 2, width - 2, height - 2); c.drawRect(r, p); p.reset(); p.setColor(Color.RED); p.setStrokeWidth(5); float[] lines = new float[max_unit]; lines = evalPosition(result, unit_x, unit_y, origin); // lines = new float[]{0,0,50,500,100,400,100,400,150,500,0,0}; // drawLines方法用一组float表示要绘制的直线,每个直线用4个点表示,前两个为起端点,后两个为终端点 c.drawLines(lines, 2, lines.length - 2, p); p.reset(); p.setColor(Color.parseColor("#dcdcdc")); drawEndPoint(lines, 2, lines.length - 2, p, c); } private void drawEndPoint(float[] lines, int offset, int count, Paint p2, Canvas c) { for (int i = offset; i < count; i += 2) { float x = lines[i]; float y = lines[i + 1]; c.drawCircle(x, y, 8, p2); } } private float[] evalPosition(float[] result2, int unit_widht, int unit_height, Point origin) { if (result2 == null) return new float[0]; float[] val = new float[result2.length * 4]; for (int i = 0; i < result2.length; i++) { float y = origin.y - result2[i] * unit_height; float x = origin.x + unit_widht * i; val[i * 4 + 0] = x; val[i * 4 + 1] = y; val[i * 4 + 2] = x; val[i * 4 + 3] = y; } return val; } final int max_unit = 6; public void onSurfaceChanged() { } }
5. 注册 手势 ,当手指拖动时,曲线要随着变化。
surfaceView1.setOnTouchListener(new OnTouchListener() { int state = 0; float x_start; @Override public boolean onTouch(View v, MotionEvent event) { // mGestureDetector.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_DOWN) { state = 1; x_start = event.getX(); } if (event.getAction() == MotionEvent.ACTION_UP) { state = 0; x_start = 0; } if (event.getAction() == MotionEvent.ACTION_MOVE) { if (state == 1) { if (mMySurfaceCallback != null && mMySurfaceCallback.mMyDraw != null) { float xEnd = event.getX(); float x_changed = (float) ((xEnd - x_start) / 1.3); if (Math.abs(x_changed) > 5) { Log.i("PDWY", "移动了 " + x_changed); mMySurfaceCallback.mMyDraw.onFliing(x_changed); x_start = xEnd; } } } } return true; } });
6。记得计算坐标位置,当不断向左拖动,拖动到 最后时,触发一个 自定义的 事件 onScrollToEnd。订阅了该事件的对象可以在 适当的时机 “加载更多的数据”
/** * 拖动的范围,x > 0说明是 从左到右拖动。 x<0是从右向左拖动 * * @param x_changed */ public void onFliing(float x_changed) { float newX = origin.x + x_changed; if (newX > preInit_x) newX = preInit_x; int minNewX = -((result.length) * unit_x - surfaceView1.getWidth()); boolean isToEnd = false;// 是否到达了最后一个点。即拖到最右侧极限处。 if (newX < minNewX) { newX = minNewX; isToEnd = true; } int x = (int) newX; if (x == origin.x) return; origin = new Point(x, origin.y);//更改坐标系原点的位置 draw(); if (isToEnd) {// 触发 到达顶点的方法。 raiseScrollToEnd(); } }
自定义事件的实现
ScrollToEndListener mScrollToEndListener; private void raiseScrollToEnd() { if (mScrollToEndListener != null) mScrollToEndListener.onScrollToEnd(); } public void setScrollToEndListener(ScrollToEndListener scrollToEndListener) { mScrollToEndListener = scrollToEndListener; } public static interface ScrollToEndListener { public void onScrollToEnd(); }
-------------------------
最后,看看如何使用它:
public class MainActivity extends Activity { SurfaceView surfaceView1; MyCustomCurve mMyCustomCurve; float[] result; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Package pck = Package.getPackage(this.getPackageName()); Resources resource = this.getResources(); surfaceView1 = (SurfaceView)findViewById(resource.getIdentifier("surfaceView1", "id", pck.getName())); mMyCustomCurve = new MyCustomCurve(this,surfaceView1); result = new float[] { 1, 30, 50, 40, 30, 5, 15, 35, 20,3,12,15,31, 30, 50, 40, 30, 5, 15, 35, 20,3,12,15,15}; mMyCustomCurve.setResult(result); //当读取到数据的终点时 mMyCustomCurve.setScrollToEndListener(new ScrollToEndListener() { @Override public void onScrollToEnd() { ArrayList<Float> lst = new ArrayList<Float>(); for (int i = 0; i < result.length; i++) { lst.add(result[i]); } //追加新的数据,随机添加10个数字,值不大于50. Random r = new Random(); for (int j = 0; j < 10; j++) { lst.add(r.nextFloat() * 50); } float[] newArray = new float[lst.size()]; for (int i = 0; i < lst.size(); i++) { newArray[i] = lst.get(i); } result = newArray; //设置新的数据源 mMyCustomCurve.setResult(result); Toast.makeText(MainActivity.this, "加载了一次新的数据", 0).show(); } }); }
代码下载 提取码:883c