Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 提高Android應用手寫流暢度(基礎篇)

提高Android應用手寫流暢度(基礎篇)

編輯:關於Android編程

在使用android類的手寫應用時,整體上都有這樣一個印象:android的手寫不流暢、不自然,和蘋果應用比起來相差太遠。本文結合作者親身經歷,介紹一下有效提高手寫流暢度的幾種方法:


1、未做任何處理的手寫效果:

\

這是一個自定義的view,通過在onTouchEvent時間中捕獲系統回調的觸摸點信息,然後再onDraw方法裡面刷新,可以明顯地感覺到線條很生硬,並且在手寫的過程中跟隨感很差,反應遲鈍,具體代碼如下:

<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHByZSBjbGFzcz0="brush:java;">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); } @Override protected void onDraw(Canvas canvas) { canvas.drawPath(mPath, mPaint); } @Override public 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(); private Path mPath = new Path(); }
通過分析,發現效率低下的原因是:

(1)底層回調給onTouchEvent方法中的點太少(單位時間內點信息少導致跟隨感差,快速手寫時點之間距離過長);

(2)捕獲點信息後通知View刷新時,刷新不及時(刷新區域太大);

結合查閱的MotionEvent和View的api文檔,發現可以從如下兩個方向著手來提高手寫體驗:

2、增加觸摸點個數:

顯然我們無法改善系統回調onTouchEvent的次數,所以只能通過插值的方式來增加觸摸點個數,但遺憾的時通過插值計算出來的點是沒有壓力值的,不方便做筆鋒效果,通過查閱MotionEvent的api文檔發現,Android對觸屏事件進行批量處理。傳遞給onTouchEvent()的每一個MotionEvent都包含上至前一個onTouchEvent()調用之間捕獲的若干個坐標點。如果將這些點都加入到繪制中,可使手寫效果更加平滑。Android Developers對MotionEvent的介紹如下:

\

將這些點取出來,跟隨感有明顯改善,並且隨著單位時間內點數的增多,快速手寫時點之間距離減小,看上去更為平滑:

\

修改後的代碼如下:

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);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		canvas.drawPath(mPath, mPaint);
	}

	@Override
	public 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();
	private Path mPath = new Path();
}

3、減少每次刷新的區域:

通過2改善了手寫流暢度和平滑度,但是還可以做進一步改善,通過減小每次刷新的區域(使用invalidate(Rect rect)方法),可以提高刷新的效率,上面的代碼都是對整個view進行刷新,當view過大(比如填充整個屏幕)時,手寫過程中還是能夠感覺到遲鈍的現象,改善後的效果如下:

\

代碼如下:

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);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		canvas.drawPath(mPath, mPaint);
	}

	@Override
	public 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 historicalY = 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) {
			mDirtyRect.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();
	private Paint mPaint = new Paint();
	private Path mPath = new Path();
}
後記:
由於Android的消息傳遞機制問題,驅動層傳遞給上層的點由於延時會丟失一部分,導致上層應用獲取的點相對於系統給的點大大減少,雖然人眼在1秒鐘內只要看到超過24幀就不能看出卡頓的現象,但由於刷新機制和由於其它原因(比如:UI線程阻塞)導致看上去不連續。本文通過增加觸摸點、減少刷新區域後,手寫效率和效果得到明顯改善,當然通過軟件的進一步處理,手寫效果還能得到進一步改善,這需要通過軟件做插值處理(壓力值也可以考慮通過插值算法算出來),具體在後面介紹。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved