日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線(xiàn)咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

 

縮放手勢(shì)對(duì)于大部分 Android 工程師來(lái)說(shuō),需要用到的機(jī)會(huì)比較少,它最常見(jiàn)于以下的一些應(yīng)用場(chǎng)景中,例如:圖片瀏覽,圖片編輯(貼圖效果)、網(wǎng)頁(yè)縮放、地圖、文本閱讀(通過(guò)縮放手勢(shì)調(diào)整文字大小)等。應(yīng)用場(chǎng)景相對(duì)比較狹窄,不過(guò)肯定也會(huì)有一些用武之地,它可以實(shí)現(xiàn)如下的效果:

縮放手勢(shì)的檢測(cè)

 

2.縮放手勢(shì)檢測(cè)(ScaleGestureDetector)

縮放手勢(shì)檢測(cè)同樣是官方提供的手勢(shì)檢測(cè)工具,它的使用方式的 GentureDetector 類(lèi)似,也是通過(guò) Listener 進(jìn)行監(jiān)聽(tīng)用戶(hù)的操作手勢(shì),它是對(duì)縮放手勢(shì)進(jìn)行了一次封裝, 可以方便用戶(hù)快速的完成縮放相關(guān)功能的開(kāi)發(fā)。縮放手勢(shì)相對(duì)比較簡(jiǎn)單,網(wǎng)絡(luò)上也能查到不少非官方實(shí)現(xiàn)的縮放手勢(shì)計(jì)算方案,但部分非官方的方案確實(shí)有所局限,例如只支持兩個(gè)手指的計(jì)算,在出現(xiàn)超過(guò)兩個(gè)手指時(shí),只計(jì)算了前兩個(gè)手指的移動(dòng),這樣顯然是不合理的。而官方的這種實(shí)現(xiàn)方案輕松的應(yīng)對(duì)了多個(gè)手指的情況,下面我們就來(lái)看看它是如何實(shí)現(xiàn)的吧。

2.1 構(gòu)造方法

它有兩個(gè)構(gòu)造方法,和 GestureDetector 類(lèi)似,如下所示:

ScaleGestureDetector(Contextcontext,ScaleGestureDetector.OnScaleGestureListenerlistener)
ScaleGestureDetector(Contextcontext,ScaleGestureDetector.OnScaleGestureListenerlistener,Handlerhandler) 

2.2 手勢(shì)監(jiān)聽(tīng)器

它只有兩個(gè)監(jiān)聽(tīng)器,但嚴(yán)格來(lái)說(shuō),這兩個(gè)監(jiān)聽(tīng)器是同一個(gè),只不過(guò)一個(gè)是接口,另一個(gè)是空實(shí)現(xiàn)而已。

監(jiān)聽(tīng)器 簡(jiǎn)介

OnScaleGestureListener 縮放手勢(shì)檢測(cè)器

SimpleOnScaleGestureListene r縮放手勢(shì)檢測(cè)器的空實(shí)現(xiàn)。

2.3 簡(jiǎn)單示例

這是使用 ScaleGestureDetector 的一個(gè)極簡(jiǎn)用例,當(dāng)然,它沒(méi)有實(shí)現(xiàn)任何功能,只是用日志的方式輸出了幾個(gè)我們比較關(guān)心的參數(shù)而已。

public class ScaleGestureDemoView extends View { 
 private static final String TAG = "ScaleGestureDemoView";
 private ScaleGestureDetector mScaleGestureDetector;
 public ScaleGestureDemoView(Context context) { 
 super(context);
 }
 public ScaleGestureDemoView(Context context, @Nullable AttributeSet attrs) {
 super(context, attrs);
 initScaleGestureDetector();
 } 
 private void initScaleGestureDetector() { 
 mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
 @Override
 public boolean onScaleBegin(ScaleGestureDetector detector) { 
 return true;
 } 
 @Override
 public boolean onScale(ScaleGestureDetector detector) {
 Log.i(TAG, "focusX = " + detector.getFocusX()); // 縮放中心,x坐標(biāo)
 Log.i(TAG, "focusY = " + detector.getFocusY()); // 縮放中心y坐標(biāo)
 Log.i(TAG, "scale = " + detector.getScaleFactor()); // 縮放因子
 return true;
 }
 @Override 
 public void onScaleEnd(ScaleGestureDetector detector) { 
 }
 });
 } 
 @Override
 public boolean onTouchEvent(MotionEvent event) {
 mScaleGestureDetector.onTouchEvent(event);
 return true; 
 }
 }

