欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 3D滑動(dòng)菜單完全解析 Android實(shí)現(xiàn)推拉門(mén)式的立體特效

 更新時(shí)間:2017年11月30日 09:40:31   作者:guolin  
這篇文章主要為大家詳細(xì)介紹了Android 3D滑動(dòng)菜單,Android實(shí)現(xiàn)推拉門(mén)式的立體特效,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

在上一篇文章中,我們學(xué)習(xí)了Camera的基本用法,并借助它們編寫(xiě)了一個(gè)例子,實(shí)現(xiàn)了類(lèi)似于A(yíng)PI Demos里的圖片中軸旋轉(zhuǎn)功能。不過(guò)那個(gè)例子的核心代碼是來(lái)自于A(yíng)PI Demos中帶有的Rotate3dAnimation這個(gè)類(lèi),是它幫助我們完成了所有的三維旋轉(zhuǎn)操作,所有Matrix和Camera相關(guān)的代碼也是封裝在這個(gè)類(lèi)中。

這樣說(shuō)來(lái)的話(huà),大家心里會(huì)不會(huì)癢癢的呢?雖然學(xué)習(xí)了Camera的用法,但卻沒(méi)有按照自己的理解來(lái)實(shí)現(xiàn)一套非常炫酷的3D效果。不要著急,今天我就帶著大家一起來(lái)實(shí)現(xiàn)一種3D推拉門(mén)式的滑動(dòng)菜單,而且完全不會(huì)借助任何API Demos里面的代碼。

當(dāng)然如果你還不是很了解Camera的使用方式,可以先去閱讀我的上一篇文章 Android中軸旋轉(zhuǎn)特效實(shí)現(xiàn),制作別樣的圖片瀏覽器

關(guān)于滑動(dòng)菜單的文章我也已經(jīng)寫(xiě)過(guò)好幾篇了,相信看過(guò)的朋友對(duì)滑動(dòng)菜單的實(shí)現(xiàn)方式應(yīng)該都已經(jīng)比較熟悉了,那么本篇文章的重點(diǎn)就在于,如何在傳統(tǒng)滑動(dòng)菜單的基礎(chǔ)上加入推拉門(mén)式的立體效果。還不了解滑動(dòng)菜單如何實(shí)現(xiàn)的朋友,可以去翻一翻我之前的文章。說(shuō)到這里我必須要吐槽一下了,最近發(fā)現(xiàn)有不少的網(wǎng)站和個(gè)人將我的文章惡意轉(zhuǎn)走,而且還特意把第一行的原文地址信息去除掉。更可氣的是,在百度上搜索我文章的標(biāo)題時(shí),竟然先找到的是那些轉(zhuǎn)載我文章的網(wǎng)站。唉,傷心了,看來(lái)還是谷歌比較正常。因此今天我也是在這里特別申明一下,我所寫(xiě)的所有文章均是首發(fā)于CSDN博客,如果你閱讀這篇文章時(shí)是在別的網(wǎng)站,那么你將無(wú)法找到我前面所寫(xiě)的關(guān)于傳統(tǒng)滑動(dòng)菜單的文章,而且你的疑問(wèn)和留言也將得不到解答。

下面還是回到正題,首先來(lái)講一下這次的實(shí)現(xiàn)原理吧,其實(shí)傳統(tǒng)的滑動(dòng)菜單功能就是把菜單部分放在了下面,主布局放在了上面,然后根據(jù)手指滑動(dòng)的距離來(lái)偏移主布局,讓菜單部分得以顯示出來(lái)就行了。不過(guò)我們這次既然要做推拉門(mén)式的立體效果,就需要將傳統(tǒng)的思維稍微轉(zhuǎn)變一下,可以先讓菜單部分隱藏掉,但卻復(fù)制一個(gè)菜單的鏡像并生成一張圖片,然后在手指滑動(dòng)的時(shí)候?qū)@張圖片進(jìn)行三維操作,讓它產(chǎn)生推拉門(mén)式的效果,等滑動(dòng)操作結(jié)束的時(shí)候,才讓真正的菜單顯示出來(lái),然后將這個(gè)圖片隱藏。原理示意圖如下所示:

那么下面我們就開(kāi)始動(dòng)手實(shí)現(xiàn)吧,首先新建一個(gè)Android項(xiàng)目,起名叫做ThreeDSlidingLayoutDemo。

然后新建一個(gè)Image3dView類(lèi)繼承自View,用于生成鏡像圖片,以及完成三維操作,代碼如下所示:

