When using the Android handwriting app, there is an overall impression that Android handwriting is not smooth, unnatural, and far from Apple's application. Based on the author's personal experience, this paper introduces several methods to improve handwriting fluency effectively:
1, did not do any processing of handwriting effect:
This is a custom view, by capturing the system callback's touch point information in the ontouchevent time, and then ondraw the method inside the refresh, you can obviously feel the line is very stiff, and in the process of handwriting in the follow sense is very poor, unresponsive, the specific code is as follows:
Package Com.mingy.paint.view;import Android.content.context;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.graphics.path;import Android.util.attributeset;import Android.view.motionevent;import Android.view.view;public class PaintOrignalView Extends View {public Paintorignalview (context context, AttributeSet attrs, int defstyle) {Super (context, attrs, Defstyle) ; Initpaintview ();} Public Paintorignalview (context context, AttributeSet Attrs) {Super (context, attrs); Initpaintview ();} Public Paintorignalview (Context context) {super (context); Initpaintview ();} public void Clear () {if (null! = MPath) {mpath.reset (); invalidate ();}} private void Initpaintview () {Mpaint.setantialias (true); Mpaint.setcolor (Color.Black); Mpaint.setstyle ( Paint.Style.STROKE); Mpaint.setstrokejoin (Paint.Join.ROUND); Mpaint.setstrokewidth (5f);} @Overrideprotected void OnDraw (canvas canvas) {Canvas.drawpath (MPath, mpaint);} @Overridepublic Boolean ontouchevent (motionevent Event) {Float eventx = event.getx (); Float eventy = event.gety (); switch (Event.getaction ()) {case Motionevent.action_down : {Mpath.moveto (Eventx, Eventy); invalidate ();} Return true;case motionevent.action_move: {mpath.lineto (Eventx, Eventy); invalidate ();} Break;case motionevent.action_up: {mpath.lineto (Eventx, Eventy); invalidate ();} Break;default: {}return false;} return true;} Private Paint Mpaint = new paint ();p rivate path MPath = new Path ();}
Through analysis, it is found that the reasons for inefficiency are:
(1) The low-level callback to the Ontouchevent method is too few points (less information in the unit time leads to follow the sense of difference, fast handwriting point between the distance too long);
(2) After capturing the point information to notify the view refresh, the refresh is not timely (the refresh area is too large);
In conjunction with the consulted Motionevent and view API documentation, it is found that you can improve your handwriting experience in the following two directions:
2. Increase the number of touch points:
Obviously we can not improve the number of system callback ontouchevent, so can only be interpolated to increase the number of touch points, but unfortunately, the point calculated by interpolation is no pressure value, not convenient to do flick effect, through the Motionevent API document discovery, android the touch-screen event in batch processing. Passed to Ontouchevent () Each of the MotionEvent A number of coordinate points that are captured between calls. If you add these points to the drawing, you can make the handwriting smoother. The Android developers Motionevent is described below:
With these points removed, the following sense is significantly improved, and as the number of points in the unit time increases, the distance between the fast handwritten dots decreases and looks smoother:
The modified code is as follows:
Package Com.mingy.paint.view;import Android.content.context;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.graphics.path;import Android.util.attributeset;import Android.view.motionevent;import Android.view.view;public Class Paintmorepointsview extends View {public Paintmorepointsview (context context, AttributeSet attrs, int defstyle) {super ( Context, attrs, Defstyle); Initpaintview ();} Public Paintmorepointsview (context context, AttributeSet Attrs) {Super (context, attrs); Initpaintview ();} Public Paintmorepointsview (Context context) {super (context); Initpaintview ();} public void Clear () {if (null! = MPath) {mpath.reset (); invalidate ();}} private void Initpaintview () {Mpaint.setantialias (true); Mpaint.setcolor (Color.Black); Mpaint.setstyle ( Paint.Style.STROKE); Mpaint.setstrokejoin (Paint.Join.ROUND); Mpaint.setstrokewidth (5f);} @Overrideprotected void OnDraw (canvas canvas) {Canvas.drawpath (MPath, mpaint);} @Overridepublic Boolean ontouchevent(Motionevent event) {Float eventx = event.getx (); Float eventy = event.gety (); switch (Event.getaction ()) {case Motionevent.action_down: { Mpath.moveto (Eventx, Eventy); invalidate ();} Return true;case motionevent.action_move: {int historysize = Event.gethistorysize (); for (int i = 0; i < historysize; i++) {Float historicalx = event.gethistoricalx (i); float historicaly = event.gethistoricaly (i); Mpath.lineto (Historicalx, historicaly); } mpath.lineto (Eventx, Eventy); invalidate ();} Break;case motionevent.action_up: {int historysize = Event.gethistorysize (); for (int i = 0; i < historysize; i++) {Float historicalx = event.gethistoricalx (i); float historicaly = event.gethistoricaly (i); Mpath.lineto (Historicalx, historicaly); } mpath.lineto (Eventx, Eventy); invalidate ();} Break;default: {}return false;} return true;} Private Paint Mpaint = new paint ();p rivate path MPath = new Path ();}
3, reduce the area of each refresh:
Handwriting fluency and smoothness are improved by 2, but further improvements can be made by reducing the area of each refresh (using the Invalidate (Rect) method) to increase the efficiency of the refresh, which is the code that refreshes the entire view. When the view is too large (such as filling the entire screen), the handwriting process can still feel sluggish phenomenon, the improved effect is as follows:
The code is as follows:
Package Com.mingy.paint.view;import Android.content.context;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.graphics.path;import Android.graphics.RectF; Import Android.util.attributeset;import Android.view.motionevent;import Android.view.view;public class Paintinvalidaterectview extends View {public Paintinvalidaterectview (context context, AttributeSet Attrs,int Defstyle) {Super (context, attrs, Defstyle); Initpaintview ();} Public Paintinvalidaterectview (context context, AttributeSet Attrs) {Super (context, attrs); Initpaintview ();} Public Paintinvalidaterectview (Context context) {super (context); Initpaintview ();} public void Clear () {if (null! = MPath) {mpath.reset (); invalidate ();}} private void Initpaintview () {Mpaint.setantialias (true); Mpaint.setcolor (Color.Black); Mpaint.setstyle ( Paint.Style.STROKE); Mpaint.setstrokejoin (Paint.Join.ROUND); Mpaint.setstrokewidth (5f);} @Overrideprotected void OnDraw (canvas canvas) {Canvas.drawpath (MPath, MPaint);} @Overridepublic boolean ontouchevent (Motionevent event) {Float eventx = event.getx (); Float eventy = event.gety (); switch ( Event.getaction ()) {case Motionevent.action_down: {Mpath.moveto (Eventx, eventy); Mlasttouchx = Eventx;mlasttouchy = Eventy;} Return true;case MotionEvent.ACTION_MOVE:case motionevent.action_up: {resetdirtyrect (Eventx, eventy); int historysize = Event.gethistorysize (); for (int i = 0; i < historysize; i++) {Float historicalx = event.gethistoricalx (i); float histo Ricaly = Event.gethistoricaly (i); Getdirtyrect (Historicalx, historicaly); Mpath.lineto (Historicalx, HistoricalY);} Mpath.lineto (Eventx, Eventy); invalidate ((int) (mdirtyrect.left-half_stroke_width), (int) (mdirtyrect.top-half_ Stroke_width), (int) (Mdirtyrect.right + half_stroke_width), (int) (Mdirtyrect.bottom + half_stroke_width)); Mlasttouchx = Eventx;mlasttouchy = Eventy;} Break;default:return false;} return true;} private void Getdirtyrect (float historicalx, float historicaly) {if (Historicalx < MdirtyrecT.left) {mdirtyrect.left = Historicalx;} else if (Historicalx > Mdirtyrect.right) {mdirtyrect.right = HistoricalX;} if (Historicaly < mdirtyrect.top) {mdirtyrect.top = historicaly;} else if (Historicaly > Mdirtyrect.bottom) {MDirtyR Ect.bottom = Historicaly;}} private void Resetdirtyrect (float eventx, float eventy) {mdirtyrect.left = Math.min (Mlasttouchx, eventx); Mdirtyrect.right = Math.max (Mlasttouchx, eventx); mdirtyrect.top = Math.min (Mlasttouchy, eventy); mDirtyRect.bottom = Math.max (Mlasttouchy, eventy);} Private static final Float Stroke_width = 5f;private static final float half_stroke_width = stroke_width/2;private float Mlasttouchx = 0;private Float mlasttouchy = 0;private final RECTF mdirtyrect = new RECTF ();p rivate Paint mpaint = new Pai NT ();p rivate path MPath = new Path ();}
Postscript:
due to the Android messaging mechanism problem, the drive layer passed to the top of the point due to delay will be lost part, resulting in the upper application to obtain a point relative to the point of the system to greatly reduce, although the human eye in 1 seconds as long as see more than 24 frames can not see the phenomenon of stalling, However, the refresh mechanism and other reasons (such as the UI thread blocking) cause the appearance to appear discontinuous. In this paper, by increasing the touch point, reduce the refresh area, handwriting efficiency and effectiveness has been significantly improved, of course, through the further processing of software, the handwriting effect can be further improved, which requires the software to do interpolation (the pressure value can also be considered by interpolation algorithm), specific in the following introduction.
Improve the handwriting fluency of Android apps (Basic article)