3. 基本原理

由于縮放手勢(shì)檢測(cè)使用起來(lái)非常簡(jiǎn)單,沒(méi)有什么復(fù)雜的內(nèi)容,不僅如此,它的實(shí)現(xiàn)也非常簡(jiǎn)單,下面我就帶大家簡(jiǎn)單分析一下它的基本原理。在縮放手勢(shì)中我們其實(shí)主要關(guān)心的只有兩個(gè)參數(shù)而已,一個(gè)是縮放的中心點(diǎn),另一個(gè)就是縮放比例了。 下面我們就看看這兩個(gè)參數(shù)是如何計(jì)算出來(lái)的.

3.1 計(jì)算縮放的中心點(diǎn)(焦點(diǎn))

如果只有兩個(gè)手指的話(huà),縮放的中心點(diǎn)自然是非常容易計(jì)算的,那就是兩個(gè)手指坐標(biāo)的中點(diǎn),但是如果有多個(gè)手指該如何計(jì)算縮放的中心點(diǎn)呢?

計(jì)算中心點(diǎn)的原理其實(shí)也非常簡(jiǎn)單,那就是將所有的坐標(biāo)都加起來(lái),然后除以數(shù)量即可。

這是一個(gè)簡(jiǎn)單的數(shù)學(xué)原理,并不復(fù)雜,如果有不理解的,自己嘗試計(jì)算一下也就能明白了。不過(guò)在實(shí)際運(yùn)用中還是需要注意一下的, 用戶(hù)的手指數(shù)量可能并不是固定的,用戶(hù)可能隨時(shí)抬起來(lái)或者按下手指,ScaleGestureDetector 中是這樣實(shí)現(xiàn)的:

 finalbooleananchoredScaleCancelled=
 mAnchoredScaleMode == ANCHORED_SCALE_MODE_STYLUS && !isStylusButtonDown;
 final boolean streamComplete = action == MotionEvent.ACTION_UP ||
 finalbooleanstreamComplete=action==MotionEvent.ACTION_UP||
 action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;
 // 注意這里
 if(action==MotionEvent.ACTION_DOWN||streamComplete){
 //重置偵聽(tīng)器正在進(jìn)行的任何縮放。
 //如果是ACTION_DOWN,我們正在開(kāi)始一個(gè)新的事件流。
 //這意味著應(yīng)用程序可能沒(méi)有給我們所有的事件(事件被上層直接攔截了)。
 if(mInProgress){
 mListener.onScaleEnd(this);
 mInProgress=false;
 mInitialSpan=0;
 mAnchoredScaleMode=ANCHORED_SCALE_MODE_NONE;
 }elseif(inAnchoredScaleMode()&&streamComplete){
 mInProgress=false;
 mInitialSpan=0;
 mAnchoredScaleMode=ANCHORED_SCALE_MODE_NONE;
 }
 if(streamComplete){
 returntrue;
 }
} 

可以看到,當(dāng)觸發(fā) down 或者觸發(fā) up,cancel 時(shí),如果之前處于縮放計(jì)算的狀態(tài),會(huì)將其狀態(tài)重置, 并調(diào)用 onScaleEnd 方法。

