編輯:關於Android編程
在編寫自定義滑動控件時常常會用到Android觸摸機制和Scroller及VelocityTracker。Android Touch系統簡介(二):實例詳解onInterceptTouchEvent與onTouchEvent的調用過程對Android觸摸機制需要用到的函數進行了詳細的解釋,本文主要介紹兩個重要的類:Scroller及VelocityTracker。利用上述知識,最後給出了一個自定義滑動控件的demo,該demo類似於ImageGallery。ImageGallery一般是用GridView來實現的,可以左右滑動。本例子實現的控件直接繼承一個ViewGroup,對其回調函數如 onTouchEvent、onInterceptTouchEvent、computeScroll等進行重載。弄懂該代碼,對Android touch的認識將會更深一層。
VelocityTracker:用於對觸摸點的速度跟蹤,方便獲取觸摸點的速度。
用法:一般在onTouchEvent事件中被調用,先在down事件中獲取一個VecolityTracker對象,然後在move或up事件中獲取速度,調用流程可如下列所示:
VelocityTracker vTracker = null; @Override public boolean onTouchEvent(MotionEvent event){ int action = event.getAction(); switch(action){ case MotionEvent.ACTION_DOWN: if(vTracker == null){ vTracker = VelocityTracker.obtain(); }else{ vTracker.clear(); } vTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: vTracker.addMovement(event); //設置單位,1000 表示每秒多少像素(pix/second),1代表每微秒多少像素(pix/millisecond)。 vTracker.computeCurrentVelocity(1000); //從左向右劃返回正數,從右向左劃返回負數 System.out.println("the x velocity is "+vTracker.getXVelocity()); //從上往下劃返回正數,從下往上劃返回負數 System.out.println("the y velocity is "+vTracker.getYVelocity()); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: vTracker.recycle(); break; } return true; }
Scroller:用於跟蹤控件滑動的軌跡,此類不會移動控件,需要你在View的一個回調函數computerScroll()中使用Scroller對象還獲取滑動的數據來控制某個View。
/** * Called by a parent to request that a child update its values for mScrollX * and mScrollY if necessary. This will typically be done if the child is * animating a scroll using a {@link android.widget.Scroller Scroller} * object. */ public void computeScroll() { }parentView在繪制式,會調用dispatchDraw(Canvas canvas),該函數會調用ViewGroup中的每個子view的boolean draw(Canvas canvas, ViewGroup parent, long drawingTime),用戶繪制View,此函數在繪制View的過程中會調用computeScroll()
@Override public void computeScroll() { // TODO Auto-generated method stub Log.e(TAG, "computeScroll"); if (mScroller.computeScrollOffset()) { //or !mScroller.isFinished() Log.e(TAG, mScroller.getCurrX() + "======" + mScroller.getCurrY()); scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); Log.e(TAG, "### getleft is " + getLeft() + " ### getRight is " + getRight()); postInvalidate(); } else Log.i(TAG, "have done the scoller -----"); }這段代碼在滑動view之前先調用mScroller.computeScrollOffset()來判斷滑動動畫是否已結束。computerScrollerOffset()的源代碼如下:
/** * Call this when you want to know the new location. If it returns true, * the animation is not yet finished. */ public boolean computeScrollOffset() { if (mFinished) { return false; } //滑動已經持續的時間 int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); //若在規定時間還未用完,則繼續設置新的滑動位置mCurrX和mCurry if (timePassed < mDuration) { switch (mMode) { case SCROLL_MODE: float x = timePassed * mDurationReciprocal; if (mInterpolator == null) x = viscousFluid(x); else x = mInterpolator.getInterpolation(x); mCurrX = mStartX + Math.round(x * mDeltaX); mCurrY = mStartY + Math.round(x * mDeltaY); break; case FLING_MODE: final float t = (float) timePassed / mDuration; final int index = (int) (NB_SAMPLES * t); float distanceCoef = 1.f; float velocityCoef = 0.f; if (index < NB_SAMPLES) { final float t_inf = (float) index / NB_SAMPLES; final float t_sup = (float) (index + 1) / NB_SAMPLES; final float d_inf = SPLINE_POSITION[index]; final float d_sup = SPLINE_POSITION[index + 1]; velocityCoef = (d_sup - d_inf) / (t_sup - t_inf); distanceCoef = d_inf + (t - t_inf) * velocityCoef; } mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f; mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX)); // Pin to mMinX <= mCurrX <= mMaxX mCurrX = Math.min(mCurrX, mMaxX); mCurrX = Math.max(mCurrX, mMinX); mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY)); // Pin to mMinY <= mCurrY <= mMaxY mCurrY = Math.min(mCurrY, mMaxY); mCurrY = Math.max(mCurrY, mMinY); if (mCurrX == mFinalX && mCurrY == mFinalY) { mFinished = true; } break; } } else { mCurrX = mFinalX; mCurrY = mFinalY; mFinished = true; } return true; }ViewGroup.computeScroll()被調用時機:
我們在開發控件時,常會有這樣的需求:當單機某個按鈕時,某個圖片會在規定的時間內滑出窗口,而不是一下子進入窗口。實現這個功能可以使用Scroller來實現。
下面給出一段代碼,該代碼控制下一個界面在3秒時間內緩慢進入的效果。
public void moveToRightSide(){ if (curScreen <= 0) { return; } curScreen-- ; Log.i(TAG, "----moveToRightSide---- curScreen " + curScreen); mScroller.startScroll((curScreen + 1) * getWidth(), 0, -getWidth(), 0, 3000); scrollTo(curScreen * getWidth(), 0); invalidate(); }上述代碼用到了一個函數:void android.widget.Scroller.startScroll(int startX, int startY, int dx, int dy, int duration)
/** * Start scrolling by providing a starting point, the distance to travel, * and the duration of the scroll. * * @param startX Starting horizontal scroll offset in pixels. Positive * numbers will scroll the content to the left. * @param startY Starting vertical scroll offset in pixels. Positive numbers * will scroll the content up. * @param dx Horizontal distance to travel. Positive numbers will scroll the * content to the left. * @param dy Vertical distance to travel. Positive numbers will scroll the * content up. * @param duration Duration of the scroll in milliseconds. */ public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; mFinished = false; mDuration = duration; mStartTime = AnimationUtils.currentAnimationTimeMillis(); mStartX = startX; mStartY = startY; mFinalX = startX + dx; mFinalY = startY + dy; mDeltaX = dx; mDeltaY = dy; mDurationReciprocal = 1.0f / (float) mDuration; }invalidate()會使得視圖重繪,導致parent調用了dispatchDraw(Canvas canvas),然後遞歸調用child View的draw()函數,該函數又會調用我們定義的computeScroll(), 而這個函數又會調用mScroller.computeScrollOffset()判斷動畫是否結束,若沒結束則繼續重繪直到直到startScroll中設置的時間耗盡mScroller.computeScrollOffset()返回false才停下來。
申明: 下面實現如何通過應用層支持多點觸控操作,對於常規的控件觸控操實現onTouchEvent()方法來處理。同時對onTouchEvent方法的參數Moti
直接使用線程在Android開發的時候,當我們需要完成一個耗時操作的時候,通常會新建一個子線程出來,例如如下代碼new Thread(new Runnable() {
本章簡單講述下android實現自動撥號的功能,該功能利用了系統啟動的rild的服務來實現,因為rild的服務是殺不死的,所以利用這一點,可以使撥號失敗或網絡斷掉後自動重
查了好多資料,現發還是不全,干脆自己整理吧,至少保證在我的做法正確的,以免誤導讀者,也是給自己做個記錄吧!簡介android供給了三種菜單類型,分別為options me