public class Image3dView extends View { 
 
 /** 
 * 源視圖,用于生成圖片對(duì)象。 
 */ 
 private View sourceView; 
 
 /** 
 * 根據(jù)傳入的源視圖生成的圖片對(duì)象。 
 */ 
 private Bitmap sourceBitmap; 
 
 /** 
 * 源視圖的寬度。 
 */ 
 private float sourceWidth; 
 
 /** 
 * Matrix對(duì)象,用于對(duì)圖片進(jìn)行矩陣操作。 
 */ 
 private Matrix matrix = new Matrix(); 
 
 /** 
 * Camera對(duì)象,用于對(duì)圖片進(jìn)行三維操作。 
 */ 
 private Camera camera = new Camera(); 
 
 /** 
 * Image3dView的構(gòu)造函數(shù) 
 * 
 * @param context 
 * @param attrs 
 */ 
 public Image3dView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 } 
 
 /** 
 * 提供外部接口,允許向Image3dView傳入源視圖。 
 * 
 * @param view 
 *  傳入的源視圖 
 */ 
 public void setSourceView(View view) { 
 sourceView = view; 
 sourceWidth = sourceView.getWidth(); 
 } 
 
 /** 
 * 清除掉緩存的圖片對(duì)象。 
 */ 
 public void clearSourceBitmap() { 
 if (sourceBitmap != null) { 
  sourceBitmap = null; 
 } 
 } 
 
 @Override 
 protected void onDraw(Canvas canvas) { 
 super.onDraw(canvas); 
 if (sourceBitmap == null) { 
  getSourceBitmap(); 
 } 
 // 計(jì)算圖片需要旋轉(zhuǎn)的角度 
 float degree = 90 - (90 / sourceWidth) * getWidth(); 
 camera.save(); 
 camera.rotateY(degree); 
 camera.getMatrix(matrix); 
 camera.restore(); 
 // 將旋轉(zhuǎn)的中心點(diǎn)移動(dòng)到屏幕左邊緣的中間位置 
 matrix.preTranslate(0, -getHeight() / 2); 
 matrix.postTranslate(0, getHeight() / 2); 
 canvas.drawBitmap(sourceBitmap, matrix, null); 
 } 
 
 /** 
 * 獲取源視圖對(duì)應(yīng)的圖片對(duì)象。 
 */ 
 private void getSourceBitmap() { 
 if (sourceView != null) { 
  sourceView.setDrawingCacheEnabled(true); 
  sourceView.layout(0, 0, sourceView.getWidth(), sourceView.getHeight()); 
  sourceView.buildDrawingCache(); 
  sourceBitmap = sourceView.getDrawingCache(); 
 } 
 } 
 
} 

可以看到,Image3dView中提供了一個(gè)setSourceView()方法,用于傳遞源視圖進(jìn)來(lái),我們稍后復(fù)制鏡像就是對(duì)它進(jìn)行復(fù)制。然后在onDraw()方法里對(duì)sourceBitmap進(jìn)行判斷,如果為空,則去調(diào)用getSourceBitmap()方法來(lái)生成一張鏡像圖片,getSourceBitmap()方法的細(xì)節(jié)大家自己去看。在獲得了鏡像圖片之后,接下來(lái)就是要計(jì)算圖片的旋轉(zhuǎn)角度了,這里根據(jù)Image3dView當(dāng)前的寬度和源視圖的總寬度進(jìn)行對(duì)比,按比例算出旋轉(zhuǎn)的角度。然后調(diào)用Camera的rotateY()方法,讓圖片團(tuán)練Y軸進(jìn)行旋轉(zhuǎn),并將旋轉(zhuǎn)的中心點(diǎn)移動(dòng)到屏幕左邊緣的中間位置,這幾行代碼我們?cè)谏掀恼轮幸呀?jīng)見(jiàn)過(guò)了,算是挺熟悉了吧!最后調(diào)用Canvas的drawBitmap()方法把圖片繪制出來(lái)。

完成了Image3dView之后,接著我們要開(kāi)始編寫(xiě)滑動(dòng)菜單部分的代碼,其實(shí)這次的代碼和之前的滑動(dòng)菜單代碼大同小異,看過(guò)我前面文章的朋友,這次理解起來(lái)一定會(huì)輕而易舉。新建ThreeDSlidingLayout類(lèi),代碼如下所示:

public class ThreeDSlidingLayout extends RelativeLayout implements OnTouchListener { 
 
