編輯:關於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(); }
通過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(); }後記:
從官網下載了ndk,可是運行ndk-build竟然提示錯誤E:\android-ndk-r10d>ndk-build‘”E:\
首先,我們要建一個layout,裡面放一個ListView,長寬皆適應屏幕即可。 然後,我們要在這個ListView裡顯示東西,顯示什麼樣的東西
在Android系統中,每一個應用程序都是由一些Activity和Service組成的,這些Activity和Service有可能運行在同一個進程中,也有可能運行在不同的
先看看效果圖:源碼下載:Android Navigation TabBar控件實現多彩標簽欄代碼:MainActivity.javapackage com.bzu.gxs