Andriod applet - Simply making the wheel that controls the movement of characters in the game
Explain
Recently, there is a project, one of the requirements of the project is to make a control device around the wheel. Many of them are turntables for guessing awards on the internet. This rookie expresses his complex mood. So it took 19 hours for this chicken to make a simple moulding, not to mention much, first of all, the picture above.
The two numbers that appear above are moving parameters, which I will explain in a moment.
This effect is fairly simple to implement and contains the whole class at the end of the article. As long as there is a little android foundation, you can understand it at a glance. The next step is to explain it step by step.
Customizing your own view inherits from the View class
Generally, they inherit from the View class and look at their own needs.
Surface View, of course, can be inherited, but here, it won't be refreshed all the time. I don't think it's necessary.
public class DiscView extends View { //Some people may not understand the meaning of the three constructions. Let me explain. //Call this when new in the main program //DiscView disc = new DiscView(this); public DiscView(Context context) { this(context,null); } //To create a control in an xml file is to call this public DiscView(Context context,AttributeSet attrs) { this(context, attrs, 0 ); } //Create a control in an xml file and use this method when specifying style attributes, otherwise default to the second public DiscView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); manager = LocalBroadcastManager.getInstance(context); } }
Override onDraw() method
Here is the point, the core of the whole control step by step.
When we see this control, it looks like that, as shown in the figure.
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.TRANSPARENT); //Setting the radius of the background map radiusBack = getWidth() / 2; //Setting the radius of a small circle radiusPre = getWidth() / 4; //Center of a circle circleX = getWidth() / 2; circleY = getHeight() / 2; if (paint == null) { paint = new Paint(); } //Setting the basic attributes of the brush paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); paint.setColor(0x7f111111); //Draw the inner circle canvas.drawCircle(circleX,circleY,(float) radiusBack,paint); //Draw the outer circle, the inner circle and the outer circle have different colors, so use different colors. paint.setColor(0xFF744041); canvas.drawCircle(btnX,btnY,(float) radiusPre,paint); canvas.save(); }
But this is certainly not the effect we need.
So it must be refreshed dynamically. Let's sort out our ideas.
Next, implement the process
Perfecting onDraw() Method
Because the position of the finger is to be captured, rewriting onTouch() is indispensable.
Rewrite the OnTouch() method
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_MOVE: //This method is called when the finger moves on the screen, (btnX,btnY) is the center of the inner circle. //We need to drive the inner circle according to the position of our fingers at any time, so we need to record the center of the circle in the process of finger movement. btnX = (int) event.getX(); btnY = (int) event.getY(); //Record the center of the circle and judge it. We don't want our inner circle to go outside completely. So we should judge it. When the center of the inner circle leaves the outer circle, we should control him not to leave the outer circle. //The Pythagorean theorem is used here. if ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX) >= radiusBack * radiusBack){ //Change the position of the center of a circle changeBtnLocation(); } //Each time the screen is submitted and refreshed, invalidate calls the OnDraw() method this.invalidate(); break; case MotionEvent.ACTION_UP: //When the finger leaves, set both btnX and btnY to zero, and judge in onMesure, when both are zero. btnX = 0; btnY = 0; //Likewise refresh this.invalidate(); break; } //When you return to true, the screen will intercept and consume your gestures. return true; }
For the changeBtnLocation() method, mathematical knowledge is used here.
Here I drew a picture. It's easy to understand.
/** * Mathematical knowledge, similar triangles, will be used here. */ private void changeBtnLocation() { //Similarity ratio double similarity = Math.sqrt((radiusBack * radiusBack) / ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX))); //Change the location of (btnX,btnY) btnX = (int) ((btnX - circleX)*similarity + circleX); btnY = (int) ((btnY - circleY)*similarity + circleY); }
The previous onDraw() method is only the previous version. Here, we need to change it so that he can refresh it differently according to the value of the generated btnX btnY.
Update onDraw() method
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.TRANSPARENT); //Setting the radius of the background map radiusBack = getWidth() / 2; //Setting the radius of a small circle radiusPre = getWidth() / 4; //Center of a circle 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 the finger leaves the screen if (btnX <= 0 && btnY <= 0){ paint.setColor(0xFFF10159); canvas.drawCircle(circleX,circleY,(float) radiusPre,paint); canvas.save(); return; } //The reason why finger clicks on the screen has different color parameters is to let the clicks have feedback. paint.setColor(0xFF744041); canvas.drawCircle(btnX,btnY,(float) radiusPre,paint); canvas.save(); }
Write here, there will be a problem can almost run. But there are a few more questions.
- The layout_width and layout_height of the control are the same.
- When the inner circle moves to the edge, it will be cut off. It's ugly.
- Control is finished, but how do we use our main program? You can't create a control to look good.
On the first question, I am too lazy to write about many solutions on the Internet. I set the same solution directly when I use it. If necessary, rewrite the onMeasure() method.
Solving legacy problems
Solving Cutting Problem
Simply, let the diameter of the inner circle and the outer circle add up to less than the width of the control, that is, getWidth().
//Setting the radius of the background map radiusBack = getWidth() / 3; //Setting the radius of a small circle radiusPre = getWidth() / 8; // 2 / 3 + 2 / 8 = 11 / 12 < 1
Solve the problem of calling main program
It's also simple; just send a broadcast as needed while moving or leaving your fingers.
Just receive the broadcast in the main program.
private void sendAction(int x,int y) { Intent intent = new Intent(DISC_BROADCAST); intent.putExtra("move",(x - circleX) + " " + (y - circleY)); manager.sendBroadcast(intent); }
Finally, put in the complete code of the program.
Rewritten controls
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; //Radius of inner and outer circles private int radiusBack; private int radiusPre; //Center of Circle private float circleX; private float circleY; //Broadcasting 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); //Setting the radius of the background map radiusBack = getWidth() / 3; //Setting the radius of a small circle radiusPre = getWidth() / 8; //Center of a circle 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"); //Broadcasting 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); } /** * Mathematical knowledge will be used here. */ private void changeBtnLocation() { //Similarity ratio 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); } }
Call in main program
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); /** * Register local broadcasting receivers to receive broadcasts **/ 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); } } }