 /** 
 * 滾動(dòng)顯示和隱藏左側(cè)布局時(shí),手指滑動(dòng)需要達(dá)到的速度。 
 */ 
 public static final int SNAP_VELOCITY = 200; 
 
 /** 
 * 滑動(dòng)狀態(tài)的一種,表示未進(jìn)行任何滑動(dòng)。 
 */ 
 public static final int DO_NOTHING = 0; 
 
 /** 
 * 滑動(dòng)狀態(tài)的一種,表示正在滑出左側(cè)菜單。 
 */ 
 public static final int SHOW_MENU = 1; 
 
 /** 
 * 滑動(dòng)狀態(tài)的一種,表示正在隱藏左側(cè)菜單。 
 */ 
 public static final int HIDE_MENU = 2; 
 
 /** 
 * 記錄當(dāng)前的滑動(dòng)狀態(tài) 
 */ 
 private int slideState; 
 
 /** 
 * 屏幕寬度值。 
 */ 
 private int screenWidth; 
 
 /** 
 * 右側(cè)布局最多可以滑動(dòng)到的左邊緣。 
 */ 
 private int leftEdge = 0; 
 
 /** 
 * 右側(cè)布局最多可以滑動(dòng)到的右邊緣。 
 */ 
 private int rightEdge = 0; 
 
 /** 
 * 在被判定為滾動(dòng)之前用戶(hù)手指可以移動(dòng)的最大值。 
 */ 
 private int touchSlop; 
 
 /** 
 * 記錄手指按下時(shí)的橫坐標(biāo)。 
 */ 
 private float xDown; 
 
 /** 
 * 記錄手指按下時(shí)的縱坐標(biāo)。 
 */ 
 private float yDown; 
 
 /** 
 * 記錄手指移動(dòng)時(shí)的橫坐標(biāo)。 
 */ 
 private float xMove; 
 
 /** 
 * 記錄手指移動(dòng)時(shí)的縱坐標(biāo)。 
 */ 
 private float yMove; 
 
 /** 
 * 記錄手機(jī)抬起時(shí)的橫坐標(biāo)。 
 */ 
 private float xUp; 
 
 /** 
 * 左側(cè)布局當(dāng)前是顯示還是隱藏。只有完全顯示或隱藏時(shí)才會(huì)更改此值,滑動(dòng)過(guò)程中此值無(wú)效。 
 */ 
 private boolean isLeftLayoutVisible; 
 
 /** 
 * 是否正在滑動(dòng)。 
 */ 
 private boolean isSliding; 
 
 /** 
 * 是否已加載過(guò)一次layout,這里onLayout中的初始化只需加載一次 
 */ 
 private boolean loadOnce; 
 
 /** 
 * 左側(cè)布局對(duì)象。 
 */ 
 private View leftLayout; 
 
 /** 
 * 右側(cè)布局對(duì)象。 
 */ 
 private View rightLayout; 
 
 /** 
 * 在滑動(dòng)過(guò)程中展示的3D視圖 
 */ 
 private Image3dView image3dView; 
 
 /** 
 * 用于監(jiān)聽(tīng)側(cè)滑事件的View。 
 */ 
 private View mBindView; 
 
 /** 
 * 左側(cè)布局的參數(shù),通過(guò)此參數(shù)來(lái)重新確定左側(cè)布局的寬度,以及更改leftMargin的值。 
 */ 
 private MarginLayoutParams leftLayoutParams; 
 
 /** 
 * 右側(cè)布局的參數(shù),通過(guò)此參數(shù)來(lái)重新確定右側(cè)布局的寬度。 
 */ 
 private MarginLayoutParams rightLayoutParams; 
 
 /** 
 * 3D視圖的參數(shù),通過(guò)此參數(shù)來(lái)重新確定3D視圖的寬度。 
 */ 
 private ViewGroup.LayoutParams image3dViewParams; 
 
 /** 
 * 用于計(jì)算手指滑動(dòng)的速度。 
 */ 
 private VelocityTracker mVelocityTracker; 
 
 /** 
 * 重寫(xiě)SlidingLayout的構(gòu)造函數(shù),其中獲取了屏幕的寬度。 
 * 
 * @param context 
 * @param attrs 
 */ 
 public ThreeDSlidingLayout(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 
 screenWidth = wm.getDefaultDisplay().getWidth(); 
 touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 
 } 
 
 /** 
 * 綁定監(jiān)聽(tīng)側(cè)滑事件的View,即在綁定的View進(jìn)行滑動(dòng)才可以顯示和隱藏左側(cè)布局。 
 * 
 * @param bindView 
 *  需要綁定的View對(duì)象。 
 */ 
 public void setScrollEvent(View bindView) { 
 mBindView = bindView; 
 mBindView.setOnTouchListener(this); 
 } 
 
 /** 
 * 將屏幕滾動(dòng)到左側(cè)布局界面,滾動(dòng)速度設(shè)定為10. 
 */ 
 public void scrollToLeftLayout() { 
 image3dView.clearSourceBitmap(); 
 new ScrollTask().execute(-10); 
 } 
 
 /** 
 * 將屏幕滾動(dòng)到右側(cè)布局界面,滾動(dòng)速度設(shè)定為-10. 
 */ 
 public void scrollToRightLayout() { 
 image3dView.clearSourceBitmap(); 
 new ScrollTask().execute(10); 
 } 
 
 /** 
 * 左側(cè)布局是否完全顯示出來(lái),或完全隱藏,滑動(dòng)過(guò)程中此值無(wú)效。 
 * 
 * @return 左側(cè)布局完全顯示返回true,完全隱藏返回false。 
 */ 
 public boolean isLeftLayoutVisible() { 
 return isLeftLayoutVisible; 
 } 
 
 /** 
 * 在onLayout中重新設(shè)定左側(cè)布局和右側(cè)布局的參數(shù)。 
 */ 
 @Override 
 protected void onLayout(boolean changed, int l, int t, int r, int b) { 
 super.onLayout(changed, l, t, r, b); 
 if (changed && !loadOnce) { 
  // 獲取左側(cè)布局對(duì)象 
  leftLayout = findViewById(R.id.menu); 
  leftLayoutParams = (MarginLayoutParams) leftLayout.getLayoutParams(); 
  rightEdge = -leftLayoutParams.width; 
  // 獲取右側(cè)布局對(duì)象 
  rightLayout = findViewById(R.id.content); 
  rightLayoutParams = (MarginLayoutParams) rightLayout.getLayoutParams(); 
  rightLayoutParams.width = screenWidth; 
  rightLayout.setLayoutParams(rightLayoutParams); 
  // 獲取3D視圖對(duì)象 
  image3dView = (Image3dView) findViewById(R.id.image_3d_view); 
  // 將左側(cè)布局傳入3D視圖中作為生成源 
  image3dView.setSourceView(leftLayout); 
  loadOnce = true; 
 } 
 } 
 
 @Override 
 public boolean onTouch(View v, MotionEvent event) { 
 createVelocityTracker(event); 
 switch (event.getAction()) { 
 case MotionEvent.ACTION_DOWN: 
  // 手指按下時(shí),記錄按下時(shí)的橫坐標(biāo) 
  xDown = event.getRawX(); 
  yDown = event.getRawY(); 
  slideState = DO_NOTHING; 
  break; 
 case MotionEvent.ACTION_MOVE: 
  // 手指移動(dòng)時(shí),對(duì)比按下時(shí)的橫坐標(biāo),計(jì)算出移動(dòng)的距離,來(lái)調(diào)整右側(cè)布局的leftMargin值,從而顯示和隱藏左側(cè)布局 
  xMove = event.getRawX(); 
  yMove = event.getRawY(); 
  int moveDistanceX = (int) (xMove - xDown); 
  int moveDistanceY = (int) (yMove - yDown); 
  checkSlideState(moveDistanceX, moveDistanceY); 
  switch (slideState) { 
  case SHOW_MENU: 
  rightLayoutParams.rightMargin = -moveDistanceX; 
  onSlide(); 
  break; 
  case HIDE_MENU: 
  rightLayoutParams.rightMargin = rightEdge - moveDistanceX; 
  onSlide(); 
  break; 
  default: 
  break; 
  } 
  break; 
 case MotionEvent.ACTION_UP: 
  xUp = event.getRawX(); 
  int upDistanceX = (int) (xUp - xDown); 
  if (isSliding) { 
  // 手指抬起時(shí),進(jìn)行判斷當(dāng)前手勢(shì)的意圖 
  switch (slideState) { 
  case SHOW_MENU: 
   if (shouldScrollToLeftLayout()) { 
   scrollToLeftLayout(); 
   } else { 
   scrollToRightLayout(); 
   } 
   break; 
  case HIDE_MENU: 
   if (shouldScrollToRightLayout()) { 
   scrollToRightLayout(); 
   } else { 
   scrollToLeftLayout(); 
   } 
   break; 
  default: 
   break; 
  } 
  } else if (upDistanceX < touchSlop && isLeftLayoutVisible) { 
  scrollToRightLayout(); 
  } 
  recycleVelocityTracker(); 
  break; 
 } 
 if (v.isEnabled()) { 
  if (isSliding) { 
  unFocusBindView(); 
  return true; 
  } 
  if (isLeftLayoutVisible) { 
  return true; 
  } 
  return false; 
 } 
 return true; 
 } 
 
