日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Android橫向滾動(dòng)屏幕特效分析

今天教大家寫一個(gè)類似于Android桌面的launcher效果的自定義控件,在開始寫之前大家需要熟悉幾個(gè)類和它們的方法,下面我分別列出來(lái):

一.VelocityTracker 速度追蹤器

顧名思義這個(gè)類的作用主要是追蹤用戶手指在屏幕上的滑動(dòng)速度。當(dāng)你要跟蹤一個(gè)touch事件的時(shí)候,使用obtain()方法得到這個(gè)類的實(shí) 例,然后 用addMovement(MotionEvent)函數(shù)將你接受到的motion event加入到VelocityTracker類實(shí)例中。當(dāng)你使用到速率時(shí),使用computeCurrentVelocity(int)初始化速率的 單位,并獲得當(dāng)前的事件的速率,然后使用getXVelocity() 或getXVelocity()獲得橫向和豎向的速率。

二.ViewConfiguration

這個(gè)類里面定義了android的許多標(biāo)準(zhǔn)的常量(UI的超時(shí)、大小和距離等)。

三.GestureDetector 手勢(shì)識(shí)別器

這個(gè)類主要是追蹤用戶手指在屏幕上的滑動(dòng)方向,這個(gè)類在我們馬上要實(shí)現(xiàn)的類中沒(méi)有使用,但是使用的原理和它差不多,所以順便提一下,而且在以后的開發(fā)中,這個(gè)類也是經(jīng)常使用的。

四.Scroller

這個(gè)類主要是支持view控件滑動(dòng),其實(shí)android很多可滑動(dòng)的控件里面默認(rèn)隱藏的就是這個(gè)類。而且這個(gè)類沒(méi)有進(jìn)行實(shí)際的視圖移動(dòng),當(dāng)調(diào)用它的 startScroll()方法實(shí)際上只是為了在父類調(diào)用computeScroll()方法前開始動(dòng)畫,也就是說(shuō)這個(gè)類實(shí)際上就是相當(dāng)于一個(gè)代理,值是 為了給后面視圖移動(dòng)添加一些動(dòng)畫效果。所以單獨(dú)調(diào)用startScroll()而不重寫computeScroll()方法是不會(huì)看到任何效果的。這兩者 必須配合使用,才能有移動(dòng)的時(shí)候的動(dòng)畫效果。

其中Scroller.computeScrollOffset()方法是判斷scroller的移動(dòng)動(dòng)畫是否完成,當(dāng)你調(diào)用startScroll()方法的時(shí)候這個(gè)方法返回的值一直都為true,如果采用其它方式移動(dòng)視圖比如:scrollTo()或 scrollBy時(shí)那么這個(gè)方法返回false。

現(xiàn)在來(lái)講講startScroll(int startX, int startY, int dx, int dy, int duration)方法的四個(gè)參數(shù)的意思:

  • startX表示當(dāng)前視圖的x坐標(biāo)值
  • startY表示當(dāng)前視圖的y坐標(biāo)值
  • dx表示在當(dāng)前視圖的x坐標(biāo)基礎(chǔ)上橫向移動(dòng)的距離
  • dy表示在當(dāng)前視圖的y坐標(biāo)基礎(chǔ)上縱向移動(dòng)的距離
  • duration表示視圖移動(dòng)的操作在多少時(shí)間內(nèi)執(zhí)行完場(chǎng),也就是動(dòng)畫的持續(xù)時(shí)間(單位:毫秒)

五.ViewGroup

這是個(gè)特殊的View,它繼承于Android.view.View,它的功能就是裝載和管理下一層的View對(duì)象或ViewGroup對(duì)象,也就說(shuō)他是一個(gè)容納其它元素的的容器。

下面我們來(lái)分別分析我們要使用這5個(gè)類的那些方法,首先我們來(lái)看ViewGroup類,因?yàn)槲覀冏远x的控件就是繼承至這個(gè)類,我們會(huì)重寫這個(gè)類中的5個(gè)方法如下:

1.onLayout(boolean changed, int l, int t, int r, int b)

這個(gè)方法是在onMeasure()方法執(zhí)行后調(diào)用,作用是父類為子類在屏幕上分配實(shí)際的寬度和高度。里面的四個(gè)參數(shù)分別表示,布局是否發(fā)生改變,布局左 上右下的邊距。

2.onMeasure(int widthMeasureSpec, int heightMeasureSpec)

這個(gè)方法在控件的父元素正要放置它的子控件時(shí)調(diào)用。然后傳入兩個(gè)參數(shù)——widthMeasureSpec和 heightMeasureSpec。它們指明控件可獲得的空間以及關(guān)于這個(gè)空間描述的元數(shù)據(jù)。比返回一個(gè)結(jié)果要好的方法是你傳遞View的高度和寬度到 setMeasuredDimension方法里。widthMeasureSpec和heightMeasureSpec參數(shù)在它們使用之前,首先要做 的是使用MeasureSpec類的靜態(tài)方法getMode和getSize來(lái)譯解。一個(gè)MeasureSpec包含一個(gè)尺寸和模式。

有三種可能的模式:

  • UNSPECIFIED:父布局沒(méi)有給子布局任何限制,子布局可以任意大小。
  • EXACTLY:父布局決定子布局的確切大小。不論子布局多大,它都必須限制在這個(gè)界限里。(當(dāng)布局定義為一個(gè)固定像素或者fill_parent時(shí)就是EXACTLY模式)
  • AT_MOST:子布局可以根據(jù)自己的大小選擇任意大小。(當(dāng)布局定義為wrap_content時(shí)就是AT_MOST模式)

3.computeScroll()

這個(gè)方法主要是父類要求它的子類滾動(dòng)的時(shí)候調(diào)用。在這個(gè)方法里,我們可以實(shí)現(xiàn) view的滾動(dòng)操作,這里滾動(dòng)并不是view的滾動(dòng)而是布局的滾動(dòng)。當(dāng)調(diào)用scroller的startScroll()方法后父類就會(huì)調(diào)用這個(gè)方法實(shí)現(xiàn) 滾動(dòng)視圖滾動(dòng)操作。

4.onTouchEvent(MotionEvent event)

處理傳遞到view 的手勢(shì)事件。手勢(shì)事件類型包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL等事件。Layout里 的onTouch默認(rèn)返回值是false, View里的onTouch默認(rèn)返回值是true,當(dāng)我們手指點(diǎn)擊屏幕時(shí)候,先調(diào)用ACTION_DOWN事件,當(dāng)onTouch里返回值是true的時(shí) 候,onTouch回繼續(xù)調(diào)用ACTION_UP事件,如果onTouch里返回值是false,那么onTouch只會(huì)調(diào)用ACTION_DOWN而不調(diào)用ACTION_UP.

5.onInterceptTouchEvent(MotionEvent ev)

用于攔截手勢(shì)事件的,每個(gè)手勢(shì)事件都會(huì)先調(diào)用這個(gè)方法。Layout里的onInterceptTouchEvent默認(rèn)返回值是false,這樣touch事件會(huì)傳遞到View控件。

下面再將幾個(gè)大家可能比較混亂的方法說(shuō)明一下:

Invalidate()和PostInvalidate(),這兩個(gè)方法作用都一樣,就是呼叫ui線程重新繪制 界面也就是刷新界面。那為什么要兩個(gè)方法呢,這是因?yàn)閍ndroid是多線程應(yīng)用,大家應(yīng)該都知道在非UI線程中是不能直接操作界面控件的,所以第2個(gè)方 法就幫助大家在子線程中刷行界面,***個(gè)方法則是在UI線程中刷新界面。

getX()和getRawX()這兩個(gè)方法的左右都是獲取當(dāng)前點(diǎn)在屏幕上的坐標(biāo),getX()是獲取當(dāng)前點(diǎn)相對(duì)于當(dāng)前視圖左上角的坐標(biāo),getRawX()則是獲取當(dāng)前點(diǎn)相對(duì)于手機(jī)屏幕左上角的坐標(biāo)。

