Android 自定义View 慢慢画一个不同颜色扇形的圆,点击圆上不同颜色扇形区域返回不同颜色

最近研究自定义View,于是做了这个demo,供大家参考

参考了一位博主文章:http://blog.csdn.net/lilong85362952/article/details/41447967

自定义View的代码:


public class DrawView extends View implements ViewRefreshInterface, OnTouchListener {
	private RectF rectF;
	private List colors;
	private List mList;
	// 圆心
	private Point point;
	// 半径
	private int radius = 0;
	// view刷新
	private int a = 0;
	private int refeshAngle = 0;
	private int number = 0;
	private boolean isPause;
	Thread thread = new Thread();
	private ViewRefreshUtil viewRefreshUtil;
	private ViewRefreshUtil.ViewRefreshInterfaceEntity viewRefreshInterface;
	
	private ViewClickListener viewClickListener;
	
	private Context mContext;

	public DrawView(Context context) {
		super(context);
		init(context);
	}

	public DrawView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init(context);
	}
	
	@SuppressLint("ClickableViewAccessibility")
	private void init(Context context) {
		this.mContext = context;
		
		colors = new ArrayList();
		mList = new ArrayList();
		colors.add(Color.RED);
		colors.add(Color.GREEN);
		colors.add(Color.BLUE);
		colors.add(Color.YELLOW);
		
		viewRefreshUtil = ViewRefreshUtil.getRefreshUtil();
		viewRefreshInterface = viewRefreshUtil.addViewRefreshInterface(this);
		this.setOnTouchListener(this);
		
		DisplayMetrics dm = new DisplayMetrics();
		((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(dm);
		
		isPause = false;
	}
	
	@SuppressLint("DrawAllocation")
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		Paint paint = new Paint();
		
		paint.setAntiAlias(true);// 抗锯齿
		paint.setStyle(Style.STROKE);// Style.FILL: 实心, STROKE:空心, FILL_OR_STROKE:同时实心与空心
//		paint.setStrokeCap(Cap.ROUND);// 画笔样式:圆形 Cap.ROUND, 方形 Cap.SQUARE
//		paint.setStrokeJoin(Join.ROUND);// 平滑效果
		paint.setStrokeWidth(15);
//		paint.setColor(Color.BLUE);

		// canvas.drawLine(0, 50, 450, 50, paint);// 画线
		// canvas.drawRect(0, 0, 20, 60, paint);// 画矩形
		// canvas.drawCircle(100, 50, 50, paint);// 画圆
//		canvas.drawOval(rectF, paint);// 画椭圆或者圆
		
		a += 1;
		refeshAngle += 1;
		int temAngle = 0;
		
		paint.setStyle(Style.FILL);
		paint.setStrokeWidth(14);
		
		for (int i = 0; i < number; i++) {
			paint.setColor(colors.get(i));
			temAngle += mList.get(i).getEndAngle() - mList.get(i).getStartAngle();
			
			if (temAngle >= a) {
				drawMyView(canvas, paint, rectF, mList.get(i).getStartAngle(), refeshAngle);
				
				if (refeshAngle == mList.get(i).getEndAngle() - mList.get(i).getStartAngle()) {
					refeshAngle = 0;
					number = (number < mList.size()) ? number + 1 : mList.size();
				}
			} else {
				drawMyView(canvas, paint, rectF, mList.get(i).getStartAngle(), Math.abs(mList.get(i).getEndAngle() - mList.get(i).getStartAngle()));
			}
		}
	}
	
	private void drawMyView(Canvas canvas, Paint paint, RectF rectF, int startAngle, int endAngle) {
		paint.setStrokeJoin(Join.MITER);
		paint.setStrokeCap(Cap.SQUARE);
		canvas.drawArc(rectF, startAngle, endAngle, true, paint);
	}

	@SuppressLint("DrawAllocation")
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int measureWidth = getMeasureWH(widthMeasureSpec);
		int measureHeigh = getMeasureWH(heightMeasureSpec);
		
		if (measureWidth > measureHeigh) {
			radius = measureHeigh / 2;
		} else {
			radius = measureWidth / 2;
		}
		
		if (getMeasuredWidth() != 0 && getMeasuredHeight() != 0) {
			rectF = new RectF(0, 0, radius * 2, radius * 2);
//			this.start();
		}
		
		if (measureWidth > measureHeigh) {
			setMeasuredDimension(measureHeigh, measureHeigh);
		} else {
			setMeasuredDimension(measureWidth, measureWidth);
		}
	}
	
	private int getMeasureWH(int measureSpec) {
		
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);
		
		return specSize;
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
	}

	public void setData(List list) {
		if (!viewRefreshInterface.isRefresh() && !isPause) {
			this.mList = list;
			this.number = 1;
//			this.start();
		}
	}
	
	public boolean isRefresh() {
		return viewRefreshInterface.isRefresh();
	}
	
	// 开始
	public void start() {
		if (isPause) {
			viewRefreshInterface.setRefresh(true);
			viewRefreshUtil.startViewRefresh(viewRefreshInterface);
			isPause = false;
		}
		
		if (!viewRefreshInterface.isRefresh()) {
			viewRefreshInterface.setRefresh(true);
			viewRefreshUtil.startViewRefresh(viewRefreshInterface);
			isPause = false;
		}
	}
	
	// 暂停
	public void pause() {
		if (viewRefreshInterface.isRefresh() && !isPause) {
			viewRefreshInterface.setRefresh(false);
			isPause = true;
		}
	}
	
	// 停止
	public void stop() {
		viewRefreshInterface.setRefresh(false);
		a = 0;
		refeshAngle = 0;
		number = 0;
		init(mContext);
		postInvalidate();
	}
	
	@Override
	public void refresh() {
		if (a >= 360) {
			viewRefreshInterface.setRefresh(false);
		} else {
			postInvalidate();
		}
	}
	
	@SuppressLint("DrawAllocation")
	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		point = new Point((right - left) / 2 + left, (bottom - top) / 2 + top);
	}
	
	@SuppressLint("ClickableViewAccessibility")
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			break;
		case MotionEvent.ACTION_UP:
			double x = Math.pow(point.x - event.getRawX(), 2);
			double y = Math.pow(point.y - event.getRawY(), 2);
			
			if (!viewRefreshInterface.isRefresh() && Math.sqrt(x + y) <= radius) {
				int tanX = (int) Math.abs(event.getRawX() - point.x);
				int tanY = (int) Math.abs(event.getRawY() - point.y);
				
				double clickAngle = getangle(tanX, tanY, (int) event.getRawX() - point.x, point.y - (int) event.getRawY());
				int mClickAngle = (int) clickAngle - 90;
				
				for (int i = 0; i < mList.size(); i++) {
					if (mClickAngle >= mList.get(i).getStartAngle() && mClickAngle <= mList.get(i).getEndAngle()) {
						viewClickListener.clickListener(mList.get(i));
						break;
					}
				}
			}
			break;
		}
		
		return true;
	}
	
	// 根据点击位置获取夹角
	private double getangle(int x, int y, int downx, int downy) {
		double angle = (float) 0.0;
		if (downx > 0 && downy < 0) {
			double c = (double) y / (double) x;
			double d = Math.toDegrees(Math.atan(c));
			angle = d + 90;
		} else if (downx <= 0 && downy <= 0) {
			double c = (double) x / (double) y;
			double d = Math.toDegrees(Math.atan(c));
			angle = d + 180;
		} else if (downx < 0 && downy >= 0) {
			double c = (double) y / (double) x;
			double d = Math.toDegrees(Math.atan(c));
			angle = d + 270;
		} else {
			double c = (double) x / (double) y;
			double d = Math.toDegrees(Math.atan(c));
			angle = d;
		}
		return angle;
	}

	public void setViewClickListenrt(ViewClickListener clickListener) {
		this.viewClickListener = clickListener;
	}
	
	public interface ViewClickListener {
		public void clickListener(SectorItem item);
	}
	
}

