Android增強現(xiàn)實(二)-支持拖拽控制進度和伸縮的VrGifView

1.Android增強現(xiàn)實(一)-AR的三種方式(展示篇)
2.Android增強現(xiàn)實(二)-支持拖拽控制進度和伸縮的VrGifView
3.Android增強現(xiàn)實(三)-3D模型展示器

前言

前段時間研究了一下增強現(xiàn)實在Android端的實現(xiàn),目前大體分為兩種,全景立體圖(GIF和全景圖)和3D模型圖。這篇博客主要講一下關(guān)于GIF相關(guān)的實現(xiàn)方式。

效果

VrGifView

使用方式

1.Add it in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Step 2. Add the dependency

dependencies {
       compile 'com.github.sdfdzx:VRShow:v1.0.2'
}

XML and Java

<com.study.xuan.gifshow.widget.VrGifView
        android:id="@+id/gif"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/demo"
        />


public class GifActivity extends AppCompatActivity {
    private VrGifView mGif;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gif);
        mGif = (VrGifView) findViewById(R.id.gif);
        mGif.setTouch(true);//是否 可觸摸
        mGif.setDrag(true);//是否可拖拽
        mGif.setScale(false);//是否可伸縮
        mGif.setMoveMode(VrGifView.MODE_FAST);//觸摸響應速度
    }
}

技術(shù)分析

京東

大家應該在淘寶和京東上看到過這樣的實現(xiàn)效果吧,我對他的分析是這樣的:

1.首先這是一個商品全景自動旋轉(zhuǎn)的gif圖。
2.這個gif圖支持進度的調(diào)整。(淘寶支持拖拽控制,京東支持陀螺儀傳感器控制)