 /** 
 * 執(zhí)行滑動(dòng)過(guò)程中的邏輯操作,如邊界檢查,改變偏移值,可見(jiàn)性檢查等。 
 */ 
 private void onSlide() { 
 checkSlideBorder(); 
 rightLayout.setLayoutParams(rightLayoutParams); 
 image3dView.clearSourceBitmap(); 
 image3dViewParams = image3dView.getLayoutParams(); 
 image3dViewParams.width = -rightLayoutParams.rightMargin; 
 // 滑動(dòng)的同時(shí)改變3D視圖的大小 
 image3dView.setLayoutParams(image3dViewParams); 
 // 保證在滑動(dòng)過(guò)程中3D視圖可見(jiàn),左側(cè)布局不可見(jiàn) 
 showImage3dView(); 
 } 
 
 /** 
 * 根據(jù)手指移動(dòng)的距離,判斷當(dāng)前用戶(hù)的滑動(dòng)意圖,然后給slideState賦值成相應(yīng)的滑動(dòng)狀態(tài)值。 
 * 
 * @param moveDistanceX 
 *  橫向移動(dòng)的距離 
 * @param moveDistanceY 
 *  縱向移動(dòng)的距離 
 */ 
 private void checkSlideState(int moveDistanceX, int moveDistanceY) { 
 if (isLeftLayoutVisible) { 
  if (!isSliding && Math.abs(moveDistanceX) >= touchSlop && moveDistanceX < 0) { 
  isSliding = true; 
  slideState = HIDE_MENU; 
  } 
 } else if (!isSliding && Math.abs(moveDistanceX) >= touchSlop && moveDistanceX > 0 
  && Math.abs(moveDistanceY) < touchSlop) { 
  isSliding = true; 
  slideState = SHOW_MENU; 
 } 
 } 
 
 /** 
 * 在滑動(dòng)過(guò)程中檢查左側(cè)菜單的邊界值,防止綁定布局滑出屏幕。 
 */ 
 private void checkSlideBorder() { 
 if (rightLayoutParams.rightMargin > leftEdge) { 
  rightLayoutParams.rightMargin = leftEdge; 
 } else if (rightLayoutParams.rightMargin < rightEdge) { 
  rightLayoutParams.rightMargin = rightEdge; 
 } 
 } 
 
 /** 
 * 判斷是否應(yīng)該滾動(dòng)將左側(cè)布局展示出來(lái)。如果手指移動(dòng)距離大于屏幕的1/2,或者手指移動(dòng)速度大于SNAP_VELOCITY, 
 * 就認(rèn)為應(yīng)該滾動(dòng)將左側(cè)布局展示出來(lái)。 
 * 
 * @return 如果應(yīng)該滾動(dòng)將左側(cè)布局展示出來(lái)返回true,否則返回false。 
 */ 
 private boolean shouldScrollToLeftLayout() { 
 return xUp - xDown > leftLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; 
 } 
 
 /** 
 * 判斷是否應(yīng)該滾動(dòng)將右側(cè)布局展示出來(lái)。如果手指移動(dòng)距離加上leftLayoutPadding大于屏幕的1/2, 
 * 或者手指移動(dòng)速度大于SNAP_VELOCITY, 就認(rèn)為應(yīng)該滾動(dòng)將右側(cè)布局展示出來(lái)。 
 * 
 * @return 如果應(yīng)該滾動(dòng)將右側(cè)布局展示出來(lái)返回true,否則返回false。 
 */ 
 private boolean shouldScrollToRightLayout() { 
 return xDown - xUp > leftLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; 
 } 
 
 /** 
 * 創(chuàng)建VelocityTracker對(duì)象,并將觸摸事件加入到VelocityTracker當(dāng)中。 
 * 
 * @param event 
 *  右側(cè)布局監(jiān)聽(tīng)控件的滑動(dòng)事件 
 */ 
 private void createVelocityTracker(MotionEvent event) { 
 if (mVelocityTracker == null) { 
  mVelocityTracker = VelocityTracker.obtain(); 
 } 
 mVelocityTracker.addMovement(event); 
 } 
 