这里有一些东西需要注意一下(这里不做详细说明):

1、当你在xml中为这个view设置margin属性的时候,在onLayout中获取的top,left,right,bottom的值都是你设置的2倍,所以再做一些计算时要注意,

2、在使用canvas.drawArc()方法画圆时,是以水平右方向顺时针画的,所以我在画圆的时候起始位置的角度为:-90,,竖直方向开始画圆,

3、onMeasure()这个方法在什么时候调用,调用几次,getMeasuredHeight()、getHeight()这两个方法区别,MeasureSpecMode()中几个不同Mode也是需要注意,

4、在画这个圆时,会有点慢,如果想改变速率,需要注意 refeshAngle 和 a 这两个参数,因为扇形时整数度数,我是一度一度慢慢绘制的,修改时需要注意

如果不需要慢慢绘制这个圆可以直接用for循环调用drawMyView(canvas, paint, rectF, mList.get(i).getStartAngle(), mList.get(i).getEndAngle());直接回执完整的圆,省去了 绘制的时间


ViewRefreshUtil代码:

public class ViewRefreshUtil implements Runnable {
	private Thread thread;
	private static ViewRefreshUtil viewRefreshUtil;
	private ArrayList viewRefreshInterfaces;
	
	public static ViewRefreshUtil getRefreshUtil() {
		if (null == viewRefreshUtil) {
			viewRefreshUtil = new ViewRefreshUtil();
		}
		return viewRefreshUtil;
	}
	
