歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android開發教程:左右滑屏的實現

Android開發教程:左右滑屏的實現

日期:2017/3/1 10:38:37   编辑:Linux編程

先上效果圖:



實現“左右滑屏”核心類是Scroller,將View中的內容左右滾動從而實現滑屏效果。關鍵方法有:
scroller.scrollTo(x,y):
直接將View中的內容滾動到指定的(x,y)位置。
scroller.scrollTo(dx,dy):
直接將View中的內容滾動到相對當前狀態的(dx,dy)位置。本例中用於實現手指拖拉移動View的效果。
scroller.startScroll(nowX, nowY, moveX, moveY, duration):
在duration的時間內完成move的位移。配合重寫View.computeScroll()不斷刷新界面從而實現滑屏動畫。

如果當前點擊拖拉的組件是按鈕等自身可處理手勢動作的組件,則重寫ViewGroup.onInterceptTouchEvent(MotionEvent)可攔截此事件並將此事件傳遞至onTouchEvent(MotionEvent)進行處理。從而對如按鈕等即可點擊亦可拖拉。


左右滑屏的指示器位置為SlidingIndicator。在fadeOut()方法中為本組件的動畫設置了延時,體驗上更親近:

[java]
  1. animFadeout.setStartTime(AnimationUtils.currentAnimationTimeMillis() + fadeDelay);
  2. setAnimation(animFadeout);

代碼如下(Java奉上,XML代碼請各位看官自己實現):


ActSlidingContainer.java

[java]
  1. package lab.sodino.sliding;
  2. import lab.sodino.sliding.SlidingContainer.OnSlidingListener;
  3. import Android.app.Activity;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.widget.Button;
  8. public class ActSlidingContainer extends Activity implements OnClickListener, OnSlidingListener {
  9. private SlidingContainer slidingContainer;
  10. private SlidingIndicator slidingIndicator;
  11. private Button btnLeft, btnRight, btnMid;
  12. @Override
  13. public void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.main);
  16. btnLeft = (Button) findViewById(R.id.left);
  17. btnLeft.setOnClickListener(this);
  18. btnRight = (Button) findViewById(R.id.right);
  19. btnRight.setOnClickListener(this);
  20. btnMid = (Button) findViewById(R.id.mid);
  21. btnMid.setOnClickListener(this);
  22. slidingContainer = (SlidingContainer) findViewById(R.id.slidingContainer);
  23. slidingContainer.setOnSlidingListener(this);
  24. slidingIndicator = (SlidingIndicator) findViewById(R.id.slidingIndicator);
  25. slidingIndicator.setPageAmount(slidingContainer.getChildCount());
  26. }
  27. @Override
  28. public void onClick(View v) {
  29. if (v == btnLeft) {
  30. slidingContainer.scroll2page(slidingContainer.getCurrentPage() - 1);
  31. } else if (v == btnRight) {
  32. slidingContainer.scroll2page(slidingContainer.getCurrentPage() + 1);
  33. } else if (v == btnMid) {
  34. slidingContainer.scroll2page(slidingContainer.getChildCount() >> 1);
  35. }
  36. }
  37. @Override
  38. public void onSliding(int scrollX) {
  39. float scale = (float) (slidingContainer.getPageWidth() * slidingContainer.getChildCount())
  40. / (float) slidingIndicator.getWidth();
  41. slidingIndicator.setPosition((int) (scrollX / scale));
  42. }
  43. @Override
  44. public void onSlidingEnd(int pageIdx, int scrollX) {
  45. slidingIndicator.setCurrentPage(pageIdx);
  46. }
  47. }

[java]
  1. <pre name="code" class="java" style="background-color: rgb(255, 255, 255); "><pre>

SlidingContainer.java