計(jì)算中心點(diǎn):

 final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
 action == MotionEvent.ACTION_POINTER_UP ||
 action == MotionEvent.ACTION_POINTER_DOWN || anchoredScaleCancelled; 
 // 注意這里
 final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; 
 final int skipIndex = pointerUp ? event.getActionIndex() : -1; 
 // 確定焦點(diǎn)
 float sumX = 0, sumY = 0;
 final int div = pointerUp ? count - 1 : count; 
 final float focusX; 
 final float focusY;
 if (inAnchoredScaleMode()) { 
 // 在錨定比例模式下,焦點(diǎn)始終是雙擊或按鈕按下時(shí)手勢(shì)開(kāi)始的位置
 focusX = mAnchoredScaleStartX; 
 focusY = mAnchoredScaleStartY; 
 if (event.getY() < focusY) {
 mEventBeforeOrAboveStartingGestureEvent = true;
 } else { 
 mEventBeforeOrAboveStartingGestureEvent = false; 
 } } else {
 // 注意這里, 最終計(jì)算得到焦點(diǎn) 
 for (int i = 0; i < count; i++) {
 if (skipIndex == i) continue;
 sumX += event.getX(i);
 sumY += event.getY(i);
 }
 focusX = sumX / div; 
 focusY = sumY / div;
 } 

3.2 計(jì)算縮放比例

計(jì)算縮放比例也很簡(jiǎn)單,就是計(jì)算各個(gè)手指到焦點(diǎn)的平均距離,在用戶(hù)手指移動(dòng)后用新的平均距離除以舊的平均距離,并以此計(jì)算得出縮放比例。

 // 計(jì)算到焦點(diǎn)的平均距離
 floatdevSumX=0,devSumY=0;
 for(inti=0;i<count;i++){
 if (skipIndex == i) continue;
 devSumX+=Math.abs(event.getX(i)-focusX);
 devSumY+=Math.abs(event.getY(i)-focusY);
 }
 finalfloatdevX=devSumX/div;
 finalfloatdevY=devSumY/div;
 // 注意這里
 finalfloatspanX=devX*2;
 finalfloatspanY=devY*2;
 finalfloatspan;
 if(inAnchoredScaleMode()){
 span=spanY;
 }else{
 // 相當(dāng)于 sqrt(x*x + y*y)
 span=(float)Math.hypot(spanX,spanY);
 }

當(dāng)用戶(hù)移動(dòng)的距離超過(guò)一定數(shù)值(數(shù)值大小由系統(tǒng)定義)后,會(huì)觸發(fā) onScaleBegin 方法,如果用戶(hù)在 onScaleBegin 方法里面返回了 true,表示接受事件后,就會(huì)重置縮放相關(guān)數(shù)值,并且開(kāi)始積累縮放因子。

 // mSpanSlop 和 mMinSpan 都是從系統(tǒng)里面取得的預(yù)定義數(shù)值,該數(shù)值實(shí)際上影響的是縮放的靈敏度。
 // 不過(guò)該參數(shù)并沒(méi)有提供設(shè)置的方法,如果對(duì)靈敏度不滿(mǎn)意的話(huà),和通過(guò)直接之際復(fù)制一個(gè) ScaleGestureDetector 到項(xiàng)目中, 并且修改其中的數(shù)值。
 finalintminSpan=inAnchoredScaleMode()?mSpanSlop:mMinSpan;
 if(!mInProgress&&span>=minSpan&&
 (wasInProgress||Math.abs(span-mInitialSpan)>mSpanSlop)){
 mPrevSpanX=mCurrSpanX=spanX;
 mPrevSpanY=mCurrSpanY=spanY;
 mPrevSpan=mCurrSpan=span;
 mPrevTime=mCurrTime;
 mInProgress=mListener.onScaleBegin(this);
 }

通知用戶(hù)縮放:

 if(action==MotionEvent.ACTION_MOVE{
 mCurrSpanX=spanX;
 mCurrSpanY=spanY;
 mCurrSpan=span;
 booleanupdatePrev=true;
 if(mInProgress){
 // 注意這里,用戶(hù)的返回值決定了是否重新計(jì)算縮放因子
 updatePrev=mListener.onScale(this);
 }
 // 如果用戶(hù)返回了 true ,就會(huì)重新計(jì)算縮放因子
 if(updatePrev){
 mPrevSpanX=mCurrSpanX;
 mPrevSpanY=mCurrSpanY;
 mPrevSpan=mCurrSpan;
 mPrevTime=mCurrTime;
 }
 }

由于縮放手勢(shì)檢測(cè)確實(shí)比較簡(jiǎn)單,也大概就這么多了,感興趣的話(huà),可以私信我

分享到:
標(biāo)簽:縮放 手勢(shì) Android
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定