上面已經(jīng)把我們要用到的類和方法做了詳細(xì)描述,下面就是實(shí)現(xiàn)的源碼:

 
 
  1. import android.content.Context;
  2. import android.util.AttributeSet;
  3. import android.view.MotionEvent;
  4. import android.view.VelocityTracker;
  5. import android.view.View;
  6. import android.view.ViewConfiguration;
  7. import android.view.ViewGroup;
  8. import android.widget.Scroller;
  9. /**
  10. * @author
  11. */
  12. public class ScrollLayout extends ViewGroup {
  13. private Scroller mScroller;
  14. private VelocityTracker mVelocityTracker;
  15. /**
  16. * 當(dāng)前的屏幕位置
  17. */
  18. private int mCurScreen;
  19. /**
  20. * 設(shè)置默認(rèn)屏幕的屬性,0表示***個(gè)屏幕
  21. */
  22. private int mDefaultScreen = 0;
  23. /**
  24. * 標(biāo)識(shí)滾動(dòng)操作已結(jié)束
  25. */
  26. private static final int TOUCH_STATE_REST = 0;
  27. /**
  28. * 標(biāo)識(shí)正在執(zhí)行滑動(dòng)操作
  29. */
  30. private static final int TOUCH_STATE_SCROLLING = 1;
  31. /**
  32. * 標(biāo)識(shí)滑動(dòng)速率
  33. */
  34. private static final int SNAP_VELOCITY = 600;
  35. /**
  36. * 當(dāng)前滑動(dòng)狀態(tài)
  37. */
  38. private int mTouchState = TOUCH_STATE_REST;
  39. /**
  40. * 在用戶觸發(fā)ontouch事件之前,我們認(rèn)為用戶能夠使view滑動(dòng)的距離(像素)
  41. */
  42. private int mTouchSlop;
  43. /**
  44. * 手指觸碰屏幕的***一次x坐標(biāo)
  45. */
  46. private float mLastMotionX;
  47. /**
  48. * 手指觸碰屏幕的***一次y坐標(biāo)
  49. */
  50. @SuppressWarnings("unused")
  51. private float mLastMotionY;
  52. public ScrollLayout(Context context) {
  53. super(context);
  54. mScroller = new Scroller(context);
  55. mCurScreen = mDefaultScreen;
  56. mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
  57. }
  58. public ScrollLayout(Context context, AttributeSet attrs) {
  59. super(context, attrs);
  60. mScroller = new Scroller(context);
  61. mCurScreen = mDefaultScreen;
  62. mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
  63. }
  64. public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
  65. super(context, attrs, defStyle);
  66. mScroller = new Scroller(context);
  67. mCurScreen = mDefaultScreen;
  68. mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
  69. }
  70. @Override
  71. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  72. if (changed) {
  73. int childLeft = 0;
  74. final int childCount = getChildCount();
  75. for (int i = 0; i < childCount; i++) {
  76. final View childView = getChildAt(i);
  77. if (childView.getVisibility() != View.GONE) {
  78. final int childWidth = childView.getMeasuredWidth();
  79. childView.layout(childLeft, 0, childLeft + childWidth,
  80. childView.getMeasuredHeight());
  81. childLeft += childWidth;
  82. }
  83. }
  84. }
  85. }
  86. @Override
  87. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  88. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  89. final int width = MeasureSpec.getSize(widthMeasureSpec);
  90. final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  91. if (widthMode != MeasureSpec.EXACTLY) {
  92. throw new IllegalStateException(
  93. "ScrollLayout only canmCurScreen run at EXACTLY mode!");
  94. }
  95. final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  96. if (heightMode != MeasureSpec.EXACTLY) {
  97. throw new IllegalStateException(
  98. "ScrollLayout only can run at EXACTLY mode!");
  99. }
  100. final int count = getChildCount();
  101. for (int i = 0; i < count; i++) {
  102. getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
  103. }
  104. // 初始化視圖的位置
  105. scrollTo(mCurScreen * width, 0);
  106. }
  107. /**
  108. * 根據(jù)滑動(dòng)的距離判斷移動(dòng)到第幾個(gè)視圖
  109. */
  110. public void snapToDestination() {
  111. final int screenWidth = getWidth();
  112. final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
  113. snapToScreen(destScreen);
  114. }
  115. /**
  116. * 滾動(dòng)到制定的視圖
  117. *
  118. * @param whichScreen
  119. * 視圖下標(biāo)
  120. */
  121. public void snapToScreen(int whichScreen) {
  122. whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
  123. if (getScrollX() != (whichScreen * getWidth())) {
  124. final int delta = whichScreen * getWidth() - getScrollX();
  125. mScroller.startScroll(getScrollX(), 0, delta, 0, 1000);
  126. mCurScreen = whichScreen;
  127. invalidate();
  128. }
  129. }
  130. public void setToScreen(int whichScreen) {
  131. whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
  132. mCurScreen = whichScreen;
  133. scrollTo(whichScreen * getWidth(), 0);
  134. }
  135. public int getCurScreen() {
  136. return mCurScreen;
  137. }
  138. @Override
  139. public void computeScroll() {
  140. if (mScroller.computeScrollOffset()) {
  141. scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  142. postInvalidate();
  143. }
  144. }
  145. @Override
  146. public boolean onTouchEvent(MotionEvent event) {
  147. if (mVelocityTracker == null) {
  148. mVelocityTracker = VelocityTracker.obtain();
  149. }
  150. mVelocityTracker.addMovement(event);
  151. final int action = event.getAction();
  152. final float x = event.getX();
  153. switch (action) {
  154. case MotionEvent.ACTION_DOWN:
  155. if (!mScroller.isFinished()) {
  156. mScroller.abortAnimation();
  157. }
  158. mLastMotionX = x;
  159. break;
  160. case MotionEvent.ACTION_MOVE:
  161. int deltaX = (int) (mLastMotionX - x);
  162. mLastMotionX = x;
  163. scrollBy(deltaX, 0);
  164. break;
  165. case MotionEvent.ACTION_UP:
  166. final VelocityTracker velocityTracker = mVelocityTracker;
  167. velocityTracker.computeCurrentVelocity(1000);
  168. int velocityX = (int) velocityTracker.getXVelocity();
  169. if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
  170. // 向左移動(dòng)
  171. snapToScreen(mCurScreen - 1);
  172. } else if (velocityX < -SNAP_VELOCITY
  173. && mCurScreen < getChildCount() - 1) {
  174. // 向右移動(dòng)
  175. snapToScreen(mCurScreen + 1);
  176. } else {
  177. snapToDestination();
  178. }
  179. if (mVelocityTracker != null) {
  180. mVelocityTracker.recycle();
  181. mVelocityTracker = null;
  182. }
  183. mTouchState = TOUCH_STATE_REST;
  184. break;
  185. case MotionEvent.ACTION_CANCEL:
  186. mTouchState = TOUCH_STATE_REST;
  187. break;
  188. }
  189. return true;
  190. }
  191. @Override
  192. public boolean onInterceptTouchEvent(MotionEvent ev) {
  193. final int action = ev.getAction();
  194. if ((action == MotionEvent.ACTION_MOVE)
  195. && (mTouchState != TOUCH_STATE_REST)) {
  196. return true;
  197. }
  198. final float x = ev.getX();
  199. final float y = ev.getY();
  200. switch (action) {
  201. case MotionEvent.ACTION_MOVE:
  202. final int xDiff = (int) Math.abs(mLastMotionX - x);
  203. if (xDiff > mTouchSlop) {
  204. mTouchState = TOUCH_STATE_SCROLLING;
  205. }
  206. break;
  207. case MotionEvent.ACTION_DOWN:
  208. mLastMotionX = x;
  209. mLastMotionY = y;
  210. mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
  211. : TOUCH_STATE_SCROLLING;
  212. break;
  213. case MotionEvent.ACTION_CANCEL:
  214. case MotionEvent.ACTION_UP:
  215. mTouchState = TOUCH_STATE_REST;
  216. break;
  217. }
  218. return mTouchState != TOUCH_STATE_REST;
  219. }
  220. }

網(wǎng)頁(yè)題目:Android橫向滾動(dòng)屏幕特效分析
URL分享:http://www.5511xx.com/article/dhjppis.html