 /** 
 * 獲取手指在右側(cè)布局的監(jiān)聽(tīng)View上的滑動(dòng)速度。 
 * 
 * @return 滑動(dòng)速度,以每秒鐘移動(dòng)了多少像素值為單位。 
 */ 
 private int getScrollVelocity() { 
 mVelocityTracker.computeCurrentVelocity(1000); 
 int velocity = (int) mVelocityTracker.getXVelocity(); 
 return Math.abs(velocity); 
 } 
 
 /** 
 * 回收VelocityTracker對(duì)象。 
 */ 
 private void recycleVelocityTracker() { 
 mVelocityTracker.recycle(); 
 mVelocityTracker = null; 
 } 
 
 /** 
 * 使用可以獲得焦點(diǎn)的控件在滑動(dòng)的時(shí)候失去焦點(diǎn)。 
 */ 
 private void unFocusBindView() { 
 if (mBindView != null) { 
  mBindView.setPressed(false); 
  mBindView.setFocusable(false); 
  mBindView.setFocusableInTouchMode(false); 
 } 
 } 
 
 /** 
 * 保證此時(shí)讓左側(cè)布局不可見(jiàn),3D視圖可見(jiàn),從而讓滑動(dòng)過(guò)程中產(chǎn)生3D的效果。 
 */ 
 private void showImage3dView() { 
 if (image3dView.getVisibility() != View.VISIBLE) { 
  image3dView.setVisibility(View.VISIBLE); 
 } 
 if (leftLayout.getVisibility() != View.INVISIBLE) { 
  leftLayout.setVisibility(View.INVISIBLE); 
 } 
 } 
 
 class ScrollTask extends AsyncTask<Integer, Integer, Integer> { 
 
 @Override 
 protected Integer doInBackground(Integer... speed) { 
  int rightMargin = rightLayoutParams.rightMargin; 
  // 根據(jù)傳入的速度來(lái)滾動(dòng)界面,當(dāng)滾動(dòng)到達(dá)左邊界或右邊界時(shí),跳出循環(huán)。 
  while (true) { 
  rightMargin = rightMargin + speed[0]; 
  if (rightMargin < rightEdge) { 
   rightMargin = rightEdge; 
   break; 
  } 
  if (rightMargin > leftEdge) { 
   rightMargin = leftEdge; 
   break; 
  } 
  publishProgress(rightMargin); 
  // 為了要有滾動(dòng)效果產(chǎn)生,每次循環(huán)使線(xiàn)程睡眠5毫秒,這樣肉眼才能夠看到滾動(dòng)動(dòng)畫(huà)。 
  sleep(5); 
  } 
  if (speed[0] > 0) { 
  isLeftLayoutVisible = false; 
  } else { 
  isLeftLayoutVisible = true; 
  } 
  isSliding = false; 
  return rightMargin; 
 } 
 
 @Override 
 protected void onProgressUpdate(Integer... rightMargin) { 
  rightLayoutParams.rightMargin = rightMargin[0]; 
  rightLayout.setLayoutParams(rightLayoutParams); 
  image3dViewParams = image3dView.getLayoutParams(); 
  image3dViewParams.width = -rightLayoutParams.rightMargin; 
  image3dView.setLayoutParams(image3dViewParams); 
  showImage3dView(); 
  unFocusBindView(); 
 } 
 
 @Override 
 protected void onPostExecute(Integer rightMargin) { 
  rightLayoutParams.rightMargin = rightMargin; 
  rightLayout.setLayoutParams(rightLayoutParams); 
  image3dViewParams = image3dView.getLayoutParams(); 
  image3dViewParams.width = -rightLayoutParams.rightMargin; 
  image3dView.setLayoutParams(image3dViewParams); 
  if (isLeftLayoutVisible) { 
  // 保證在滑動(dòng)結(jié)束后左側(cè)布局可見(jiàn),3D視圖不可見(jiàn)。 
  image3dView.setVisibility(View.INVISIBLE); 
  leftLayout.setVisibility(View.VISIBLE); 
  } 
 } 
 } 
 
