Android 自定義簡易的方向盤操作控件


   最近在做一款交互性較為復雜的APP,需要開發一個方向操作控件。最終用自定義控件做了一個簡單的版本。

    這里我准備了兩張素材圖,作為方向盤被點擊和沒被點擊的背景圖。下面看看自定義的Wheel類

public class Wheel extends View implements View.OnTouchListener{
    int xPosition;//點擊按鈕的x坐標
    int yPosition;//點擊按鈕的y坐標
    int centerX;//方向盤X軸中心
    int centerY;//方向盤Y軸中心
    int mainRadius;
    int secondRadius;//點擊的圓形按鈕的半徑
    boolean isClicked;//用於判斷方向盤是否被點擊
 
    public Wheel(Context context) {
        super(context);
    }
 
    public Wheel(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
 
    public Wheel(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        isClicked=false;//初始化為未點擊狀態
    }
}

上面這是最初的代碼,僅僅是聲明了一些變量。

    接下來我們來復寫OnMeasure

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width =MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(heightMeasureSpec);
        if(width>height){//將自定義控件的區域限制為正方形
            width=height;
        }else{
            height=width;
        }
        this.mainRadius=(getWidth()-100)/2;//給主要半徑賦值
        this.secondRadius=mainRadius/5*2;//賦值可點擊的圓形按鈕的半徑
        setMeasuredDimension(width, height);
        this.centerX=getWidth()/2;//確定中心
        this.centerY=getHeight()/2;
        this.xPosition=centerX;//最初可點擊的圓形按鈕在中心位置
        this.yPosition=centerY;
    }

接着是OnDraw函數

    @Override
    protected void onDraw(Canvas canvas){
        Bitmap bm;//背景圖的bitmap
        Paint circlePaint=new Paint();//可點擊的圓形按鈕的Paint對象
        circlePaint.setColor(Color.parseColor("#52c1bd"));//設置顏色
        circlePaint.setStyle(Paint.Style.FILL);//設置作圖形式為填滿
        if(!isClicked){//如果為點擊就將bm賦值為circle1這張圖的bitmap對象,否則為circle
            bm=((BitmapDrawable)getResources().getDrawable(R.mipmap.circle1)).getBitmap();
        }
        else {
            bm = ((BitmapDrawable) getResources().getDrawable(R.mipmap.circle)).getBitmap();
        }
        Rect mSrcRect = new Rect(0, 0, bm.getWidth(), bm.getHeight());//設置原始圖像中要被畫出來的區域
        Rect mDestRect = new Rect(30, 30,getWidth()-30, getHeight()-30);//設置目標區域中會被畫進去圖像的區域
        canvas.drawBitmap(bm,mSrcRect,mDestRect,BackgroundPaint);//畫背景圖
        canvas.drawCircle(this.xPosition,this.yPosition,secondRadius,circlePaint);//畫出可點擊的中心按鈕
    }

上面我們創建Wheel類時還使用了OnTouchListener接口,所以要復寫onTouch函數,但這里我們僅僅是寫成下面的代碼就行

不需要實現額外功能

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return false;
    }

我們實際上要用的是下面這個函數

   @Override
    public boolean onTouchEvent(MotionEvent event){
        isClicked=true;//設置為已經點擊
        this.xPosition = (int) event.getX();//得到點擊的x坐標
        this.yPosition = (int) event.getY();//得到點擊的y坐標
        //如果點擊的位置與圓心距離差距大於半徑,就限制按鈕的位置在邊界處
        if(Math.sqrt((this.xPosition-this.centerX)*(this.xPosition-this.centerX)+(this.yPosition-this.centerY)*(this.yPosition-this.centerY))>mainRadius){
            double Yrate=(this.yPosition-this.centerY)/Math.sqrt((this.xPosition-this.centerX)*(this.xPosition-this.centerX)+(this.yPosition-this.centerY)*(this.yPosition-this.centerY));
            double Xrate=(this.xPosition-this.centerX)/Math.sqrt((this.xPosition-this.centerX)*(this.xPosition-this.centerX)+(this.yPosition-this.centerY)*(this.yPosition-this.centerY));
            this.yPosition=(int)(mainRadius*Yrate)+this.centerY;//設置可點擊圓心按鈕的位置在邊界處
            this.xPosition=(int)(mainRadius*Xrate)+this.centerX;
        }
        
        if(this.myWheelMoveListener!=null){//這里是之后我們要實現交互用的,限制先忽略
            this.myWheelMoveListener.onValueChanged(this.xPosition,this.yPosition);
        }
        invalidate();
 
        if(event.getAction()==1){//如果點擊釋放后
            isClicked=false;//設置為未點擊狀態
            this.yPosition=this.centerY;//按鈕歸於圓心
            this.xPosition=this.centerX;
            if(this.myWheelMoveListener!=null){
                this.myWheelMoveListener.onValueChanged(this.xPosition,this.yPosition);
            }
            invalidate();//重新繪圖
        }
        return true;
    }

