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)方式。
效果
使用方式
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,謝謝~