 /** 
 * 使當(dāng)前線(xiàn)程睡眠指定的毫秒數(shù)。 
 * 
 * @param millis 
 *  指定當(dāng)前線(xiàn)程睡眠多久,以毫秒為單位 
 */ 
 private void sleep(long millis) { 
 try { 
  Thread.sleep(millis); 
 } catch (InterruptedException e) { 
  e.printStackTrace(); 
 } 
 } 
} 

代碼比較長(zhǎng),我還是帶著大家來(lái)理一下思路。首先在onLayout方法中,我們分別初始化了左側(cè)布局對(duì)象、右側(cè)布局對(duì)象和Image3dView對(duì)象,這三個(gè)對(duì)象稍后都要配置到Activity布局里面的。在onLayout()方法的最后,調(diào)用了Image3dView的setSourceView()方法,并將左側(cè)布局對(duì)象傳了進(jìn)去,說(shuō)明我們后面就要對(duì)它進(jìn)行鏡像復(fù)制。

當(dāng)手指在界面上拖動(dòng)來(lái)顯示左側(cè)布局的時(shí)候,就會(huì)進(jìn)入到onTouch()方法中,這里會(huì)調(diào)用checkSlideState()方法來(lái)檢查滑動(dòng)的狀態(tài),以判斷用戶(hù)是想要顯示左側(cè)布局還是隱藏左側(cè)布局,然后根據(jù)手指滑動(dòng)的距離對(duì)右側(cè)布局進(jìn)行偏移,就可以實(shí)現(xiàn)基本的滑動(dòng)效果了。接下來(lái)是重點(diǎn)內(nèi)容,這里會(huì)根據(jù)右側(cè)布局的偏移量來(lái)改變Image3dView的寬度,當(dāng)Image3dView大小發(fā)生改變時(shí),當(dāng)然會(huì)調(diào)用onDraw()方法來(lái)進(jìn)行重繪,此時(shí)我們編寫(xiě)的三維旋轉(zhuǎn)邏輯就可以得到執(zhí)行了,于是就會(huì)產(chǎn)生立體的推拉門(mén)式效果。注意,在整個(gè)的滑動(dòng)過(guò)程中,真正的左側(cè)布局一直都是不可見(jiàn)的,我們所看到的只是它的一張鏡像圖片。

當(dāng)手指離開(kāi)屏幕后,會(huì)根據(jù)當(dāng)前的移動(dòng)距離來(lái)決定是顯示左側(cè)布局還是隱藏左側(cè)布局,并會(huì)調(diào)用scrollToLeftLayout()方法或scrollToRightLayout()方法來(lái)完成后續(xù)的滾動(dòng)操作。當(dāng)整個(gè)滾動(dòng)操作完成之后,才會(huì)將真正的左側(cè)布局顯示出來(lái),再把鏡像圖片隱藏掉,這樣用戶(hù)就可以點(diǎn)擊左側(cè)布局上按鈕之類(lèi)的東西了。

接著我們需要在A(yíng)ctivity的布局文件當(dāng)中去引用這個(gè)三維滑動(dòng)菜單框架,打開(kāi)或新建activity_main.xml作為程序的主布局文件,代碼如下所示:

<com.example.slidinglayout3d.ThreeDSlidingLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:tools="http://schemas.android.com/tools" 
 android:id="@+id/slidingLayout" 
 android:layout_width="fill_parent" 
 android:layout_height="fill_parent" > 
 
 <RelativeLayout 
 android:id="@+id/menu" 
 android:layout_width="270dip" 
 android:layout_height="fill_parent" 
 android:layout_alignParentLeft="true" 
 android:background="#00ccff" 
 android:visibility="invisible" > 
 
 <LinearLayout 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:layout_centerInParent="true" 
  android:orientation="vertical" > 
 
  <TextView 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_gravity="center_horizontal" 
  android:text="This is menu" 
  android:textColor="#000000" 
  android:textSize="28sp" /> 
 
  <Button 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_gravity="center_horizontal" 
  android:text="Test Button" /> 
 </LinearLayout> 
 </RelativeLayout> 
 
 <LinearLayout 
 android:id="@+id/content" 
 android:layout_width="320dip" 
 android:layout_height="fill_parent" 
 android:layout_alignParentRight="true" 
 android:background="#e9e9e9" 
 android:orientation="vertical" 
 android:visibility="visible" > 
 
 <Button 
  android:id="@+id/menuButton" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:text="Menu" /> 
 
 <ListView 
  android:id="@+id/contentList" 
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" 
  android:cacheColorHint="#00000000" > 
 </ListView> 
 </LinearLayout> 
 
 <com.example.slidinglayout3d.Image3dView 
 android:id="@+id/image_3d_view" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_alignParentLeft="true" 
 android:visibility="invisible" /> 
 