[java]
  1. package lab.sodino.sliding;
  2. import java.util.ArrayList;
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.graphics.Canvas;
  6. import android.graphics.Rect;
  7. import android.util.AttributeSet;
  8. import android.view.MotionEvent;
  9. import android.view.View;
  10. import android.view.ViewConfiguration;
  11. import android.view.ViewGroup;
  12. import android.widget.Scroller;
  13. /**
  14. * @author Sodino E-mail:[email protected]
  15. * @version Time:2012-1-18 下午02:55:59
  16. */
  17. public class SlidingContainer extends ViewGroup {
  18. private static final int INVALID_SCREEN = -1;
  19. public static final int SCROLL_DURATION = 500;
  20. public static final int SPEC_UNDEFINED = ViewGroup.LayoutParams.FILL_PARENT;
  21. public static final int SNAP_VELOCITY = 500;
  22. private static final int STATE_STATIC = 0;
  23. private static final int STATE_SCROLLING = 1;
  24. private int pageWidth;
  25. /**
  26. * 標識是否是第一次布局。<br/>
  27. * 第一次布局需要將第一頁調居中顯示在屏幕上。<br/>
  28. */
  29. private boolean isFirstLayout;
  30. private int currentPage, nextPage;
  31. private Scroller scroller;
  32. /** 手指滑動過程中可理解為拖動的最小長度。 */
  33. private int distanceSlop;
  34. private int state = STATE_STATIC;
  35. private float lastMotionX;
  36. private OnSlidingListener slidingListener;
  37. public SlidingContainer(Context context, AttributeSet attrs, int defStyle) {
  38. super(context, attrs, defStyle);
  39. LogOut.out(this, "SlidingContainer() 3");
  40. initialization(context, attrs);
  41. }
  42. public SlidingContainer(Context context, AttributeSet attrs) {
  43. super(context, attrs);
  44. LogOut.out(this, "SlidingContainer() 2");
  45. initialization(context, attrs);
  46. }
  47. public SlidingContainer(Context context) {
  48. super(context);
  49. LogOut.out(this, "SlidingContainer() 1");
  50. initialization(context, null);
  51. }
  52. private void initialization(Context context, AttributeSet attrs) {
  53. if (attrs != null) {
  54. TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.sliding_SlidingContainer);
  55. pageWidth = typedArr.getDimensionPixelSize(R.styleable.sliding_SlidingContainer_pageWidth, SPEC_UNDEFINED);
  56. typedArr.recycle();
  57. }
  58. state = STATE_STATIC;
  59. isFirstLayout = true;
  60. currentPage = 0;
  61. nextPage = INVALID_SCREEN;
  62. scroller = new Scroller(context);
  63. final ViewConfiguration configuration = ViewConfiguration.get(context);
  64. distanceSlop = configuration.getScaledTouchSlop();
  65. }
  66. public int getCurrentPage() {
  67. return currentPage;
  68. }
  69. public int getScrollXByPage(int page) {
  70. return (page * pageWidth) - getPagePadding();
  71. }
  72. public int getPagePadding() {
  73. return (getMeasuredWidth() - pageWidth) >> 1;
  74. }
  75. public int getPageWidth() {
  76. return pageWidth;
  77. }
  78. public boolean scroll2page(int page) {
  79. if (page < 0) {
  80. return false;
  81. } else if (page >= getChildCount()) {
  82. return false;
  83. } else if (scroller.isFinished() == false) {
  84. return false;
  85. }
  86. enableChildrenCache(true);
  87. boolean changingPage = (page != currentPage);
  88. nextPage = page;
  89. View focusedChild = getFocusedChild();
  90. if (changingPage && focusedChild != null && focusedChild == getChildAt(currentPage)) {
  91. focusedChild.clearFocus();
  92. }
  93. final int nowX = getScrollX();
  94. final int newX = getScrollXByPage(nextPage);
  95. final int move = newX - nowX;
  96. final int absMove = Math.abs(move);
  97. int duration = SCROLL_DURATION;
  98. if (absMove < pageWidth) {
  99. duration = SCROLL_DURATION * absMove / pageWidth;
  100. }
  101. // 啟動左右切屏動畫
  102. scroller.startScroll(nowX, 0, move, 0, duration);
  103. invalidate();
  104. return true;
  105. }
  106. private void checkScrolling(float x) {
  107. float diff = Math.abs(x - lastMotionX);
  108. if (diff > distanceSlop) {
  109. state = STATE_SCROLLING;
  110. enableChildrenCache(true);
  111. }
  112. }
  113. /**
  114. * 開始滑動時設置允許使用緩存。<br/>
  115. * 結束滑動時設置取消緩存。<br/>
  116. */
  117. public void enableChildrenCache(boolean enable) {
  118. setChildrenDrawingCacheEnabled(enable);
  119. setChildrenDrawnWithCacheEnabled(enable);
  120. }
  121. /** 在正式顯示之前設置才有效。 */
  122. public boolean setPageWidth(int width) {
  123. if (isFirstLayout) {
  124. pageWidth = width;
  125. return true;
  126. }
  127. return false;
  128. }
  129. public void setOnSlidingListener(OnSlidingListener listener) {
  130. slidingListener = listener;
  131. }
  132. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  133. LogOut.out(this, "onMeasure()");
  134. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  135. pageWidth = (pageWidth == SPEC_UNDEFINED) ? getMeasuredWidth() : pageWidth;
  136. pageWidth = Math.min(Math.max(0, pageWidth), getMeasuredWidth());
  137. final int count = getChildCount();
  138. for (int i = 0; i < count; i++) {
  139. int childWidthSpec = MeasureSpec.makeMeasureSpec(pageWidth, MeasureSpec.EXACTLY);
  140. View view = getChildAt(i);
  141. view.measure(childWidthSpec, heightMeasureSpec);
  142. }
  143. }
  144. @Override
  145. protected void onLayout(boolean changing, int left, int top, int right, int bottom) {
  146. LogOut.out(this, "onLayout");
  147. int childLeft = 0;
  148. final int count = getChildCount();
  149. for (int i = 0; i < count; i++) {
  150. final View view = getChildAt(i);
  151. if (view.getVisibility() != View.GONE) {
  152. int childWidth = view.getMeasuredWidth();
  153. view.layout(childLeft, 0, childLeft + childWidth, view.getMeasuredHeight());
  154. childLeft += childWidth;
  155. }
  156. }
  157. if (isFirstLayout) {
  158. scrollTo(getScrollXByPage(currentPage), 0);
  159. isFirstLayout = false;
  160. }
  161. }
  162. public boolean onInterceptTouchEvent(MotionEvent event) {
  163. LogOut.out(this, "onInterceptTouchEvent action=" + event.getAction());
  164. final int action = event.getAction();
  165. if (action == MotionEvent.ACTION_MOVE && state != STATE_STATIC) {
  166. // MOVE及非靜止情況下,返回TRUE阻止將此事件傳遞給子組件,
  167. // 而是執行onTouchEvent()來實現滑動
  168. return true;
  169. }
  170. final float x = event.getX();
  171. switch (action) {
  172. case MotionEvent.ACTION_DOWN:
  173. lastMotionX = x;
  174. // 點擊按鈕時,此處設置狀態為靜止。
  175. state = scroller.isFinished() ? STATE_STATIC : STATE_SCROLLING;
  176. break;
  177. case MotionEvent.ACTION_MOVE:
  178. if (state == STATE_STATIC) {
  179. // 由於已靜止,在點擊按鈕後進行拖拉,則根據拖拉位移大小決定是否需要改變狀態進而進一步攔截此事件。
  180. checkScrolling(x);
  181. }
  182. break;
  183. case MotionEvent.ACTION_UP:
  184. case MotionEvent.ACTION_CANCEL:
  185. enableChildrenCache(false);
  186. state = STATE_STATIC;
  187. break;
  188. }
  189. // 非靜止狀態,將此事件交由onTouchEvent()處理。
  190. return state != STATE_STATIC;
  191. }
  192. public boolean onTouchEvent(MotionEvent event) {
  193. LogOut.out(this, "onTouchEvent");
  194. super.onTouchEvent(event);
  195. final int action = event.getAction();
  196. final float x = event.getX();
  197. switch (action) {
  198. case MotionEvent.ACTION_DOWN:
  199. lastMotionX = x;
  200. if (scroller.isFinished() == false) {
  201. scroller.abortAnimation();
  202. }
  203. break;
  204. case MotionEvent.ACTION_MOVE:
  205. if (state == STATE_STATIC) {
  206. checkScrolling(x);
  207. } else if (state == STATE_SCROLLING) {
  208. int moveX = (int) (lastMotionX - x);
  209. lastMotionX = x;
  210. if (getScrollX() < 0 || getScrollX() > getChildAt(getChildCount() - 1).getLeft()) {
  211. // 對於越界的拖拉,則將位移減半。
  212. moveX = moveX >> 1;
  213. }
  214. scrollBy(moveX, 0);
  215. }
  216. break;
  217. case MotionEvent.ACTION_UP:
  218. case MotionEvent.ACTION_CANCEL:
  219. if (state == STATE_SCROLLING) {
  220. final int startX = getScrollXByPage(currentPage);
  221. // 默認選擇回到手指滑動之前的當前頁
  222. int whichPage = currentPage;
  223. int xSpace = getWidth() / 8;
  224. if (getScrollX() < startX - xSpace) {
  225. whichPage = Math.max(0, whichPage - 1);
  226. } else if (getScrollX() > startX + xSpace) {
  227. whichPage = Math.min(getChildCount() - 1, whichPage + 1);
  228. }
  229. scroll2page(whichPage);
  230. }
  231. state = STATE_STATIC;
  232. break;
  233. }
  234. return true;
  235. }
  236. /** 讓拖拉、動畫過程中界面過渡順滑。 */
  237. protected void dispatchDraw(Canvas canvas) {
  238. final long drawingTime = getDrawingTime();
  239. final int count = getChildCount();
  240. for (int i = 0; i < count; i++) {
  241. drawChild(canvas, getChildAt(i), drawingTime);
  242. }
  243. if (slidingListener != null) {
  244. int adjustedScrollX = getScrollX() + getPagePadding();
  245. slidingListener.onSliding(adjustedScrollX);
  246. if (adjustedScrollX % pageWidth == 0) {
  247. slidingListener.onSlidingEnd(adjustedScrollX / pageWidth, adjustedScrollX);
  248. }
  249. }
  250. }
  251. /** 與Scroller相匹配,實現動畫效果中每一幀的界面更新。 */
  252. public void computeScroll() {
  253. if (scroller.computeScrollOffset()) {
  254. scrollTo(scroller.getCurrX(), scroller.getCurrY());
  255. postInvalidate();
  256. } else if (nextPage != INVALID_SCREEN) {
  257. currentPage = nextPage;
  258. nextPage = INVALID_SCREEN;
  259. enableChildrenCache(false);
  260. }
  261. }
  262. public static interface OnSlidingListener {
  263. public void onSliding(int scrollX);
  264. public void onSlidingEnd(int pageIdx, int scrollX);
  265. }
  266. }
SlidingIndicator.java

[java]
  1. package lab.sodino.sliding;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Canvas;
  5. import android.graphics.Paint;
  6. import android.graphics.RectF;
  7. import android.util.AttributeSet;
  8. import android.view.View;
  9. import android.view.animation.AlphaAnimation;
  10. import android.view.animation.Animation;
  11. import android.view.animation.AnimationUtils;
  12. import android.view.animation.LinearInterpolator;
  13. /**
  14. * @author Sodino E-mail:[email protected]
  15. * @version Time:2012-1-18 下午03:31:08
  16. */
  17. public class SlidingIndicator extends View {
  18. public static final int BAR_COLOR = 0xaa777777;
  19. public static final int HIGHLIGHT_COLOR = 0xaa999999;
  20. public static final int FADE_DELAY = 2000;
  21. public static final int FADE_DURATION = 500;
  22. private int amount, currentPage, position;
  23. private Paint barPaint, highlightPaint;
  24. private int fadeDelay, fadeDuration;
  25. private float ovalRadius;
  26. private Animation animFadeout;
  27. /** RectF比Rect是精度上更精確。 */
  28. private RectF rectFBody, rectFIndicator;
  29. public SlidingIndicator(Context context, AttributeSet attrs, int defStyle) {
  30. super(context, attrs, defStyle);
  31. // 預設值。
  32. int barColor = BAR_COLOR, highlightColor = HIGHLIGHT_COLOR;
  33. fadeDelay = FADE_DELAY;
  34. fadeDuration = FADE_DURATION;
  35. if (attrs != null) {
  36. TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.sliding_SlidingIndicator);
  37. barColor = typedArr.getColor(R.styleable.sliding_SlidingIndicator_barColor, BAR_COLOR);
  38. highlightColor = typedArr.getColor(R.styleable.sliding_SlidingIndicator_highlightColor, HIGHLIGHT_COLOR);
  39. fadeDelay = typedArr.getInteger(R.styleable.sliding_SlidingIndicator_fadeDelay, FADE_DELAY);
  40. fadeDuration = typedArr.getInteger(R.styleable.sliding_SlidingIndicator_fadeDuration, FADE_DURATION);
  41. ovalRadius = typedArr.getDimension(R.styleable.sliding_SlidingIndicator_roundRectRadius, 0f);
  42. typedArr.recycle();
  43. }
  44. initialization(barColor, highlightColor, fadeDuration);
  45. }
  46. public SlidingIndicator(Context context, AttributeSet attrs) {
  47. this(context, attrs, 0);
  48. }
  49. public SlidingIndicator(Context context) {
  50. super(context);
  51. }
  52. private void initialization(int barColor, int highlightColor, int fadeDuration) {
  53. barPaint = new Paint();
  54. barPaint.setColor(barColor);
  55. highlightPaint = new Paint();
  56. highlightPaint.setColor(highlightColor);
  57. animFadeout = new AlphaAnimation(1f, 0f);
  58. animFadeout.setDuration(fadeDuration);
  59. animFadeout.setRepeatCount(0);
  60. animFadeout.setInterpolator(new LinearInterpolator());
  61. // 設置動畫結束後,本組件保持動畫結束時的最後狀態,即全透明不可見。
  62. animFadeout.setFillEnabled(true);
  63. animFadeout.setFillAfter(true);
  64. rectFBody = new RectF();
  65. rectFIndicator = new RectF();
  66. }
  67. public void setPageAmount(int num) {
  68. if (num < 0) {
  69. throw new IllegalArgumentException("num must be positive.");
  70. }
  71. amount = num;
  72. invalidate();
  73. fadeOut();
  74. }
  75. private void fadeOut() {
  76. if (fadeDuration > 0) {
  77. clearAnimation();
  78. // 設置動畫的延時時間,此時間段內正好指示當前頁位置。
  79. animFadeout.setStartTime(AnimationUtils.currentAnimationTimeMillis() + fadeDelay);
  80. setAnimation(animFadeout);
  81. }
  82. }
  83. public int getCurrentPage() {
  84. return currentPage;
  85. }
  86. public void setCurrentPage(int idx) {
  87. if (currentPage < 0 || currentPage >= amount) {
  88. throw new IllegalArgumentException("currentPage parameter out of bounds");
  89. }
  90. if (this.currentPage != idx) {
  91. this.currentPage = idx;
  92. this.position = currentPage * getPageWidth();
  93. invalidate();
  94. fadeOut();
  95. }
  96. }
  97. public void setPosition(int position) {
  98. if (this.position != position) {
  99. this.position = position;
  100. invalidate();
  101. fadeOut();
  102. }
  103. }
  104. public int getPageWidth() {
  105. return getWidth() / amount;
  106. }
  107. protected void onDraw(Canvas canvas) {
  108. rectFBody.set(0, 0, getWidth(), getHeight());
  109. canvas.drawRoundRect(rectFBody, ovalRadius, ovalRadius, barPaint);
  110. rectFIndicator.set(position, 0, position + getPageWidth(), getHeight());
  111. canvas.drawRoundRect(rectFIndicator, ovalRadius, ovalRadius, highlightPaint);
  112. }
  113. }
Copyright © Linux教程網 All Rights Reserved