基于以上的分析首先我們要實現(xiàn)的組件肯定要支持這幾個關(guān)鍵點:
1.支持播放gif
2.支持gif的進度控制(重要

這兩個條件其實就第一個來說Glide就可以實現(xiàn),但是第二個條件就比較難了,其實gif就是圖片集的播放,想控制gif的進度,目前Glide我還沒有找到相關(guān)的api可以控制(大家知道的話可以評論告訴我~),考慮到第二個條件,我最后選用了比較知名的Android端加載Gif的開源庫android-gif-drawable,這個庫提供了對應的api來對進度進行控制。

Animation control
GifDrawable implements an Animatable and MediaPlayerControl so you can use its methods and more:

stop() - stops the animation, can be called from any thread
start() - starts the animation, can be called from any thread
isRunning() - returns whether animation is currently running or not
reset() - rewinds the animation, does not restart stopped one
setSpeed(float factor) - sets new animation speed factor, eg. passing 2.0f will double the animation speed
seekTo(int position) - seeks animation (within current loop) to given position (in milliseconds)
getDuration() - returns duration of one loop of the animation
getCurrentPosition() - returns elapsed time from the beginning of a current loop of animation

目前兩個前提條件找到了,現(xiàn)在的問題就是手勢控制進度了,目前看起來一帆風順沒有什么坑,繼續(xù)往下實現(xiàn)。

功能

既然前提條件已經(jīng)具備,現(xiàn)在就來提需求:

1.支持單指拖動
2.支持雙指縮放
3.考慮一定的性能

實現(xiàn)關(guān)鍵點

一.單指拖動
單指拖動似乎很簡單
實現(xiàn)思路:
1.獲取滑動的距離
2.獲取Gif的總進度,和MOVE時的當前的進度
3.滑動距離/屏幕寬度 = MOVE時的當前的進度/Gif的總進度,對應將滑動距離轉(zhuǎn)換成GIF的進度,從而通過seekTo來控制GIF的進度。

關(guān)鍵代碼:

private void rotateModel(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                if (touchMode == TOUCH_NONE && event.getPointerCount() == 1) {
                    touchMode = TOUCH_DRAG;
                    gifDrawable.stop();
                    lastX = event.getX();
                    downTime = event.getDownTime();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (touchMode == TOUCH_DRAG) {
                    //通過move的時間控制刷新頻率
                    if ((event.getEventTime() - downTime) > moveSpeed) {
                        moveX = event.getX();
                        moveDis = moveX - lastX;
                        lastX = moveX;
                        curPos = gifDrawable.getCurrentPosition();
                        if ((curPos + moveDis * PX_TO_POS) < 0) {
                            curPos += moveDis * PX_TO_POS + gifLength;
                        } else {
                            curPos += moveDis * PX_TO_POS;
                        }
                        if (curPos < 0) {
                            curPos = 0;
                        }
                        gifDrawable.seekTo(curPos);
                        downTime = event.getEventTime();
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (touchMode == TOUCH_DRAG) {
                    touchMode = TOUCH_NONE;
                }
                gifDrawable.start();
                break;
        }
    }

思路濾清了,代碼還是很好理解的,這里唯一需要注意的地方:注意對于界面刷新頻率的控制,一開始我沒有考慮這一點,簡單的只是移動了就改變進度,這時會發(fā)現(xiàn)在MOVE的過程中,gif會不停的閃黑屏,一開始我以為是我計算進度有問題,怎么改都沒有解決,后來我看了下GifDrawable的源碼,每次都會重繪,我就懷疑是由于MOVE過程中的滑動觸發(fā)頻率過快,導致刷新過快導致的,我便通過MOVE的時間來控制

if ((event.getEventTime() - downTime) > moveSpeed)

可以看到這里通過move時的時間點和down的時間點相減來控制觸發(fā)刷新的頻率,這里的moveSpeed是可以調(diào)整的。

/**
     * 設(shè)置觸摸觸發(fā)響應速度
     */
    public void setMoveMode(int mode) {
        switch (mode) {
            case MODE_FAST:
                moveMode = MODE_FAST;
                moveSpeed = SPEED_FAST;
                break;
            case MODE_NORMAL:
                moveMode = MODE_NORMAL;
                moveSpeed = SPEED_NORMAL;
                break;
            case MODE_LOW:
                moveMode = MODE_LOW;
                moveSpeed = SPEED_LOW;
                break;
            default:
                moveMode = MODE_NORMAL;
                moveSpeed = SPEED_NORMAL;
                break;
        }
    }

二.雙指縮放
網(wǎng)上對于雙指縮放的做法很多,有通過矩陣變換,有通過canvas的,這里我考慮到原圖是一個GIF,對于雙指縮放,我選擇使用屬性動畫來實現(xiàn)。
實現(xiàn)思路:
1.獲得雙指的距離
2.將距離轉(zhuǎn)換為scale的縮放量
3.利用ObjectAnimator來實現(xiàn)縮放。

關(guān)鍵代碼:

private void zoomScale(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            // starts pinch
            case MotionEvent.ACTION_POINTER_DOWN:
                if (event.getPointerCount() >= 2) {
                    pinchStartDistance = getPinchDistance(event);
                    downTime = event.getDownTime();
                    if (pinchStartDistance > 50f) {
                        touchMode = TOUCH_ZOOM;
                    }
                }
                break;

            case MotionEvent.ACTION_MOVE:
                if (touchMode == TOUCH_ZOOM && pinchStartDistance > 0) {
                    // on pinch
                    if ((event.getEventTime() - downTime) > moveSpeed) {
                        if (getPinchDistance(event) > pinchStartDistance) {
                            //遞增
                            isUp = true;
                        } else {
                            isUp = false;
                        }
                        pinchScale = getPinchDistance(event) / pinchStartDistance;
                        if (checkScale(pinchScale)) {
                            changeScale(pinchScale);
                        }
                    }
                }
                break;

            // end pinch
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                pinchScale = 0;
                if (touchMode == TOUCH_ZOOM) {
                    touchMode = TOUCH_NONE;
                }
                break;
        }
    }

這里轉(zhuǎn)化其實和上面的原理相近,但是這里有同樣有幾個坑需要踩一下:
難點:
1.刷新頻率
2.手指縮放誤差

和上面一樣,當我一氣呵成實現(xiàn)后發(fā)現(xiàn)并沒有想象的那么簡單,實現(xiàn)效果會發(fā)現(xiàn)當我雙指放大的時候,GIF的大小總是有時候會莫名其妙的變小,我通過將縮放量LOG打出來發(fā)現(xiàn),雖然我們的雙指手勢是放大,但是在放大的過程中由于停頓等其他原因會有間歇性的變小的趨勢,這樣GIF就會出現(xiàn)在變大的過程中變小,為了避免這樣的出現(xiàn),我的解決思路是這樣的:

1.過濾超小范圍的起始點
2.通過移動趨勢判斷時變大還是變小
3.執(zhí)行動畫之前判斷要執(zhí)行的動畫是否符合當前的變化趨勢。

case MotionEvent.ACTION_POINTER_DOWN:
                if (event.getPointerCount() >= 2) {
                    pinchStartDistance = getPinchDistance(event);
                    downTime = event.getDownTime();
                    //過濾超小范圍的起始點
                    if (pinchStartDistance > 50f) {
                        touchMode = TOUCH_ZOOM;
                    }
                }
                break;

可以看到我在down的時候?qū)τ诔》秶钠鹗键c是進行了過濾的,只有大于50的才算雙指縮放。

case MotionEvent.ACTION_MOVE:
                if (touchMode == TOUCH_ZOOM && pinchStartDistance > 0) {
                    // on pinch
                    if ((event.getEventTime() - downTime) > moveSpeed) {
                        //判斷趨勢
                        if (getPinchDistance(event) > pinchStartDistance) {
                            //遞增
                            isUp = true;
                        } else {
                            isUp = false;
                        }
                        pinchScale = getPinchDistance(event) / pinchStartDistance;
                        //檢查變化是否符合趨勢
                        if (checkScale(pinchScale)) {
                            changeScale(pinchScale);
                        }
                    }
                }
                break;


private boolean checkScale(float pinchScale) {
        if (canAnim) {
            if (isUp) {
                if (pinchScale > 1) {
                    return true;
                }
            } else {
                if (pinchScale < 1) {
                    return true;
                }
            }
        }
        return false;
    }

可以看到,這里比較了和down的時候的距離變化,來判斷時變大還是變小,最后在執(zhí)行動畫前先判斷一下當前執(zhí)行的動畫是否符合我們的移動趨勢,符合才執(zhí)行動畫,不符合不執(zhí)行。

總結(jié)

具體難點已經(jīng)分析完畢了,主要就是多思考一下,其實也沒有特別復雜的地方,只是在巨人的肩膀上封裝了一下,這里放上源碼地址

github地址:VRShow
喜歡的點個Star,謝謝~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373