</com.example.slidinglayout3d.ThreeDSlidingLayout> 

可以看到,在最外層的ThreeDSlidingLayout布局里面,我們放入了三個(gè)直接子布局,第一個(gè)RelativeLayout也就是左側(cè)布局了,里面簡(jiǎn)單地放了一個(gè)TextView和一個(gè)按鈕。第二個(gè)LinearLayout是右側(cè)布局,里面放入了一個(gè)按鈕和一個(gè)ListView,都是用于顯示左側(cè)布局而準(zhǔn)備的。第三個(gè)是Image3dView,當(dāng)然是用于在滑動(dòng)過(guò)程中顯示左側(cè)布局的鏡像圖片了。

最后,打開(kāi)或新建MainActivity作為程序的主Activity,在里面加入如下代碼:

public class MainActivity extends Activity { 
 
 /** 
 * 側(cè)滑布局對(duì)象,用于通過(guò)手指滑動(dòng)將左側(cè)的菜單布局進(jìn)行顯示或隱藏。 
 */ 
 private ThreeDSlidingLayout slidingLayout; 
 
 /** 
 * menu按鈕,點(diǎn)擊按鈕展示左側(cè)布局,再點(diǎn)擊一次隱藏左側(cè)布局。 
 */ 
 private Button menuButton; 
 
 /** 
 * 放在content布局中的ListView。 
 */ 
 private ListView contentListView; 
 
 /** 
 * 作用于contentListView的適配器。 
 */ 
 private ArrayAdapter<String> contentListAdapter; 
 
 /** 
 * 用于填充contentListAdapter的數(shù)據(jù)源。 
 */ 
 private String[] contentItems = { "Content Item 1", "Content Item 2", "Content Item 3", 
  "Content Item 4", "Content Item 5", "Content Item 6", "Content Item 7", 
  "Content Item 8", "Content Item 9", "Content Item 10", "Content Item 11", 
  "Content Item 12", "Content Item 13", "Content Item 14", "Content Item 15", 
  "Content Item 16" }; 
 
 @Override 
 protected void onCreate(Bundle savedInstanceState) { 
 super.onCreate(savedInstanceState); 
 setContentView(R.layout.activity_main); 
 slidingLayout = (ThreeDSlidingLayout) findViewById(R.id.slidingLayout); 
 menuButton = (Button) findViewById(R.id.menuButton); 
 contentListView = (ListView) findViewById(R.id.contentList); 
 contentListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, 
  contentItems); 
 contentListView.setAdapter(contentListAdapter); 
 // 將監(jiān)聽(tīng)滑動(dòng)事件綁定在contentListView上 
 slidingLayout.setScrollEvent(contentListView); 
 menuButton.setOnClickListener(new OnClickListener() { 
  @Override 
  public void onClick(View v) { 
  if (slidingLayout.isLeftLayoutVisible()) { 
   slidingLayout.scrollToRightLayout(); 
  } else { 
   slidingLayout.scrollToLeftLayout(); 
  } 
  } 
 }); 
 contentListView.setOnItemClickListener(new OnItemClickListener() { 
  @Override 
  public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 
  String text = contentItems[position]; 
  Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show(); 
  } 
 }); 
 } 
} 

這些代碼應(yīng)該都非常簡(jiǎn)單和眼熟了吧,和以前滑動(dòng)菜單中的代碼完全一樣,調(diào)用ThreeDSlidingLayout的setScrollEvent方法,將ListView作為綁定布局傳入,這樣就可以通過(guò)拖動(dòng)ListView來(lái)顯示或隱藏左側(cè)布局。并且在按鈕的點(diǎn)擊事件里也加入了顯示和隱藏左側(cè)布局的邏輯。

好了,這樣所有的編碼工作就已經(jīng)完成了,讓我們來(lái)運(yùn)行一下吧,效果如下圖所示:

怎么樣?效果非常炫麗吧!其實(shí)只要對(duì)Camera進(jìn)行巧妙地運(yùn)用,還可以編寫(xiě)出很多非常精彩的特效,就看你敢不敢去發(fā)揮你的想象力了。

好了,今天的講解到此結(jié)束,有疑問(wèn)的朋友請(qǐng)?jiān)谙旅媪粞浴?/p>

源碼下載,請(qǐng)點(diǎn)擊這里

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

相關(guān)文章

最新評(píng)論