Android touch event (3)-use an instance of the touch event class
Directory Overview
This article mainly introducesAbsTouchEventHandle
(Custom touch event processing class) andTouchUtils
(Touch event auxiliary tool class) How to Use together.
The purpose or result is:
Simple and Convenient drag and zoom of interface elements
In the whole process, the drag and zoom operations of the interface elements are completely handed over to the above two classes, without any other operations. you only need to complete the implementation and callback of related interfaces.
Procedure
The following is a simple process for combining the two classes. If you do not have a special understanding of the process, you can skip it. The following is a detailed description.
Create a class dedicated to drawing the entire interface,
Draw
Enable
Draw
Inherited from
AbsTouchEventHandle
And rewrite all of its abstract methods (no need to process them for the moment) to create
TouchUtils
Instance object,
TestRectangleDraw
Implementation
TouchUtils
In the mobile and zoom Interfaces
IMoveEvent
And
IScaleEvent
Set
TouchUtils
Instance object and its interface implementation class object to facilitate callback Use
AbsTouchEventHandle
We know that the final display of interface elements isView
To the screen.View
All the work is done inonDraw()
.
But here we need to create a new class to complete the painting, instead of directly usingView
This is because
AbsTouchEventHandle
It is an abstract class, which means it must be inherited before it can be used. Custom View must be inherited from the system class.View
So it is impossible to inherit from another class.
In addition, there is another benefit of using a brand new class to deal with the rendering work: the painting work is independent, insteadView
Some methods are obfuscated. This is a method similar to a combination, rather than a nested method.
Because the drawing class does not inherit fromView
, You needView
Therefore, you need to provide some methods to connect the drawing interfaceView
.
Last important point,AbsTouchEventHandle
Essentially, the touch event interface is implemented.View.OnTouchListener
Do not forgetView
OfonTouchListener
Set the event to the current drawing class ~~~
Public class TestRectangleDraw extends AbsTouchEventHandle {private View mDrawView; public TestRectangleDraw (View drawView) {mDrawView = drawView; // you must replace the touch listening event of the View with the current class mDrawView. setOnTouchListener (this);} // ignore the rewriting of the abstract method, and process it later when TouchUtils is used}
The above isAbsTouchEventHandle
Class, when inheriting this class, you can directly process the View Click Event, double-click the event (drag and zoom required)TouchUtils
Auxiliary)
Use
TouchUtils
UseTouchUtils
The secondary tool class is used to facilitate the processing of drag and zoom events. Generally, drag and zoom events do not need to be customized and can be directly used.TouchUtils
And implement related interfaces.
First, according to the previousTouchUtils
As we know, using this tool class requires implementing corresponding interfaces, which is a very important operation.
The implementation of interfaces does not need to be the current drawing class. If you like it, you can use a new class to process these interfaces. however, this is not actually necessary. Here we use the plotting class to implement these interfaces directly.
Then you need to createTouchUtils
Instance object, and bind the interface implementation object to achieve its validity of dragging and scaling.
// For ease of viewing, ignore the previously inherited AbsTouchEventHandle and only check the implementation of the public class TestRectangleDraw implements TouchUtils interface. IMoveEvent, TouchUtils. IScaleEvent {private TouchUtils mTouch; public TestRectangleDraw (View drawView) {mDrawView = drawView; // you must replace the touch listening event of the drawn View with the current class mDrawView. setOnTouchListener (this); mTouch = new TouchUtils (); mTouch. setMoveEvent (this); mTouch. setScaleEvent (this);} // The interface implementation content will be explained in detail below, temporarily ignored}
The above isTouchUtils
The following describes how to deal with some details.
Easy to troubleshoot
In particular, this should not be difficult to use,TouchUtils.IScaleEvent
The error rate may be very high in interface implementation. this is not a problem of the two classes, but an imperfect problem may be considered during data processing (a few pitfalls have been stepped on). The following is a comparison of the two implementation classes. at the end of the article, we will give two classes completely. for more information, see.
About
TouchUtils.IMoveEvent
The operations in the previous two parts are required. This part only discusses the specific implementation of this interface.
This interface is used to implement the drag operation. The interface itself does not process any drag operation events, just
Check whether the drag operation is allowed. When the drag operation is executed or the drag operation is incorrect.
This interface has four methods:
// Whether to allow boolean isCanMovedOnX (float moveDistanceX, float newOffsetX) in the X axis; // whether to allow boolean isCanMovedOnY (float moveDistacneY, float newOffsetY) in the Y axis ); // drag the event (basically re-painting) void onMove (int suggestEventAction); // drag the failed event (cannot be dragged) void onMoveFail (int suggetEventAction );
The functions of the interface should be clear. A simple implementation is provided below.
// You can drag boolean isCanMovedOnX (float moveDistanceX, float newOffsetX) {return true;} // you can drag boolean isCanMovedOnY (float moveDistacneY, float newOffsetY) along the X axis) {return true;} // Notification View redraw void onMove (int suggestEventAction) {mDrawView. postInvalidate ();} // drag the failure event (you cannot drag it) void onMoveFail (int suggetEventAction) {// generally, no special processing is required. You can choose to remind the user not to drag it}
The implementation of the drag event interface is not very troublesome, and is generally not prone to errors. The only problem is thatisCanMovedOn
In the method, determine when to drag or when to draw the element boundary based on the actual situation? Or reach the screen boundary ?)
About
TouchUtils.IScaleEvent
The scaling callback event is a little more complex. This is because the element size remains unchanged during dragging, that is, the attributes of the interface elements are unchanged. Only the coordinates are changed.
But fromTouchUtils
In this article, we have separated the changes in coordinates from the coordinates of the elements themselves. that is, at any time, the element only processes the coordinates at initialization, and any movement produces the offset coordinates fromTouchUtils
Class to process.
But scaling is different. Scaling means that the attributes of the element will change, and the size and length and width will change, including the coordinates of the element.
The tool class of the drawn element cannot be determined, so the entire element change operation can only be handled by the class of the drawn element. that is, the tool class is not responsible for scaling the attribute changes of the element. It only tells the ratio of the class changes.
Based on this principle,TouchUtils.IScaleEvent
There is a need. Similarly, referTouchUtils.IMoveEvent
Method,IScaleEvent
There are also four methods
// Whether scaling is allowed. scaling must be performed by the entire element. If one dimension can be scaled but the other cannot be scaled, boolean isCanScale (float newScaleRate ); // set the element scaling ratio void setScaleRate (float newScaleRate, boolean isNeedStoreValue); // The scaling operation (basically re-painting) void onScale (int suggestEventAction ); // The scaling operation fails (if the scaling fails) void onScaleFail (int suggetEventAction );
The above four methods should not be easily understood.onScale
AndonScaleFail
The two methods are the easiest.isCanScale
Depends on the user's needs and whether it is complex. In any case, you can scale and return directly.true
You can.
The most important method may be the most complex and error-prone:setScaleRate
Which of the following must be remembered:
setScaleRate
Is used to process the element scaling attribute data. In a scaling event, each callback is a ratio relative to the original element size before the scaling starts.
This method is explained as follows. The complete code of this interface is provided at the end of this section.
A scaling event refers:The whole process from two-point touch to two-point touch.
The size of the original element before zooming begins refers:Element size when two-point touch is pressed
The ratio of the entire scaling process isWhen two points are pressed, the element size is based on
Two different scenarios are used to explain the use of this method. One of them isTestCircleDraw
The interface element is only a circle;
The other one isTestRectangleDraw
The interface element is only a rectangle;
The two different interface elements are used to illustrate some issues that need to be paid attention to when using this method.
Before zooming, we need to determine what the element zooming actually requires?
TestCircleDraw
Circular Scaling
Circular Scaling:Radius
// Globally defined radius variable // The radius variable used for drawing (including the variable used for changing radius During Scaling) float mRadius; // It is used to save the fixed radius variable after each Scaling (it will only change after scaling, and will not change for other times) float mTempRadius; // implementation method public void setScaleRate (float newScaleRate, boolean isNeedStoreValue) {// calculate the new radius mRadius = mTempRadius * newScaleRate; // when the returned flag is true, the reminder is that the up event has been reached // at this time, we should save the last zoom ratio as the final data. if (isNeedStoreValue) {// The zoom ends and the Save status is mTempRadius = mRadius ;}}
This place should not be hard to understand. Remember ~
mRadius
Is the variable used for painting, which will change throughout the scaling process.mTempRadius
It is used for saving variables and will only change after scaling, but will not change at other times
The circle should be easier to understand, which is also the reason for putting it first. below is the rectangle, which will increase the complexity.
TestRectangleDraw
Rectangular Scaling
Similarly, what is the need to scale?
The width and height of the rectangle must be scaled, not the coordinates. This is very important.
Because the rectangle is used for drawingRectF
Class to record the attribute data of the rectangle. Here we also need to create corresponding variables to record the data before and after scaling.
// Define global variables // the variables used for plotting (similarly, they will change constantly) RectF mRectf; // It is used to save RectF mTempRectF for each scaled data RectF; // implement public void setScaleRate (float newScaleRate, boolean isNeedStoreValue) using the interface method {// calculate the new width float newWidth = mTempRectf. width () * newScaleRate; // calculate the New Height float newHeight = mTempRectf. height () * newScaleRate; // Computing Center Position float centerX = mTempRectf. centerX (); float centerY = mTempRectf. centerY (); // adjust the size according to the center position. // The standard mDrawRectf is used to draw objects. left = centerX-newWidth/2; mDrawRectf. top = centerY-newHeight/2; mDrawRectf. right = mDrawRectf. left + newWidth; mDrawRectf. bottom = mDrawRectf. top + newHeight; // when the scaling event ends, you need to save the data if (isNeedStoreValue) {mTempRectf = new RectF (mDrawRectf );}}
It can be seen that the save of the rectangle is much more complex than that of the circle. Of course, the actual process is not very difficult to understand. when the elements to be processed are complex, there will be more things to be done to save the work, which is prone to errors. Therefore, this method is the focus of scaling, it is also prone to errors.
Note the following:
There must be a scaling Center for zooming. You need to determine whether the element needs to be scaled in the center or by a certain point.
If the above rectangle is used to update the coordinate of the rectangle
mDrawRectf.right=mDrawRectf.left+newWidth;mDrawRectf.bottom=mDrawRectf.top+newHeight;
At this time, the center is scaled from the top left, rather than the center of the element object.
Circular/rectangular scaling interface implementation code
Circle:
@ Overridepublic boolean isCanScale (float newScaleRate) {return true ;}@ Overridepublic void setScaleRate (float newScaleRate, boolean isNeedStoreValue) {// update the current data // The scaling ratio of newScaleRate is always relative to the page when you press it, therefore, in the process of moving, // each time it is scaled proportionally to the interface at the press time, instead of targeting the last result // use this method. On the one hand, the idea of processing the scaling is clear. // on the other hand, the scaling ratio is not very small (if it is relative to the previous one, each move Moves several pixels. // In this case, the scaling ratio is definitely 0.0 XXXX compared to the previous one, and some unnecessary problems may occur if the data volume is small.) mRadius = mTempRadius * newScaleRate; // when the returned flag is true, the reminder is that the up event has been reached // at this time, the ratio of the last scaling should be saved as the final data if (isNeedStoreValue) {mTempRadius = mRadius ;}@overridepublic void onScale (int suggestEventAction) {mDrawView. postInvalidate () ;}@ Overridepublic void onScaleFail (int suggetEventAction ){}
Rectangle
@ Overridepublic boolean isCanScale (float newScaleRate) {return true;} @ Overridepublic void setScaleRate (float newScaleRate, boolean isNeedStoreValue) {float newWidth = mTempRectf. width () * newScaleRate; float newHeight = mTempRectf. height () * newScaleRate; // Computing Center Position float centerX = mTempRectf. centerX (); float centerY = mTempRectf. centerY (); // adjust the size according to the center position. // The standard mDrawRectf is used to draw objects. left = centerX-newWidth/2; mDrawRectf. top = centerY-newHeight/2; mDrawRectf. right = mDrawRectf. left + newWidth; mDrawRectf. bottom = mDrawRectf. top + newHeight; // The zoom center is in the upper left corner. // mDrawRectf. right = mDrawRectf. left + newWidt // mDrawRectf. bottom = mDrawRectf. top + newHeig if (isNeedStoreValue) {mTempRectf = new RectF (mDrawRectf) ;}@ Overridepublic void onScale (int suggestEventAction) {mDrawView. postInvalidate () ;}@ Overridepublic void onScaleFail (int suggetEventAction ){}
Draw other View Details
onDraw(Canvas)
According to the above, the essence of the interface isView.onDraw()
Method, while the painting class is just a class we created, so we also provide the corresponding method for the custom View inonDraw()
Method call
// Ignore irrelevant factors such as the inheritance class and interface. public class TestRectangleDraw {public void onDraw (Canvas canvas) {// draw operation }}
TouchUtils
In
AbsTouchEventHandle
Use of abstract methods
TouchUtils
The class has corresponding methods to handle drag and zoom events, and can be called directly in the abstract method.TouchUtils
Processing.
@ Overridepublic void onSingleTouchEventHandle (MotionEvent event, int extraMotionEvent) {// mTouch event processed by the tool class by default. singleTouchEvent (event, extraMotionEvent) ;}@ Overridepublic void onMultiTouchEventHandle (MotionEvent event, int extraMotionEvent) {// The mTouch event processed by the tool class by default (only two events are actually processed. multiTouchEvent (event, extraMotionEvent);} @ Overridepublic void onSingleClickByTime (MotionEvent event) {// time-based Click event // The Press and lift time cannot exceed 500 ms} @ Overridepublic void onSingleClickByDistance (MotionEvent event) {// distance-based Click Event // The distance between pressing and lifting is no more than 20 pixels (not related to time, if you do not press it for a few hours, it can be triggered as long as the distance is within the range )}
Draw Operation Note
When drawing elements, note that,TouchUtils
All the offsets related to movement are processed and savedTouchUtils
So the offset must be used to draw an element to display the Moving Interface.
Taking rectangle as an Example
// Normally draw a rectangle // canvas. drawRect (mDrawRectf. left, mDrawRectf. top, mDrawRectf. right, mDrawRectf. bottom, mPaint); // draw the float offsetX = mTouchUtils rectangle correctly. getOffsetX (); float offsetY = mTouchUtils. getOffsetY (); // you must add the offset part to the actual coordinate of the painting to correctly draw the moving element canvas. drawRect (mDrawRectf. left + offsetX, mDrawRectf. top + offsetY, mDrawRectf. right + offsetX, mDrawRectf. bottom + offsetY, mPaint );
Rectangular source code
// Rectangular drawing class public class TestRectangleDraw extends AbsTouchEventHandle implements TouchUtils. IMoveEvent, TouchUtils. IScaleEvent {// creation tool private TouchUtils mTouch = null; // Save the displayed View private View mDrawView = null; // brush private Paint mPaint = null; // private RectF mDrawRectf = null when drawing; // The scaled data stored during scaling // This data is stored after each scaled data (when the screen does not have a touch, private RectF mTempRectf = null; public TestRectangleDr Aw (View drawView) {mTouch = new TouchUtils (); mTouch. setMoveEvent (this); mTouch. setScaleEvent (this); mDrawView = drawView; mDrawView. setOnTouchListener (this); mPaint = new Paint (); mPaint. setAntiAlias (true); // the start position is 300,300 // The width is 200, and the length is 300 mDrawRectf = new RectF (); mDrawRectf. left = 300; mDrawRectf. right = 500; mDrawRectf. top = 300; mDrawRectf. bottom = 600; // The data used for initialization must be temporarily saved. mTempRectf = new RectF (mD RawRectf); mTouch. setIsShowLog (false); this. setIsShowLog (false, null);} // roll back the Mobile Location public void rollback () {mTouch. rollbackToLastOffset ();} public void onDraw (Canvas canvas) {mPaint. setColor (Color. BLACK); mPaint. setStyle (Paint. style. FILL); // This is the actual drawing interface + offset. Remember not to save the offset to the actually drawn Data !!!! // MDrawRectf cannot be used. offset (x, y) canvas. drawRect (mDrawRectf. left + mTouch. getDrawOffsetX (), mDrawRectf. top + mTouch. getDrawOffsetY (), mDrawRectf. right + mTouch. getDrawOffsetX (), mDrawRectf. bottom + mTouch. getDrawOffsetY (), mPaint) ;}@ Override public void onSingleTouchEventHandle (MotionEvent event, int extraMotionEvent) {// mTouch event processed by the tool class by default. singleTouchEvent (event, extraMotionEvent) ;}@ Override public void onMultiTouchEventHandle (MotionEvent event, int extraMotionEvent) {// The mTouch event processed by the tool class by default (only two events are actually processed. multiTouchEvent (event, extraMotionEvent);} @ Override public void onSingleClickByTime (MotionEvent event) {// time-based Click event // The Press and lift time cannot exceed 500 ms} @ Override public void onSingleClickByDistance (MotionEvent event) {// distance-based Click Event // The distance between pressing and lifting is no more than 20 pixels (not related to time, if you do not press it for a few hours, it can be triggered as long as the distance is within the range.)} @ Override public void onDoubleClickByTime () {// time-based double-click event // clickByTime-Based Two-Click Event // The time between two clicks cannot exceed 250 ms} @ Override public boolean isCanMovedOnX (float moveDistanceX, float newOffsetX) {return true;} @ Override public boolean isCanMovedOnY (float moveDistacneY, float newOffsetY) {return true;} @ Override public void onMove (int suggestEventAction) {mDrawView. postInvalidate () ;}@ Override public void encode (int suggetEventAction) {}@ Override public boolean isCanScale (float newScaleRate) {return true ;}@ Override public void setScaleRate (float newScaleRate, boolean isNeedStoreValue) {float newWidth = mTempRectf. width () * newScaleRate; float newHeight = mTempRectf. height () * newScaleRate; // Computing Center Position float centerX = mTempRectf. centerX (); float centerY = mTempRectf. centerY (); // adjust the size according to the center position. // The standard mDrawRectf is used to draw objects. left = centerX-newWidth/2; mDrawRectf. top = centerY-newHeight/2; mDrawRectf. right = mDrawRectf. left + newWidth; mDrawRectf. bottom = mDrawRectf. top + newHeight; // The zoom center is in the upper left corner. // mDrawRectf. right = mDrawRectf. left + newWidth; // mDrawRectf. bottom = mDrawRectf. top + newHeight; if (isNeedStoreValue) {mTempRectf = new RectF (mDrawRectf) ;}@ Override public void onScale (int suggestEventAction) {mDrawView. postInvalidate () ;}@ Override public void onScaleFail (int suggetEventAction ){}}
Custom VIEW display
/*** Created by CT on 15/9/25. * this View demonstrates how to use AbsTouchEventHandle and TouchUtils */public class TestView extends View {// creates a special drawing class TestCircleDraw mTestCircleDraw = new TestCircleDraw (this, getContext (); // create a drawing class for the square example interface. TestRectangleDraw mTestRectfDraw = new TestRectangleDraw (this); public TestView (Context context) {super (context );} public TestView (Context context, AttributeSet attrs) {super (context, attrs);} public TestView (Context context, AttributeSet attrs, int defStyleAttr) {super (context, attrs, defStyleAttr);} // roll back the Mobile Location public void rollback () {mTestRectfDraw. rollback () ;}@ Override protected void onDraw (Canvas canvas) {// actually, all the painting work is handed over to a dedicated class. // when drawing a complex interface, in this way, you can clearly separate // The rendering and view, because the view itself may have to handle other events (such as events from callback in the rendering event) // and there are enough methods for the View, and a lot of painting methods are added, which does not seem easy to understand. // mTestCircleDraw. onDraw (canvas); mTestRectfDraw. onDraw (canvas );}}