	private ViewRefreshUtil() {
		viewRefreshInterfaces = new ArrayList();
	}
	
	public ViewRefreshInterfaceEntity addViewRefreshInterface(ViewRefreshInterface viewRefreshInterface) {
		ViewRefreshInterfaceEntity viewRefreshInterfaceEntity = new ViewRefreshInterfaceEntity();
		viewRefreshInterfaceEntity.setViewRefreshInterface(viewRefreshInterface);
		viewRefreshInterfaces.add(viewRefreshInterfaceEntity);
		return viewRefreshInterfaceEntity;
	}
	
	public void startViewRefresh(ViewRefreshInterfaceEntity viewRefreshInterfaceEntity) {
		viewRefreshInterfaceEntity.setRefresh(true);
		if (null == thread || !thread.isAlive()) {
			thread = new Thread(this);
            thread.start();
		}
	}
	
	public void stopViewRefresh(ViewRefreshInterfaceEntity viewRefreshInterfaceEntity) {
		viewRefreshInterfaceEntity.setRefresh(false);
	}
	
	public interface ViewRefreshInterface {
		void refresh();
	}
	
	@Override
	public void run() {
		int i = 1;
        while (i != 0) {
            i=0;
            for (ViewRefreshInterfaceEntity viewRefreshInterfaceEntity : viewRefreshInterfaces) {
                if (viewRefreshInterfaceEntity.isRefresh()) {
                	viewRefreshInterfaceEntity.getViewRefreshInterface().refresh();
                    i++;
                }
            }

            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
	}
	
	public class ViewRefreshInterfaceEntity {
		private boolean isRefresh = false;
		private ViewRefreshInterface viewRefreshInterface;

		public boolean isRefresh() {
			return isRefresh;
		}

		public void setRefresh(boolean isRefresh) {
			this.isRefresh = isRefresh;
		}

		public ViewRefreshInterface getViewRefreshInterface() {
			return viewRefreshInterface;
		}

		public void setViewRefreshInterface(ViewRefreshInterface viewRefreshInterface) {
			this.viewRefreshInterface = viewRefreshInterface;
		}

	}

}

SectorItem代码:

public class SectorItem implements Serializable {
	private int startAngle;
	private int endAngle;
	private String name;

	public int getStartAngle() {
		return startAngle;
	}

	public void setStartAngle(int startAngle) {
		this.startAngle = startAngle;
	}

	public int getEndAngle() {
		return endAngle;
	}

	public void setEndAngle(int endAngle) {
		this.endAngle = endAngle;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}


mainactivity代码:

public class MainActivity extends Activity implements OnClickListener {
	private DrawView drawView;
	private Button start, pause, stop;
	
	private List list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		drawView = (DrawView) findViewById(R.id.dv);
		start = (Button) findViewById(R.id.start);
		pause = (Button) findViewById(R.id.pause);
		stop = (Button) findViewById(R.id.stop);
		start.setOnClickListener(this);
		pause.setOnClickListener(this);
		stop.setOnClickListener(this);
		
		drawView.setViewClickListenrt(new ViewClickListener() {
			@Override
			public void clickListener(SectorItem item) {
				Toast.makeText(MainActivity.this, "You Click :" + item.getName() + " Area !", Toast.LENGTH_SHORT).show();
			}
		});
	}

	private void initData() {
		list = new ArrayList();
		
		SectorItem item = new SectorItem();
		item.setStartAngle(-90);
		item.setEndAngle(0);
		item.setName("Red");
		list.add(item);
		
		item = new SectorItem();
		item.setStartAngle(0);
		item.setEndAngle(80);
		item.setName("Green");
		list.add(item);
		
		item = new SectorItem();
		item.setStartAngle(80);
		item.setEndAngle(150);
		item.setName("BLUE");
		list.add(item);
		
		item = new SectorItem();
		item.setStartAngle(150);
		item.setEndAngle(270);
		item.setName("YELLOW");
		list.add(item);
		
		drawView.setData(list);
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		if (drawView.isRefresh()) {
			drawView.stop();
		}
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.start:
			initData();
			drawView.start();
			break;
		case R.id.pause:
			drawView.pause();
			break;
		case R.id.stop:
			drawView.stop();
			break;
		}
	}

}


mainactivity 的布局:



    

    

Android 自定义View 慢慢画一个不同颜色扇形的圆,点击圆上不同颜色扇形区域返回不同颜色_第1张图片

功能很简单,进入页面,点击start按钮开始竖直顺时针方向画圆,圆内有四种不同颜色的扇形,点击pause按钮暂停画圆,点击stop按钮停止画圆并清空view,当我们点击圆内不同颜色的扇形时,用Toast打印我们点击的扇形的颜色



最后,未经允许禁止转载

你可能感兴趣的:(Android)