近期有在做一个项目,项目的其中一个要求就是,做一个控制设备上下左右的轮盘。网上找了好多都是猜奖转盘,本菜鸟表示心情十分复杂。于是在花费了19个小时之后,本菜鸡才简单制作了一个 成型,话不多说,先上图
上面出现的两个数,是移动的参数,一会儿我会说明。
这个效果实现起来相当简单 文末含有整个类 ,只要有一点android基础,一看就懂,下面一步步解释。
一般都是继承于View类,看自己的需求。
当然也可以继承于SurfaceView,但是这里,不会一直刷新,我认为没必要。
public class DiscView extends View {
//有人可能不懂三个构造方法的意思,我解释一下
//在主程序中new的时候调用这个
//DiscView disc = new DiscView(this);
public DiscView(Context context) {
this(context,null);
}
//在xml文件中创建控件是调用这个
public DiscView(Context context,AttributeSet attrs) {
this(context, attrs, 0 );
}
//在xml文件中创建控件,指定style属性的时候会使用这个方法,否则默认第二种
public DiscView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
manager = LocalBroadcastManager.getInstance(context);
}
}
这里是 重点,整个控件的核心 要一步步来。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
//设置背景图的半径
radiusBack = getWidth() / 2;
//设置小圆点的半径
radiusPre = getWidth() / 4;
//圆心
circleX = getWidth() / 2;
circleY = getHeight() / 2;
if (paint == null) {
paint = new Paint();
}
//设置画笔的基本属性
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0x7f111111);
//画出内圆
canvas.drawCircle(circleX,circleY,(float) radiusBack,paint);
//画出外圆,内外圆颜色不一样,所以要用不同的颜色
paint.setColor(0xFF744041);
canvas.drawCircle(btnX,btnY,(float) radiusPre,paint);
canvas.save();
}
但是这个肯定不是我们需要的效果。
所以说肯定是要动态刷新的。我们整理一下思路。
接下来实现这个过程
因为要捕获手指的位置,因此重写onTouch()方法是不可少的
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
//手指在屏幕上移动会调用这个方法,(btnX,btnY)是内部圆的圆心
//我们要随时根据自己的手指位置来带动内部圆移动,因此在手指移动过程中要记录圆心。
btnX = (int) event.getX();
btnY = (int) event.getY();
//记录圆心,进行判断,我们不希望我们的内部圆完全跑到外部之外,所以要进行判断,当内部圆的圆心在离开外圆,就控制他不离开外圆
//这里用到了勾股定理
if ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX) >= radiusBack * radiusBack){
//改变圆心位置
changeBtnLocation();
}
//每次都要提交并刷新屏幕,invalidate会调用OnDraw()方法
this.invalidate();
break;
case MotionEvent.ACTION_UP:
//当手指离开的时候 把btnX和btnY都置为零,onMesure中进行判断,当两个都为零时
btnX = 0;
btnY = 0;
//同样刷新
this.invalidate();
break;
}
//返回true时屏幕才会对你的手势拦截并消费。
return true;
}
/**
* 这里会用到数学知识,相似三角形
*/
private void changeBtnLocation() {
//相似比
double similarity = Math.sqrt((radiusBack * radiusBack) / ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX)));
//改变(btnX,btnY)的位置
btnX = (int) ((btnX - circleX)*similarity + circleX);
btnY = (int) ((btnY - circleY)*similarity + circleY);
}
之前的onDraw()方法只有之前的版本,这里,我们要改一下,让他根据生成的btnX btnY的值进行不同的刷新
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
//设置背景图的半径
radiusBack = getWidth() / 2;
//设置小圆点的半径
radiusPre = getWidth() / 4;
//圆心
circleX = getWidth() / 2;
circleY = getHeight() / 2;
if (paint == null) {
paint = new Paint();
}
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0x7f111111);
canvas.drawCircle(circleX,circleY,(float) radiusBack,paint);
//如果手指离开屏幕
if (btnX <= 0 && btnY <= 0){
paint.setColor(0xFFF10159);
canvas.drawCircle(circleX,circleY,(float) radiusPre,paint);
canvas.save();
return;
}
//手指点击屏幕,之所以颜色参数不一样,是为了让点击有反馈
paint.setColor(0xFF744041);
canvas.drawCircle(btnX,btnY,(float) radiusPre,paint);
canvas.save();
}
写到这里,会有一个问题差不多就可以跑起来了。但是还有几个问题
关于第一个问题网上很多解决方法 ,我懒得写了 ,我是用的时候直接设置一样的,如有需要,重写 onMeasure() 方法
很简单,让内圆和外圆的直径加起来小于控件的宽度,即getWidth()即可
//设置背景图的半径
radiusBack = getWidth() / 3;
//设置小圆点的半径
radiusPre = getWidth() / 8;
// 2 / 3 + 2 / 8 = 11 / 12 < 1
这个也很简单;在移动或者手指离开的时候 根据需求 发个广播就行了
在主程序中接收广播就行了
private void sendAction(int x,int y) {
Intent intent = new Intent(DISC_BROADCAST);
intent.putExtra("move",(x - circleX) + " " + (y - circleY));
manager.sendBroadcast(intent);
}
package com.example.administrator.myapplication;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.v4.content.LocalBroadcastManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
public class DiscView extends View {
private Paint paint;
private int btnX;
private int btnY;
//内外圆半径
private int radiusBack;
private int radiusPre;
//外圆圆心
private float circleX;
private float circleY;
//广播action
public static final String DISC_BROADCAST = "com.scsdesign.DISCVIEW";
private LocalBroadcastManager manager;
public DiscView(Context context) {
this(context,null);
}
public DiscView(Context context,AttributeSet attrs) {
this(context, attrs, 0 );
}
public DiscView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
manager = LocalBroadcastManager.getInstance(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
//设置背景图的半径
radiusBack = getWidth() / 3;
//设置小圆点的半径
radiusPre = getWidth() / 8;
//圆心
circleX = getWidth() / 2;
circleY = getHeight() / 2;
if (paint == null) {
paint = new Paint();
}
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0x7f111111);
canvas.drawCircle(circleX,circleY,(float) radiusBack,paint);
if (btnX <= 0 && btnY <= 0){
paint.setColor(0xFFF10159);
canvas.drawCircle(circleX,circleY,(float) radiusPre,paint);
canvas.save();
return;
}
paint.setColor(0xFF744041);
canvas.drawCircle(btnX,btnY,(float) radiusPre,paint);
canvas.save();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
System.out.println(event.getX() + " " + event.getY());
btnX = (int) event.getX();
btnY = (int) event.getY();
if ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX) >= radiusBack * radiusBack){
changeBtnLocation();
}
this.invalidate();
break;
case MotionEvent.ACTION_UP:
System.out.println(event.getX() + " " + event.getY() + "TAG");
//发广播
sendAction(btnX,btnY);
btnX = 0;
btnY = 0;
this.invalidate();
break;
}
return true;
}
private void sendAction(int x,int y) {
Intent intent = new Intent(DISC_BROADCAST);
intent.putExtra("move",(x - circleX) + " " + (y - circleY));
manager.sendBroadcast(intent);
}
/**
* 这里会用到数学知识
*/
private void changeBtnLocation() {
//相似比
double similarity = Math.sqrt((radiusBack * radiusBack) / ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX)));
btnX = (int) ((btnX - circleX)*similarity + circleX);
btnY = (int) ((btnY - circleY)*similarity + circleY);
}
}
package com.example.administrator.myapplication;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView textView;
private String message;
private MyBroadCastReceiver receiver;
private LocalBroadcastManager manager;
private DiscView view;
private boolean is = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = findViewById(R.id.view);
textView = findViewById(R.id.textView2);
/**
* 注册本地广播接收器就,接收广播
**/
manager = LocalBroadcastManager.getInstance(this);
IntentFilter filter = new IntentFilter();
filter.addAction(view.DISC_BROADCAST);
receiver = new MyBroadCastReceiver();
manager.registerReceiver(receiver,filter);
}
class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String string = intent.getStringExtra("move");
textView.setText(string);
}
}
}