到這里我們的按鈕已經可以用了,但是我們還需要實現控件與外部的交互所以我們要定義接口,如下

public void setOnMyWheelMoveListener(OnMyWheelMoveListener listener){    //設置交互事件
        this.myWheelMoveListener=listener;
    }
    public static abstract interface OnMyWheelMoveListener {
        public abstract void onValueChanged(int xDistance, int yDistance);
    }

下面看看完整的代碼

public class Wheel extends View implements View.OnTouchListener{
    int xPosition;
    int yPosition;
    int centerX;
    int centerY;
    int mainRadius;
    int secondRadius;
    boolean isClicked;
    OnMyWheelMoveListener myWheelMoveListener;
 
    public Wheel(Context context) {
        super(context);
    }
 
    public Wheel(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
 
    public Wheel(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        isClicked=false;
    }
 
    @Override
 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width =MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(heightMeasureSpec);
        if(width>height){
            width=height;
        }else{
            height=width;
        }
        this.mainRadius=(getWidth()-100)/2;
        this.secondRadius=mainRadius/5*2;
        setMeasuredDimension(width, height);
        this.centerX=getWidth()/2;
        this.centerY=getHeight()/2;
        this.xPosition=centerX;
        this.yPosition=centerY;
    }
 
    @Override
    protected void onDraw(Canvas canvas){
        Bitmap bm;
        Paint BackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        BackgroundPaint.setFilterBitmap(true);
        BackgroundPaint.setDither(true);
 
        Paint circlePaint=new Paint();
        circlePaint.setColor(Color.parseColor("#52c1bd"));
        circlePaint.setStyle(Paint.Style.FILL);
        if(!isClicked){
            bm=((BitmapDrawable)getResources().getDrawable(R.mipmap.circle1)).getBitmap();
        }
        else {
            bm = ((BitmapDrawable) getResources().getDrawable(R.mipmap.circle)).getBitmap();
        }
        Rect mSrcRect = new Rect(0, 0, bm.getWidth(), bm.getHeight());
        Rect mDestRect = new Rect(30, 30,getWidth()-30, getHeight()-30);
        canvas.drawBitmap(bm,mSrcRect,mDestRect,BackgroundPaint);
        canvas.drawCircle(this.xPosition,this.yPosition,secondRadius,circlePaint);
 
    }
 
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return false;
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event){
        isClicked=true;
        this.xPosition = (int) event.getX();
        this.yPosition = (int) event.getY();
        if(Math.sqrt((this.xPosition-this.centerX)*(this.xPosition-this.centerX)+(this.yPosition-this.centerY)*(this.yPosition-this.centerY))>mainRadius){
            double Yrate=(this.yPosition-this.centerY)/Math.sqrt((this.xPosition-this.centerX)*(this.xPosition-this.centerX)+(this.yPosition-this.centerY)*(this.yPosition-this.centerY));
            double Xrate=(this.xPosition-this.centerX)/Math.sqrt((this.xPosition-this.centerX)*(this.xPosition-this.centerX)+(this.yPosition-this.centerY)*(this.yPosition-this.centerY));
            this.yPosition=(int)(mainRadius*Yrate)+this.centerY;
            this.xPosition=(int)(mainRadius*Xrate)+this.centerX;
        }
        if(this.myWheelMoveListener!=null){
            this.myWheelMoveListener.onValueChanged(this.xPosition,this.yPosition);
        }
        invalidate();
 
        if(event.getAction()==1){
            isClicked=false;
            this.yPosition=this.centerY;
            this.xPosition=this.centerX;
            if(this.myWheelMoveListener!=null){
                this.myWheelMoveListener.onValueChanged(this.xPosition,this.yPosition);
            }
            invalidate();
        }
        return true;
    }
 
    public void setOnMyWheelMoveListener(OnMyWheelMoveListener listener){
        this.myWheelMoveListener=listener;
    }
    public static abstract interface OnMyWheelMoveListener {
        public abstract void onValueChanged(int xDistance, int yDistance);
    }
}

    到這里自定義簡單方向盤控件就基本實現了,下面看看具體使用

    布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.drw.myapplication.MainActivity"
    android:background="#fff">
 
    <com.drw.myapplication.Wheel
        android:id="@+id/myWheel"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true" />
    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"/>
 
</RelativeLayout>

主類

public class MainActivity extends AppCompatActivity {
    Wheel wheel;
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        wheel=(Wheel)findViewById(R.id.myWheel);
        tv=(TextView)findViewById(R.id.tv);
        wheel.setOnMyWheelMoveListener(new Wheel.OnMyWheelMoveListener() {//設置交互事件
            @Override
            public void onValueChanged(int xDistance, int yDistance) {
                tv.setText(""+xDistance+","+yDistance);
            }
        });
    }
}

  執行的效果圖如下

     

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM