国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

Android手勢(shì)ImageView三部曲 第三部

2019-12-12 03:28:12
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

接著上一節(jié) Android手勢(shì)ImageView三部曲(二)的往下走,我們講到了github上的GestureDetector框架,
先附上github鏈接:
https://github.com/Almeros/android-gesture-detectors
其實(shí)把這個(gè)框架的主體思想也是參考的Android自帶的ScaleGestureDetector工具類,ScaleGestureDetector估計(jì)是參考的GestureDetector工具類,不管誰(shuí)參考誰(shuí)的,既然被我們遇到了,我們就要變成自己的東西,真不能全變成自己的東西的話,至少

我們要了解下它的思想。

我們先了解一下android自帶的ScaleGestureDetector(縮放手勢(shì)監(jiān)測(cè)器):

ScaleGestureDetector跟GestureDetector構(gòu)造都差不多,但是ScaleGestureDetector只能用于監(jiān)測(cè)縮放的手勢(shì),而GestureDetector監(jiān)測(cè)的手勢(shì)就比較多了,我們上一節(jié)內(nèi)容中有提到。

ScaleGestureDetector的一些用法跟api,小伙伴自行去查看官網(wǎng)文檔:
https://developer.android.google.cn/reference/android/view/ScaleGestureDetector.html

我們?cè)趺词褂盟兀ㄎ乙缘谝还?jié)中最后一個(gè)demo為例)?

首先創(chuàng)建一個(gè)ScaleGestureDetector對(duì)象:

 private void initView() {    ....    mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());    ....  }

然后傳遞一個(gè)叫ScaleListener的回調(diào)接口給它,ScaleListener里面有三個(gè)回調(diào)方法:

 private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {    @Override    public boolean onScale(ScaleGestureDetector detector) {      mScaleFactor *= detector.getScaleFactor(); // scale change since previous event      // Don't let the object get too small or too large.      mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));      changeMatrix();      return true;    }    @Override    public boolean onScaleBegin(ScaleGestureDetector detector) {      return super.onScaleBegin(detector);    }    @Override    public void onScaleEnd(ScaleGestureDetector detector) {      super.onScaleEnd(detector);    }  }

小伙伴應(yīng)該看得懂哈,就是onScale放縮時(shí)回調(diào),onScaleBegin縮放開(kāi)始時(shí)回調(diào),onScaleEnd縮放完畢后回調(diào)。

最后在view的onTouchEvent方法中把事件給ScaleGestureDetector對(duì)象:

@Override  public boolean onTouchEvent(MotionEvent event) {    //把縮放事件給mScaleDetector    mScaleDetector.onTouchEvent(event);    return true;  }好啦~??!上

一節(jié)最后的時(shí)候,我寫(xiě)了一個(gè)小demo去實(shí)現(xiàn)了圖片的位移,下面我們繼續(xù)加上圖片的縮放:

public class MatrixImageView extends ImageView {  private Matrix currMatrix;  private GestureDetector detector;  private ScaleGestureDetector scaleDetector;  private float currX;//當(dāng)前圖片的x坐標(biāo)值  private float currY;//當(dāng)前圖片的y坐標(biāo)值  private float scaleFactor=1f;//當(dāng)前圖片的縮放值  public MatrixImageView(Context context, AttributeSet attrs) {    super(context, attrs);    initView();    detector=new GestureDetector(context,onGestureListener);    //創(chuàng)建一個(gè)縮放手勢(shì)監(jiān)測(cè)器    scaleDetector=new ScaleGestureDetector(context,onScaleGestureListener);  }  private void initView() {    currMatrix = new Matrix();    DisplayMetrics dm = getResources().getDisplayMetrics();    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);    bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true);    setImageBitmap(bitmap);  }  @Override  public boolean onTouchEvent(MotionEvent event) {    detector.onTouchEvent(event);    //把事件給scaleDetector    scaleDetector.onTouchEvent(event);    return true;  }  private void setMatrix(){    currMatrix.reset();    currMatrix.postTranslate(currX,currY);    currMatrix.postScale(scaleFactor,scaleFactor,getMeasuredWidth()/2,getMeasuredHeight()/2);    setImageMatrix(currMatrix);  }  private GestureDetector.SimpleOnGestureListener onGestureListener=new GestureDetector.SimpleOnGestureListener(){    @Override    public boolean onDown(MotionEvent e) {      return true;    }    @Override    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {      currX-=distanceX;      currY-=distanceY;      setMatrix();      return super.onScroll(e1, e2, distanceX, distanceY);    }  };  private ScaleGestureDetector.SimpleOnScaleGestureListener onScaleGestureListener=new ScaleGestureDetector.SimpleOnScaleGestureListener(){    @Override    public boolean onScale(ScaleGestureDetector detector) {      scaleFactor*=detector.getScaleFactor();      setMatrix();      /**       * 因?yàn)間etScaleFactor=當(dāng)前兩個(gè)手指之間的距離(preEvent)/手指按下時(shí)候兩個(gè)點(diǎn)的距離(currEvent)       * 這里如果返回true的話,會(huì)在move操作的時(shí)候去更新之前的event,       * 如果為false的話,不會(huì)去更新之前按下時(shí)候保存的event       */      return true;    }  };}

尷尬了,模擬器不太好用于兩個(gè)手指縮放的錄制,所以效果小伙伴自己拿到代碼運(yùn)行一下哈~!?。?/p>

下面一起擼一擼ScaleGestureDetector的源碼:
我們知道,ScaleGestureDetector核心代碼也就是onTouchEvent,于是我們點(diǎn)開(kāi)onTouchEvent:

@SuppressLint("NewApi")  public boolean onTouchEvent(MotionEvent event) {    //獲取當(dāng)前event事件    mCurrTime = event.getEventTime();    final int action = event.getActionMasked();    // Forward the event to check for double tap gesture    if (mQuickScaleEnabled) {      mGestureDetector.onTouchEvent(event);    }    //獲取手指?jìng)€(gè)數(shù)    final int count = event.getPointerCount();    final boolean isStylusButtonDown =        (event.getButtonState() & MotionEvent.BUTTON_STYLUS_PRIMARY) != 0;    final boolean anchoredScaleCancelled =        mAnchoredScaleMode == ANCHORED_SCALE_MODE_STYLUS && !isStylusButtonDown;    final boolean streamComplete = action == MotionEvent.ACTION_UP ||        action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;    //手指按下的時(shí)候    if (action == MotionEvent.ACTION_DOWN || streamComplete) {      // Reset any scale in progress with the listener.      // If it's an ACTION_DOWN we're beginning a new event stream.      // This means the app probably didn't give us all the events. Shame on it.      if (mInProgress) {        mListener.onScaleEnd(this);        mInProgress = false;        mInitialSpan = 0;        mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;      } else if (inAnchoredScaleMode() && streamComplete) {        mInProgress = false;        mInitialSpan = 0;        mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;      }      if (streamComplete) {        return true;      }    }    if (!mInProgress && mStylusScaleEnabled && !inAnchoredScaleMode()        && !streamComplete && isStylusButtonDown) {      // Start of a button scale gesture      mAnchoredScaleStartX = event.getX();      mAnchoredScaleStartY = event.getY();      mAnchoredScaleMode = ANCHORED_SCALE_MODE_STYLUS;      mInitialSpan = 0;    }    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()) {      // In anchored scale mode, the focal pt is always where the double tap      // or button down gesture started      focusX = mAnchoredScaleStartX;      focusY = mAnchoredScaleStartY;      if (event.getY() < focusY) {        mEventBeforeOrAboveStartingGestureEvent = true;      } else {        mEventBeforeOrAboveStartingGestureEvent = false;      }    } else {      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;    }    // Determine average deviation from focal point    float devSumX = 0, devSumY = 0;    for (int i = 0; i < count; i++) {      if (skipIndex == i) continue;      // Convert the resulting diameter into a radius.      devSumX += Math.abs(event.getX(i) - focusX);      devSumY += Math.abs(event.getY(i) - focusY);    }    final float devX = devSumX / div;    final float devY = devSumY / div;    final float spanX = devX * 2;    final float spanY = devY * 2;    final float span;    if (inAnchoredScaleMode()) {      span = spanY;    } else {      span = (float) Math.hypot(spanX, spanY);    }    // Dispatch begin/end events as needed.    // If the configuration changes, notify the app to reset its current state by beginning    // a fresh scale event stream.    final boolean wasInProgress = mInProgress;    mFocusX = focusX;    mFocusY = focusY;    if (!inAnchoredScaleMode() && mInProgress && (span < mMinSpan || configChanged)) {      mListener.onScaleEnd(this);      mInProgress = false;      mInitialSpan = span;    }    if (configChanged) {      mPrevSpanX = mCurrSpanX = spanX;      mPrevSpanY = mCurrSpanY = spanY;      mInitialSpan = mPrevSpan = mCurrSpan = span;    }    final int minSpan = 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);    }    // Handle motion; focal point and span/scale factor are changing.    if (action == MotionEvent.ACTION_MOVE) {      mCurrSpanX = spanX;      mCurrSpanY = spanY;      mCurrSpan = span;      boolean updatePrev = true;      if (mInProgress) {        updatePrev = mListener.onScale(this);      }      if (updatePrev) {        mPrevSpanX = mCurrSpanX;        mPrevSpanY = mCurrSpanY;        mPrevSpan = mCurrSpan;        mPrevTime = mCurrTime;      }    }    return true;  }

一堆代碼,數(shù)學(xué)不太好的看起來(lái)還真比較艱難,大概就是根據(jù)多個(gè)觸碰點(diǎn)的x坐標(biāo)算出一個(gè)x軸平均值,然后y軸也一樣,然后通過(guò)Math.hypot(spanX, spanY);算出斜邊長(zhǎng),斜邊長(zhǎng)即為兩點(diǎn)之間的距離,然后保存當(dāng)前的span跟移動(dòng)過(guò)后的span。

最后當(dāng)我們調(diào)用getScaleFactor獲取縮放比例的時(shí)候,即用現(xiàn)在的span/之前的span:

public float getScaleFactor() {    if (inAnchoredScaleMode()) {      // Drag is moving up; the further away from the gesture      // start, the smaller the span should be, the closer,      // the larger the span, and therefore the larger the scale      final boolean scaleUp =          (mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan < mPrevSpan)) ||              (!mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan > mPrevSpan));      final float spanDiff = (Math.abs(1 - (mCurrSpan / mPrevSpan)) * SCALE_FACTOR);      return mPrevSpan <= 0 ? 1 : scaleUp ? (1 + spanDiff) : (1 - spanDiff);    }    return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;  }

這數(shù)學(xué)渣真的是硬傷啊~~~

有了android自帶的ScaleGestureDetector作為參考,我們能自己實(shí)現(xiàn)ScaleGestureDetector嗎?? 當(dāng)然github上大神已經(jīng)實(shí)現(xiàn)了,但是如果沒(méi)有的話,你能寫(xiě)出來(lái)么?

先寫(xiě)到這,未完待續(xù)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 修文县| 城步| 天津市| 巴彦县| 太湖县| 全州县| 德安县| 双城市| 于都县| 武功县| 融水| 伊金霍洛旗| 曲麻莱县| 夹江县| 邵东县| 新绛县| 肇东市| 马关县| 讷河市| 正蓝旗| 环江| 金溪县| 内黄县| 通海县| 鄯善县| 洛川县| 麻江县| 武威市| 偏关县| 奇台县| 剑阁县| 屏东县| 乐都县| 潼南县| 永康市| 湘阴县| 招远市| 巫溪县| 安仁县| 宝清县| 武鸣县|