本文由孙国威 原创。如需转载,请注明出处!

原文:http://blog.csdn.net/manoel/article/details/39013095

为了后续对这个项目进行优化,比如透明度动画、背景图的位移动画,以及性能上的优化。

我把这个项目上传到github上面,请大家随时关注。

github地址https://github.com/sunguowei

最近项目要做一个QQ5.0的侧滑菜单效果,和传统的侧滑菜单存在着一些差异。想必大家都已经见识过了。

为了不重复发明轮子,先去github上面搜索了一番。

发现了几个类似的,但是还是有一些不同。

下面是搜索到的类似的开源项目。

RESideMenu(ios项目)

https://github.com/romaonthego/RESideMenu

AndroidResideMenu

https://github.com/SpecialCyCi/AndroidResideMenu

ResideLayout

https://github.com/kyze8439690/ResideLayout

研究了一下这些开源项目的源代码。感觉并不是特别适用于我们自己的项目。所以,我自己又研究了一下。最后的效果如下。当然了,还有很多可以优化的地方,后续再慢慢优化。

备注:如果图片动画显示不出来,可以点击这个网址查看。

http://img.blog.csdn.net/20140902225149282?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFub2Vs/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

我是基于SlidingMenu库进行的二次修改,增加了一些转场动画。

大家对这个库应该比较熟悉,下面是SlidingMenu的github地址。非常感谢Jeremy Feinstein提供的这个库,让广大Android Developers省去了非常多的麻烦。

https://github.com/jfeinstein10/SlidingMenu

备注:SlidingMenu使用了SherlockActionBar这个库,配置起来会比较麻烦,在文章的最后我会把demo上传,供大家下载,减去了大家自己配置项目的麻烦。

我主要修改了2个类,SlidingMenu.java和CustonViewAbove.java,只是增加了一些功能,并没有修改原本的功能。

做了修改的地方,我做了中文注释,其实实现很简单,几行代码而已。推荐大家下载Demo,然后自己调试一下。Demo的下载地址在文章的末尾。

废话不多说,直接上代码,略微有点长。

  1. public class SlidingMenu extends RelativeLayout {
  2. private static final String TAG = SlidingMenu.class.getSimpleName();
  3. public static final int SLIDING_WINDOW = 0;
  4. public static final int SLIDING_CONTENT = 1;
  5. private boolean mActionbarOverlay = false;
  6. /**
  7. * Constant value for use with setTouchModeAbove(). Allows the SlidingMenu
  8. * to be opened with a swipe gesture on the screen's margin
  9. */
  10. public static final int TOUCHMODE_MARGIN = 0;
  11. /**
  12. * Constant value for use with setTouchModeAbove(). Allows the SlidingMenu
  13. * to be opened with a swipe gesture anywhere on the screen
  14. */
  15. public static final int TOUCHMODE_FULLSCREEN = 1;
  16. /**
  17. * Constant value for use with setTouchModeAbove(). Denies the SlidingMenu
  18. * to be opened with a swipe gesture
  19. */
  20. public static final int TOUCHMODE_NONE = 2;
  21. /**
  22. * Constant value for use with setMode(). Puts the menu to the left of the
  23. * content.
  24. */
  25. public static final int LEFT = 0;
  26. /**
  27. * Constant value for use with setMode(). Puts the menu to the right of the
  28. * content.
  29. */
  30. public static final int RIGHT = 1;
  31. /**
  32. * Constant value for use with setMode(). Puts menus to the left and right
  33. * of the content.
  34. */
  35. public static final int LEFT_RIGHT = 2;
  36. private CustomViewAbove mViewAbove;
  37. private CustomViewBehind mViewBehind;
  38. /** 整体的背景,用一个ImageView代替 */
  39. private ImageView mViewBackground;
  40. private OnOpenListener mOpenListener;
  41. private OnOpenListener mSecondaryOpenListner;
  42. private OnCloseListener mCloseListener;
  43. /**
  44. * The listener interface for receiving onOpen events. The class that is
  45. * interested in processing a onOpen event implements this interface, and
  46. * the object created with that class is registered with a component using
  47. * the component's <code>addOnOpenListener<code> method. When
  48. * the onOpen event occurs, that object's appropriate
  49. * method is invoked
  50. */
  51. public interface OnOpenListener {
  52. /**
  53. * On open.
  54. */
  55. public void onOpen();
  56. }
  57. /**
  58. * The listener interface for receiving onOpened events. The class that is
  59. * interested in processing a onOpened event implements this interface, and
  60. * the object created with that class is registered with a component using
  61. * the component's <code>addOnOpenedListener<code> method. When
  62. * the onOpened event occurs, that object's appropriate
  63. * method is invoked.
  64. *
  65. * @see OnOpenedEvent
  66. */
  67. public interface OnOpenedListener {
  68. /**
  69. * On opened.
  70. */
  71. public void onOpened();
  72. }
  73. /**
  74. * The listener interface for receiving onClose events. The class that is
  75. * interested in processing a onClose event implements this interface, and
  76. * the object created with that class is registered with a component using
  77. * the component's <code>addOnCloseListener<code> method. When
  78. * the onClose event occurs, that object's appropriate
  79. * method is invoked.
  80. *
  81. * @see OnCloseEvent
  82. */
  83. public interface OnCloseListener {
  84. /**
  85. * On close.
  86. */
  87. public void onClose();
  88. }
  89. /**
  90. * The listener interface for receiving onClosed events. The class that is
  91. * interested in processing a onClosed event implements this interface, and
  92. * the object created with that class is registered with a component using
  93. * the component's <code>addOnClosedListener<code> method. When
  94. * the onClosed event occurs, that object's appropriate
  95. * method is invoked.
  96. *
  97. * @see OnClosedEvent
  98. */
  99. public interface OnClosedListener {
  100. /**
  101. * On closed.
  102. */
  103. public void onClosed();
  104. }
  105. /**
  106. * The Interface CanvasTransformer.
  107. */
  108. public interface CanvasTransformer {
  109. /**
  110. * Transform canvas.
  111. *
  112. * @param canvas
  113. *            the canvas
  114. * @param percentOpen
  115. *            the percent open
  116. */
  117. public void transformCanvas(Canvas canvas, float percentOpen);
  118. }
  119. /**
  120. * Instantiates a new SlidingMenu.
  121. *
  122. * @param context
  123. *            the associated Context
  124. */
  125. public SlidingMenu(Context context) {
  126. this(context, null);
  127. }
  128. /**
  129. * Instantiates a new SlidingMenu and attach to Activity.
  130. *
  131. * @param activity
  132. *            the activity to attach slidingmenu
  133. * @param slideStyle
  134. *            the slidingmenu style
  135. */
  136. public SlidingMenu(Activity activity, int slideStyle) {
  137. this(activity, null);
  138. this.attachToActivity(activity, slideStyle);
  139. }
  140. /**
  141. * Instantiates a new SlidingMenu.
  142. *
  143. * @param context
  144. *            the associated Context
  145. * @param attrs
  146. *            the attrs
  147. */
  148. public SlidingMenu(Context context, AttributeSet attrs) {
  149. this(context, attrs, 0);
  150. }
  151. /**
  152. * Instantiates a new SlidingMenu.
  153. *
  154. * @param context
  155. *            the associated Context
  156. * @param attrs
  157. *            the attrs
  158. * @param defStyle
  159. *            the def style
  160. */
  161. public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
  162. super(context, attrs, defStyle);
  163. /** SlidingMenu是一个RelativeLayout,这里把背景图ImageView添加到RelativeLayout的最底层。*/
  164. LayoutParams backgroundParams = new LayoutParams(
  165. LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
  166. mViewBackground = new ImageView(context);
  167. mViewBackground.setScaleType(ImageView.ScaleType.CENTER_CROP);
  168. addView(mViewBackground, backgroundParams);
  169. LayoutParams behindParams = new LayoutParams(LayoutParams.MATCH_PARENT,
  170. LayoutParams.MATCH_PARENT);
  171. mViewBehind = new CustomViewBehind(context);
  172. addView(mViewBehind, behindParams);
  173. LayoutParams aboveParams = new LayoutParams(LayoutParams.MATCH_PARENT,
  174. LayoutParams.MATCH_PARENT);
  175. mViewAbove = new CustomViewAbove(context);
  176. addView(mViewAbove, aboveParams);
  177. // register the CustomViewBehind with the CustomViewAbove
  178. mViewAbove.setCustomViewBehind(mViewBehind);
  179. mViewBehind.setCustomViewAbove(mViewAbove);
  180. mViewAbove.setOnPageChangeListener(new OnPageChangeListener() {
  181. public static final int POSITION_OPEN = 0;
  182. public static final int POSITION_CLOSE = 1;
  183. public static final int POSITION_SECONDARY_OPEN = 2;
  184. public void onPageScrolled(int position, float positionOffset,
  185. int positionOffsetPixels) {
  186. }
  187. public void onPageSelected(int position) {
  188. if (position == POSITION_OPEN && mOpenListener != null) {
  189. mOpenListener.onOpen();
  190. } else if (position == POSITION_CLOSE && mCloseListener != null) {
  191. mCloseListener.onClose();
  192. } else if (position == POSITION_SECONDARY_OPEN
  193. && mSecondaryOpenListner != null) {
  194. mSecondaryOpenListner.onOpen();
  195. }
  196. }
  197. });
  198. // now style everything!
  199. TypedArray ta = context.obtainStyledAttributes(attrs,
  200. R.styleable.SlidingMenu);
  201. // set the above and behind views if defined in xml
  202. int mode = ta.getInt(R.styleable.SlidingMenu_mode, LEFT);
  203. setMode(mode);
  204. int viewAbove = ta.getResourceId(R.styleable.SlidingMenu_viewAbove, -1);
  205. if (viewAbove != -1) {
  206. setContent(viewAbove);
  207. } else {
  208. setContent(new FrameLayout(context));
  209. }
  210. int viewBehind = ta.getResourceId(R.styleable.SlidingMenu_viewBehind,
  211. -1);
  212. if (viewBehind != -1) {
  213. setMenu(viewBehind);
  214. } else {
  215. setMenu(new FrameLayout(context));
  216. }
  217. int touchModeAbove = ta.getInt(R.styleable.SlidingMenu_touchModeAbove,
  218. TOUCHMODE_MARGIN);
  219. setTouchModeAbove(touchModeAbove);
  220. int touchModeBehind = ta.getInt(
  221. R.styleable.SlidingMenu_touchModeBehind, TOUCHMODE_MARGIN);
  222. setTouchModeBehind(touchModeBehind);
  223. int offsetBehind = (int) ta.getDimension(
  224. R.styleable.SlidingMenu_behindOffset, -1);
  225. int widthBehind = (int) ta.getDimension(
  226. R.styleable.SlidingMenu_behindWidth, -1);
  227. if (offsetBehind != -1 && widthBehind != -1)
  228. throw new IllegalStateException(
  229. "Cannot set both behindOffset and behindWidth for a SlidingMenu");
  230. else if (offsetBehind != -1)
  231. setBehindOffset(offsetBehind);
  232. else if (widthBehind != -1)
  233. setBehindWidth(widthBehind);
  234. else
  235. setBehindOffset(0);
  236. float scrollOffsetBehind = ta.getFloat(
  237. R.styleable.SlidingMenu_behindScrollScale, 0.33f);
  238. setBehindScrollScale(scrollOffsetBehind);
  239. int shadowRes = ta.getResourceId(
  240. R.styleable.SlidingMenu_shadowDrawable, -1);
  241. if (shadowRes != -1) {
  242. setShadowDrawable(shadowRes);
  243. }
  244. int shadowWidth = (int) ta.getDimension(
  245. R.styleable.SlidingMenu_shadowWidth, 0);
  246. setShadowWidth(shadowWidth);
  247. boolean fadeEnabled = ta.getBoolean(
  248. R.styleable.SlidingMenu_fadeEnabled, true);
  249. setFadeEnabled(fadeEnabled);
  250. float fadeDeg = ta.getFloat(R.styleable.SlidingMenu_fadeDegree, 0.33f);
  251. setFadeDegree(fadeDeg);
  252. boolean selectorEnabled = ta.getBoolean(
  253. R.styleable.SlidingMenu_selectorEnabled, false);
  254. setSelectorEnabled(selectorEnabled);
  255. int selectorRes = ta.getResourceId(
  256. R.styleable.SlidingMenu_selectorDrawable, -1);
  257. if (selectorRes != -1)
  258. setSelectorDrawable(selectorRes);
  259. ta.recycle();
  260. }
  261. /**
  262. * Attaches the SlidingMenu to an entire Activity
  263. *
  264. * @param activity
  265. *            the Activity
  266. * @param slideStyle
  267. *            either SLIDING_CONTENT or SLIDING_WINDOW
  268. */
  269. public void attachToActivity(Activity activity, int slideStyle) {
  270. attachToActivity(activity, slideStyle, false);
  271. }
  272. /**
  273. * Attaches the SlidingMenu to an entire Activity
  274. *
  275. * @param activity
  276. *            the Activity
  277. * @param slideStyle
  278. *            either SLIDING_CONTENT or SLIDING_WINDOW
  279. * @param actionbarOverlay
  280. *            whether or not the ActionBar is overlaid
  281. */
  282. public void attachToActivity(Activity activity, int slideStyle,
  283. boolean actionbarOverlay) {
  284. if (slideStyle != SLIDING_WINDOW && slideStyle != SLIDING_CONTENT)
  285. throw new IllegalArgumentException(
  286. "slideStyle must be either SLIDING_WINDOW or SLIDING_CONTENT");
  287. if (getParent() != null)
  288. throw new IllegalStateException(
  289. "This SlidingMenu appears to already be attached");
  290. // get the window background
  291. TypedArray a = activity.getTheme().obtainStyledAttributes(
  292. new int[] { android.R.attr.windowBackground });
  293. int background = a.getResourceId(0, 0);
  294. a.recycle();
  295. switch (slideStyle) {
  296. case SLIDING_WINDOW:
  297. mActionbarOverlay = false;
  298. ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
  299. ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);
  300. // save ActionBar themes that have transparent assets
  301. decorChild.setBackgroundResource(background);
  302. decor.removeView(decorChild);
  303. decor.addView(this);
  304. setContent(decorChild);
  305. break;
  306. case SLIDING_CONTENT:
  307. mActionbarOverlay = actionbarOverlay;
  308. // take the above view out of
  309. ViewGroup contentParent = (ViewGroup) activity
  310. .findViewById(android.R.id.content);
  311. View content = contentParent.getChildAt(0);
  312. contentParent.removeView(content);
  313. contentParent.addView(this);
  314. setContent(content);
  315. // save people from having transparent backgrounds
  316. if (content.getBackground() == null)
  317. content.setBackgroundResource(background);
  318. break;
  319. }
  320. }
  321. /**
  322. * Set the above view content from a layout resource. The resource will be
  323. * inflated, adding all top-level views to the above view.
  324. *
  325. * @param res
  326. *            the new content
  327. */
  328. public void setContent(int res) {
  329. setContent(LayoutInflater.from(getContext()).inflate(res, null));
  330. }
  331. /**
  332. * Set the above view content to the given View.
  333. *
  334. * @param view
  335. *            The desired content to display.
  336. */
  337. public void setContent(View view) {
  338. mViewAbove.setContent(view);
  339. showContent();
  340. }
  341. /**
  342. * 设置背景图片
  343. *
  344. * @param resid
  345. */
  346. public void setBackgroundImage(int resid) {
  347. mViewBackground.setBackgroundResource(resid);
  348. }
  349. /**
  350. * Retrieves the current content.
  351. *
  352. * @return the current content
  353. */
  354. public View getContent() {
  355. return mViewAbove.getContent();
  356. }
  357. /**
  358. * Set the behind view (menu) content from a layout resource. The resource
  359. * will be inflated, adding all top-level views to the behind view.
  360. *
  361. * @param res
  362. *            the new content
  363. */
  364. public void setMenu(int res) {
  365. setMenu(LayoutInflater.from(getContext()).inflate(res, null));
  366. }
  367. /**
  368. * Set the behind view (menu) content to the given View.
  369. *
  370. * @param view
  371. *            The desired content to display.
  372. */
  373. public void setMenu(View v) {
  374. mViewBehind.setContent(v);
  375. }
  376. /**
  377. * Retrieves the main menu.
  378. *
  379. * @return the main menu
  380. */
  381. public View getMenu() {
  382. return mViewBehind.getContent();
  383. }
  384. /**
  385. * Set the secondary behind view (right menu) content from a layout
  386. * resource. The resource will be inflated, adding all top-level views to
  387. * the behind view.
  388. *
  389. * @param res
  390. *            the new content
  391. */
  392. public void setSecondaryMenu(int res) {
  393. setSecondaryMenu(LayoutInflater.from(getContext()).inflate(res, null));
  394. }
  395. /**
  396. * Set the secondary behind view (right menu) content to the given View.
  397. *
  398. * @param view
  399. *            The desired content to display.
  400. */
  401. public void setSecondaryMenu(View v) {
  402. mViewBehind.setSecondaryContent(v);
  403. // mViewBehind.invalidate();
  404. }
  405. /**
  406. * Retrieves the current secondary menu (right).
  407. *
  408. * @return the current menu
  409. */
  410. public View getSecondaryMenu() {
  411. return mViewBehind.getSecondaryContent();
  412. }
  413. /**
  414. * Sets the sliding enabled.
  415. *
  416. * @param b
  417. *            true to enable sliding, false to disable it.
  418. */
  419. public void setSlidingEnabled(boolean b) {
  420. mViewAbove.setSlidingEnabled(b);
  421. }
  422. /**
  423. * Checks if is sliding enabled.
  424. *
  425. * @return true, if is sliding enabled
  426. */
  427. public boolean isSlidingEnabled() {
  428. return mViewAbove.isSlidingEnabled();
  429. }
  430. /**
  431. * Sets which side the SlidingMenu should appear on.
  432. *
  433. * @param mode
  434. *            must be either SlidingMenu.LEFT or SlidingMenu.RIGHT
  435. */
  436. public void setMode(int mode) {
  437. if (mode != LEFT && mode != RIGHT && mode != LEFT_RIGHT) {
  438. throw new IllegalStateException(
  439. "SlidingMenu mode must be LEFT, RIGHT, or LEFT_RIGHT");
  440. }
  441. mViewBehind.setMode(mode);
  442. }
  443. /**
  444. * Returns the current side that the SlidingMenu is on.
  445. *
  446. * @return the current mode, either SlidingMenu.LEFT or SlidingMenu.RIGHT
  447. */
  448. public int getMode() {
  449. return mViewBehind.getMode();
  450. }
  451. /**
  452. * Sets whether or not the SlidingMenu is in static mode (i.e. nothing is
  453. * moving and everything is showing)
  454. *
  455. * @param b
  456. *            true to set static mode, false to disable static mode.
  457. */
  458. public void setStatic(boolean b) {
  459. if (b) {
  460. setSlidingEnabled(false);
  461. mViewAbove.setCustomViewBehind(null);
  462. mViewAbove.setCurrentItem(1);
  463. // mViewBehind.setCurrentItem(0);
  464. } else {
  465. mViewAbove.setCurrentItem(1);
  466. // mViewBehind.setCurrentItem(1);
  467. mViewAbove.setCustomViewBehind(mViewBehind);
  468. setSlidingEnabled(true);
  469. }
  470. }
  471. /**
  472. * Opens the menu and shows the menu view.
  473. */
  474. public void showMenu() {
  475. showMenu(true);
  476. }
  477. /**
  478. * Opens the menu and shows the menu view.
  479. *
  480. * @param animate
  481. *            true to animate the transition, false to ignore animation
  482. */
  483. public void showMenu(boolean animate) {
  484. mViewAbove.setCurrentItem(0, animate);
  485. }
  486. /**
  487. * Opens the menu and shows the secondary menu view. Will default to the
  488. * regular menu if there is only one.
  489. */
  490. public void showSecondaryMenu() {
  491. showSecondaryMenu(true);
  492. }
  493. /**
  494. * Opens the menu and shows the secondary (right) menu view. Will default to
  495. * the regular menu if there is only one.
  496. *
  497. * @param animate
  498. *            true to animate the transition, false to ignore animation
  499. */
  500. public void showSecondaryMenu(boolean animate) {
  501. mViewAbove.setCurrentItem(2, animate);
  502. }
  503. /**
  504. * Closes the menu and shows the above view.
  505. */
  506. public void showContent() {
  507. showContent(true);
  508. }
  509. /**
  510. * Closes the menu and shows the above view.
  511. *
  512. * @param animate
  513. *            true to animate the transition, false to ignore animation
  514. */
  515. public void showContent(boolean animate) {
  516. mViewAbove.setCurrentItem(1, animate);
  517. }
  518. /**
  519. * Toggle the SlidingMenu. If it is open, it will be closed, and vice versa.
  520. */
  521. public void toggle() {
  522. toggle(true);
  523. }
  524. /**
  525. * Toggle the SlidingMenu. If it is open, it will be closed, and vice versa.
  526. *
  527. * @param animate
  528. *            true to animate the transition, false to ignore animation
  529. */
  530. public void toggle(boolean animate) {
  531. if (isMenuShowing()) {
  532. showContent(animate);
  533. } else {
  534. showMenu(animate);
  535. }
  536. }
  537. /**
  538. * Checks if is the behind view showing.
  539. *
  540. * @return Whether or not the behind view is showing
  541. */
  542. public boolean isMenuShowing() {
  543. return mViewAbove.getCurrentItem() == 0
  544. || mViewAbove.getCurrentItem() == 2;
  545. }
  546. /**
  547. * Checks if is the behind view showing.
  548. *
  549. * @return Whether or not the behind view is showing
  550. */
  551. public boolean isSecondaryMenuShowing() {
  552. return mViewAbove.getCurrentItem() == 2;
  553. }
  554. /**
  555. * Gets the behind offset.
  556. *
  557. * @return The margin on the right of the screen that the behind view
  558. *         scrolls to
  559. */
  560. public int getBehindOffset() {
  561. return ((RelativeLayout.LayoutParams) mViewBehind.getLayoutParams()).rightMargin;
  562. }
  563. /**
  564. * Sets the behind offset.
  565. *
  566. * @param i
  567. *            The margin, in pixels, on the right of the screen that the
  568. *            behind view scrolls to.
  569. */
  570. public void setBehindOffset(int i) {
  571. // RelativeLayout.LayoutParams params =
  572. // ((RelativeLayout.LayoutParams)mViewBehind.getLayoutParams());
  573. // int bottom = params.bottomMargin;
  574. // int top = params.topMargin;
  575. // int left = params.leftMargin;
  576. // params.setMargins(left, top, i, bottom);
  577. mViewBehind.setWidthOffset(i);
  578. }
  579. /**
  580. * Sets the behind offset.
  581. *
  582. * @param resID
  583. *            The dimension resource id to be set as the behind offset. The
  584. *            menu, when open, will leave this width margin on the right of
  585. *            the screen.
  586. */
  587. public void setBehindOffsetRes(int resID) {
  588. int i = (int) getContext().getResources().getDimension(resID);
  589. setBehindOffset(i);
  590. }
  591. /**
  592. * Sets the above offset.
  593. *
  594. * @param i
  595. *            the new above offset, in pixels
  596. */
  597. public void setAboveOffset(int i) {
  598. mViewAbove.setAboveOffset(i);
  599. }
  600. /**
  601. * Sets the above offset.
  602. *
  603. * @param resID
  604. *            The dimension resource id to be set as the above offset.
  605. */
  606. public void setAboveOffsetRes(int resID) {
  607. int i = (int) getContext().getResources().getDimension(resID);
  608. setAboveOffset(i);
  609. }
  610. /**
  611. * Sets the behind width.
  612. *
  613. * @param i
  614. *            The width the Sliding Menu will open to, in pixels
  615. */
  616. @SuppressWarnings("deprecation")
  617. public void setBehindWidth(int i) {
  618. int width;
  619. Display display = ((WindowManager) getContext().getSystemService(
  620. Context.WINDOW_SERVICE)).getDefaultDisplay();
  621. try {
  622. Class<?> cls = Display.class;
  623. Class<?>[] parameterTypes = { Point.class };
  624. Point parameter = new Point();
  625. Method method = cls.getMethod("getSize", parameterTypes);
  626. method.invoke(display, parameter);
  627. width = parameter.x;
  628. } catch (Exception e) {
  629. width = display.getWidth();
  630. }
  631. setBehindOffset(width - i);
  632. }
  633. /**
  634. * Sets the behind width.
  635. *
  636. * @param res
  637. *            The dimension resource id to be set as the behind width
  638. *            offset. The menu, when open, will open this wide.
  639. */
  640. public void setBehindWidthRes(int res) {
  641. int i = (int) getContext().getResources().getDimension(res);
  642. setBehindWidth(i);
  643. }
  644. /**
  645. * Gets the behind scroll scale.
  646. *
  647. * @return The scale of the parallax scroll
  648. */
  649. public float getBehindScrollScale() {
  650. return mViewBehind.getScrollScale();
  651. }
  652. /**
  653. * Gets the touch mode margin threshold
  654. *
  655. * @return the touch mode margin threshold
  656. */
  657. public int getTouchmodeMarginThreshold() {
  658. return mViewBehind.getMarginThreshold();
  659. }
  660. /**
  661. * Set the touch mode margin threshold
  662. *
  663. * @param touchmodeMarginThreshold
  664. */
  665. public void setTouchmodeMarginThreshold(int touchmodeMarginThreshold) {
  666. mViewBehind.setMarginThreshold(touchmodeMarginThreshold);
  667. }
  668. /**
  669. * Sets the behind scroll scale.
  670. *
  671. * @param f
  672. *            The scale of the parallax scroll (i.e. 1.0f scrolls 1 pixel
  673. *            for every 1 pixel that the above view scrolls and 0.0f scrolls
  674. *            0 pixels)
  675. */
  676. public void setBehindScrollScale(float f) {
  677. if (f < 0 && f > 1)
  678. throw new IllegalStateException(
  679. "ScrollScale must be between 0 and 1");
  680. mViewBehind.setScrollScale(f);
  681. }
  682. /**
  683. * Sets the behind canvas transformer.
  684. *
  685. * @param t
  686. *            the new behind canvas transformer
  687. */
  688. public void setBehindCanvasTransformer(CanvasTransformer t) {
  689. mViewBehind.setCanvasTransformer(t);
  690. }
  691. /**
  692. * 设置右侧视图的转场动画
  693. *
  694. * @param t
  695. *            the new above canvas transformer
  696. */
  697. public void setAboveCanvasTransformer(CanvasTransformer t) {
  698. mViewAbove.setCanvasTransformer(t);
  699. }
  700. /**
  701. * Gets the touch mode above.
  702. *
  703. * @return the touch mode above
  704. */
  705. public int getTouchModeAbove() {
  706. return mViewAbove.getTouchMode();
  707. }
  708. /**
  709. * Controls whether the SlidingMenu can be opened with a swipe gesture.
  710. * Options are {@link #TOUCHMODE_MARGIN TOUCHMODE_MARGIN},
  711. * {@link #TOUCHMODE_FULLSCREEN TOUCHMODE_FULLSCREEN}, or
  712. * {@link #TOUCHMODE_NONE TOUCHMODE_NONE}
  713. *
  714. * @param i
  715. *            the new touch mode
  716. */
  717. public void setTouchModeAbove(int i) {
  718. if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN
  719. && i != TOUCHMODE_NONE) {
  720. throw new IllegalStateException(
  721. "TouchMode must be set to either"
  722. + "TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN or TOUCHMODE_NONE.");
  723. }
  724. mViewAbove.setTouchMode(i);
  725. }
  726. /**
  727. * Controls whether the SlidingMenu can be opened with a swipe gesture.
  728. * Options are {@link #TOUCHMODE_MARGIN TOUCHMODE_MARGIN},
  729. * {@link #TOUCHMODE_FULLSCREEN TOUCHMODE_FULLSCREEN}, or
  730. * {@link #TOUCHMODE_NONE TOUCHMODE_NONE}
  731. *
  732. * @param i
  733. *            the new touch mode
  734. */
  735. public void setTouchModeBehind(int i) {
  736. if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN
  737. && i != TOUCHMODE_NONE) {
  738. throw new IllegalStateException(
  739. "TouchMode must be set to either"
  740. + "TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN or TOUCHMODE_NONE.");
  741. }
  742. mViewBehind.setTouchMode(i);
  743. }
  744. /**
  745. * Sets the shadow drawable.
  746. *
  747. * @param resId
  748. *            the resource ID of the new shadow drawable
  749. */
  750. public void setShadowDrawable(int resId) {
  751. setShadowDrawable(getContext().getResources().getDrawable(resId));
  752. }
  753. /**
  754. * Sets the shadow drawable.
  755. *
  756. * @param d
  757. *            the new shadow drawable
  758. */
  759. public void setShadowDrawable(Drawable d) {
  760. mViewBehind.setShadowDrawable(d);
  761. }
  762. /**
  763. * Sets the secondary (right) shadow drawable.
  764. *
  765. * @param resId
  766. *            the resource ID of the new shadow drawable
  767. */
  768. public void setSecondaryShadowDrawable(int resId) {
  769. setSecondaryShadowDrawable(getContext().getResources().getDrawable(
  770. resId));
  771. }
  772. /**
  773. * Sets the secondary (right) shadow drawable.
  774. *
  775. * @param d
  776. *            the new shadow drawable
  777. */
  778. public void setSecondaryShadowDrawable(Drawable d) {
  779. mViewBehind.setSecondaryShadowDrawable(d);
  780. }
  781. /**
  782. * Sets the shadow width.
  783. *
  784. * @param resId
  785. *            The dimension resource id to be set as the shadow width.
  786. */
  787. public void setShadowWidthRes(int resId) {
  788. setShadowWidth((int) getResources().getDimension(resId));
  789. }
  790. /**
  791. * Sets the shadow width.
  792. *
  793. * @param pixels
  794. *            the new shadow width, in pixels
  795. */
  796. public void setShadowWidth(int pixels) {
  797. mViewBehind.setShadowWidth(pixels);
  798. }
  799. /**
  800. * Enables or disables the SlidingMenu's fade in and out
  801. *
  802. * @param b
  803. *            true to enable fade, false to disable it
  804. */
  805. public void setFadeEnabled(boolean b) {
  806. mViewBehind.setFadeEnabled(b);
  807. }
  808. /**
  809. * Sets how much the SlidingMenu fades in and out. Fade must be enabled, see
  810. * {@link #setFadeEnabled(boolean) setFadeEnabled(boolean)}
  811. *
  812. * @param f
  813. *            the new fade degree, between 0.0f and 1.0f
  814. */
  815. public void setFadeDegree(float f) {
  816. mViewBehind.setFadeDegree(f);
  817. }
  818. /**
  819. * Enables or disables whether the selector is drawn
  820. *
  821. * @param b
  822. *            true to draw the selector, false to not draw the selector
  823. */
  824. public void setSelectorEnabled(boolean b) {
  825. mViewBehind.setSelectorEnabled(true);
  826. }
  827. /**
  828. * Sets the selected view. The selector will be drawn here
  829. *
  830. * @param v
  831. *            the new selected view
  832. */
  833. public void setSelectedView(View v) {
  834. mViewBehind.setSelectedView(v);
  835. }
  836. /**
  837. * Sets the selector drawable.
  838. *
  839. * @param res
  840. *            a resource ID for the selector drawable
  841. */
  842. public void setSelectorDrawable(int res) {
  843. mViewBehind.setSelectorBitmap(BitmapFactory.decodeResource(
  844. getResources(), res));
  845. }
  846. /**
  847. * Sets the selector drawable.
  848. *
  849. * @param b
  850. *            the new selector bitmap
  851. */
  852. public void setSelectorBitmap(Bitmap b) {
  853. mViewBehind.setSelectorBitmap(b);
  854. }
  855. /**
  856. * Add a View ignored by the Touch Down event when mode is Fullscreen
  857. *
  858. * @param v
  859. *            a view to be ignored
  860. */
  861. public void addIgnoredView(View v) {
  862. mViewAbove.addIgnoredView(v);
  863. }
  864. /**
  865. * Remove a View ignored by the Touch Down event when mode is Fullscreen
  866. *
  867. * @param v
  868. *            a view not wanted to be ignored anymore
  869. */
  870. public void removeIgnoredView(View v) {
  871. mViewAbove.removeIgnoredView(v);
  872. }
  873. /**
  874. * Clear the list of Views ignored by the Touch Down event when mode is
  875. * Fullscreen
  876. */
  877. public void clearIgnoredViews() {
  878. mViewAbove.clearIgnoredViews();
  879. }
  880. /**
  881. * Sets the OnOpenListener. {@link OnOpenListener#onOpen()
  882. * OnOpenListener.onOpen()} will be called when the SlidingMenu is opened
  883. *
  884. * @param listener
  885. *            the new OnOpenListener
  886. */
  887. public void setOnOpenListener(OnOpenListener listener) {
  888. // mViewAbove.setOnOpenListener(listener);
  889. mOpenListener = listener;
  890. }
  891. /**
  892. * Sets the OnOpenListner for secondary menu {@link OnOpenListener#onOpen()
  893. * OnOpenListener.onOpen()} will be called when the secondary SlidingMenu is
  894. * opened
  895. *
  896. * @param listener
  897. *            the new OnOpenListener
  898. */
  899. public void setSecondaryOnOpenListner(OnOpenListener listener) {
  900. mSecondaryOpenListner = listener;
  901. }
  902. /**
  903. * Sets the OnCloseListener. {@link OnCloseListener#onClose()
  904. * OnCloseListener.onClose()} will be called when any one of the SlidingMenu
  905. * is closed
  906. *
  907. * @param listener
  908. *            the new setOnCloseListener
  909. */
  910. public void setOnCloseListener(OnCloseListener listener) {
  911. // mViewAbove.setOnCloseListener(listener);
  912. mCloseListener = listener;
  913. }
  914. /**
  915. * Sets the OnOpenedListener. {@link OnOpenedListener#onOpened()
  916. * OnOpenedListener.onOpened()} will be called after the SlidingMenu is
  917. * opened
  918. *
  919. * @param listener
  920. *            the new OnOpenedListener
  921. */
  922. public void setOnOpenedListener(OnOpenedListener listener) {
  923. mViewAbove.setOnOpenedListener(listener);
  924. }
  925. /**
  926. * Sets the OnClosedListener. {@link OnClosedListener#onClosed()
  927. * OnClosedListener.onClosed()} will be called after the SlidingMenu is
  928. * closed
  929. *
  930. * @param listener
  931. *            the new OnClosedListener
  932. */
  933. public void setOnClosedListener(OnClosedListener listener) {
  934. mViewAbove.setOnClosedListener(listener);
  935. }
  936. public static class SavedState extends BaseSavedState {
  937. private final int mItem;
  938. public SavedState(Parcelable superState, int item) {
  939. super(superState);
  940. mItem = item;
  941. }
  942. private SavedState(Parcel in) {
  943. super(in);
  944. mItem = in.readInt();
  945. }
  946. public int getItem() {
  947. return mItem;
  948. }
  949. /*
  950. * (non-Javadoc)
  951. *
  952. * @see android.view.AbsSavedState#writeToParcel(android.os.Parcel, int)
  953. */
  954. public void writeToParcel(Parcel out, int flags) {
  955. super.writeToParcel(out, flags);
  956. out.writeInt(mItem);
  957. }
  958. public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
  959. public SavedState createFromParcel(Parcel in) {
  960. return new SavedState(in);
  961. }
  962. public SavedState[] newArray(int size) {
  963. return new SavedState[size];
  964. }
  965. };
  966. }
  967. /*
  968. * (non-Javadoc)
  969. *
  970. * @see android.view.View#onSaveInstanceState()
  971. */
  972. @Override
  973. protected Parcelable onSaveInstanceState() {
  974. Parcelable superState = super.onSaveInstanceState();
  975. SavedState ss = new SavedState(superState, mViewAbove.getCurrentItem());
  976. return ss;
  977. }
  978. /*
  979. * (non-Javadoc)
  980. *
  981. * @see android.view.View#onRestoreInstanceState(android.os.Parcelable)
  982. */
  983. @Override
  984. protected void onRestoreInstanceState(Parcelable state) {
  985. SavedState ss = (SavedState) state;
  986. super.onRestoreInstanceState(ss.getSuperState());
  987. mViewAbove.setCurrentItem(ss.getItem());
  988. }
  989. /*
  990. * (non-Javadoc)
  991. *
  992. * @see android.view.ViewGroup#fitSystemWindows(android.graphics.Rect)
  993. */
  994. @SuppressLint("NewApi")
  995. @Override
  996. protected boolean fitSystemWindows(Rect insets) {
  997. int leftPadding = insets.left;
  998. int rightPadding = insets.right;
  999. int topPadding = insets.top;
  1000. int bottomPadding = insets.bottom;
  1001. if (!mActionbarOverlay) {
  1002. Log.v(TAG, "setting padding!");
  1003. setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
  1004. }
  1005. return true;
  1006. }
  1007. @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  1008. public void manageLayers(float percentOpen) {
  1009. if (Build.VERSION.SDK_INT < 11)
  1010. return;
  1011. boolean layer = percentOpen > 0.0f && percentOpen < 1.0f;
  1012. final int layerType = layer ? View.LAYER_TYPE_HARDWARE
  1013. : View.LAYER_TYPE_NONE;
  1014. if (layerType != getContent().getLayerType()) {
  1015. getHandler().post(new Runnable() {
  1016. public void run() {
  1017. Log.v(TAG, "changing layerType. hardware? "
  1018. + (layerType == View.LAYER_TYPE_HARDWARE));
  1019. getContent().setLayerType(layerType, null);
  1020. getMenu().setLayerType(layerType, null);
  1021. if (getSecondaryMenu() != null) {
  1022. getSecondaryMenu().setLayerType(layerType, null);
  1023. }
  1024. }
  1025. });
  1026. }
  1027. }
  1028. }
  1. public class CustomViewAbove extends ViewGroup {
  2. private static final String TAG = "CustomViewAbove";
  3. private static final boolean DEBUG = false;
  4. private static final boolean USE_CACHE = false;
  5. private static final int MAX_SETTLE_DURATION = 600; // ms
  6. private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
  7. private static final Interpolator sInterpolator = new Interpolator() {
  8. public float getInterpolation(float t) {
  9. t -= 1.0f;
  10. return t * t * t * t * t + 1.0f;
  11. }
  12. };
  13. private View mContent;
  14. private int mCurItem;
  15. private Scroller mScroller;
  16. private boolean mScrollingCacheEnabled;
  17. private boolean mScrolling;
  18. private boolean mIsBeingDragged;
  19. private boolean mIsUnableToDrag;
  20. private int mTouchSlop;
  21. private float mInitialMotionX;
  22. /**
  23. * Position of the last motion event.
  24. */
  25. private float mLastMotionX;
  26. private float mLastMotionY;
  27. /**
  28. * ID of the active pointer. This is used to retain consistency during
  29. * drags/flings if multiple pointers are used.
  30. */
  31. protected int mActivePointerId = INVALID_POINTER;
  32. /**
  33. * Sentinel value for no current active pointer.
  34. * Used by {@link #mActivePointerId}.
  35. */
  36. private static final int INVALID_POINTER = -1;
  37. /** 保存转场动画的变量*/
  38. private CanvasTransformer mTransformer;
  39. /**
  40. * Determines speed during touch scrolling
  41. */
  42. protected VelocityTracker mVelocityTracker;
  43. private int mMinimumVelocity;
  44. protected int mMaximumVelocity;
  45. private int mFlingDistance;
  46. private CustomViewBehind mViewBehind;
  47. //  private int mMode;
  48. private boolean mEnabled = true;
  49. private OnPageChangeListener mOnPageChangeListener;
  50. private OnPageChangeListener mInternalPageChangeListener;
  51. //  private OnCloseListener mCloseListener;
  52. //  private OnOpenListener mOpenListener;
  53. private OnClosedListener mClosedListener;
  54. private OnOpenedListener mOpenedListener;
  55. private List<View> mIgnoredViews = new ArrayList<View>();
  56. //  private int mScrollState = SCROLL_STATE_IDLE;
  57. /**
  58. * Callback interface for responding to changing state of the selected page.
  59. */
  60. public interface OnPageChangeListener {
  61. /**
  62. * This method will be invoked when the current page is scrolled, either as part
  63. * of a programmatically initiated smooth scroll or a user initiated touch scroll.
  64. *
  65. * @param position Position index of the first page currently being displayed.
  66. *                 Page position+1 will be visible if positionOffset is nonzero.
  67. * @param positionOffset Value from [0, 1) indicating the offset from the page at position.
  68. * @param positionOffsetPixels Value in pixels indicating the offset from position.
  69. */
  70. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
  71. /**
  72. * This method will be invoked when a new page becomes selected. Animation is not
  73. * necessarily complete.
  74. *
  75. * @param position Position index of the new selected page.
  76. */
  77. public void onPageSelected(int position);
  78. }
  79. /**
  80. * Simple implementation of the {@link OnPageChangeListener} interface with stub
  81. * implementations of each method. Extend this if you do not intend to override
  82. * every method of {@link OnPageChangeListener}.
  83. */
  84. public static class SimpleOnPageChangeListener implements OnPageChangeListener {
  85. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  86. // This space for rent
  87. }
  88. public void onPageSelected(int position) {
  89. // This space for rent
  90. }
  91. public void onPageScrollStateChanged(int state) {
  92. // This space for rent
  93. }
  94. }
  95. public CustomViewAbove(Context context) {
  96. this(context, null);
  97. }
  98. public CustomViewAbove(Context context, AttributeSet attrs) {
  99. super(context, attrs);
  100. initCustomViewAbove();
  101. }
  102. void initCustomViewAbove() {
  103. setWillNotDraw(false);
  104. setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
  105. setFocusable(true);
  106. final Context context = getContext();
  107. mScroller = new Scroller(context, sInterpolator);
  108. final ViewConfiguration configuration = ViewConfiguration.get(context);
  109. mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
  110. mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
  111. mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
  112. setInternalPageChangeListener(new SimpleOnPageChangeListener() {
  113. public void onPageSelected(int position) {
  114. if (mViewBehind != null) {
  115. switch (position) {
  116. case 0:
  117. case 2:
  118. mViewBehind.setChildrenEnabled(true);
  119. break;
  120. case 1:
  121. mViewBehind.setChildrenEnabled(false);
  122. break;
  123. }
  124. }
  125. }
  126. });
  127. final float density = context.getResources().getDisplayMetrics().density;
  128. mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
  129. }
  130. /**
  131. * Set the currently selected page. If the CustomViewPager has already been through its first
  132. * layout there will be a smooth animated transition between the current item and the
  133. * specified item.
  134. *
  135. * @param item Item index to select
  136. */
  137. public void setCurrentItem(int item) {
  138. setCurrentItemInternal(item, true, false);
  139. }
  140. /**
  141. * Set the currently selected page.
  142. *
  143. * @param item Item index to select
  144. * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
  145. */
  146. public void setCurrentItem(int item, boolean smoothScroll) {
  147. setCurrentItemInternal(item, smoothScroll, false);
  148. }
  149. public int getCurrentItem() {
  150. return mCurItem;
  151. }
  152. void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
  153. setCurrentItemInternal(item, smoothScroll, always, 0);
  154. }
  155. void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
  156. if (!always && mCurItem == item) {
  157. setScrollingCacheEnabled(false);
  158. return;
  159. }
  160. item = mViewBehind.getMenuPage(item);
  161. final boolean dispatchSelected = mCurItem != item;
  162. mCurItem = item;
  163. final int destX = getDestScrollX(mCurItem);
  164. if (dispatchSelected && mOnPageChangeListener != null) {
  165. mOnPageChangeListener.onPageSelected(item);
  166. }
  167. if (dispatchSelected && mInternalPageChangeListener != null) {
  168. mInternalPageChangeListener.onPageSelected(item);
  169. }
  170. if (smoothScroll) {
  171. smoothScrollTo(destX, 0, velocity);
  172. } else {
  173. completeScroll();
  174. scrollTo(destX, 0);
  175. }
  176. }
  177. /**
  178. * Set a listener that will be invoked whenever the page changes or is incrementally
  179. * scrolled. See {@link OnPageChangeListener}.
  180. *
  181. * @param listener Listener to set
  182. */
  183. public void setOnPageChangeListener(OnPageChangeListener listener) {
  184. mOnPageChangeListener = listener;
  185. }
  186. /*
  187. public void setOnOpenListener(OnOpenListener l) {
  188. mOpenListener = l;
  189. }
  190. public void setOnCloseListener(OnCloseListener l) {
  191. mCloseListener = l;
  192. }
  193. */
  194. public void setOnOpenedListener(OnOpenedListener l) {
  195. mOpenedListener = l;
  196. }
  197. public void setOnClosedListener(OnClosedListener l) {
  198. mClosedListener = l;
  199. }
  200. /**
  201. * Set a separate OnPageChangeListener for internal use by the support library.
  202. *
  203. * @param listener Listener to set
  204. * @return The old listener that was set, if any.
  205. */
  206. OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {
  207. OnPageChangeListener oldListener = mInternalPageChangeListener;
  208. mInternalPageChangeListener = listener;
  209. return oldListener;
  210. }
  211. public void addIgnoredView(View v) {
  212. if (!mIgnoredViews.contains(v)) {
  213. mIgnoredViews.add(v);
  214. }
  215. }
  216. public void removeIgnoredView(View v) {
  217. mIgnoredViews.remove(v);
  218. }
  219. public void clearIgnoredViews() {
  220. mIgnoredViews.clear();
  221. }
  222. // We want the duration of the page snap animation to be influenced by the distance that
  223. // the screen has to travel, however, we don't want this duration to be effected in a
  224. // purely linear fashion. Instead, we use this method to moderate the effect that the distance
  225. // of travel has on the overall snap duration.
  226. float distanceInfluenceForSnapDuration(float f) {
  227. f -= 0.5f; // center the values about 0.
  228. f *= 0.3f * Math.PI / 2.0f;
  229. return (float) FloatMath.sin(f);
  230. }
  231. public int getDestScrollX(int page) {
  232. switch (page) {
  233. case 0:
  234. case 2:
  235. return mViewBehind.getMenuLeft(mContent, page);
  236. case 1:
  237. return mContent.getLeft();
  238. }
  239. return 0;
  240. }
  241. private int getLeftBound() {
  242. return mViewBehind.getAbsLeftBound(mContent);
  243. }
  244. private int getRightBound() {
  245. return mViewBehind.getAbsRightBound(mContent);
  246. }
  247. public int getContentLeft() {
  248. return mContent.getLeft() + mContent.getPaddingLeft();
  249. }
  250. public boolean isMenuOpen() {
  251. return mCurItem == 0 || mCurItem == 2;
  252. }
  253. private boolean isInIgnoredView(MotionEvent ev) {
  254. Rect rect = new Rect();
  255. for (View v : mIgnoredViews) {
  256. v.getHitRect(rect);
  257. if (rect.contains((int)ev.getX(), (int)ev.getY())) return true;
  258. }
  259. return false;
  260. }
  261. public int getBehindWidth() {
  262. if (mViewBehind == null) {
  263. return 0;
  264. } else {
  265. return mViewBehind.getBehindWidth();
  266. }
  267. }
  268. public int getChildWidth(int i) {
  269. switch (i) {
  270. case 0:
  271. return getBehindWidth();
  272. case 1:
  273. return mContent.getWidth();
  274. default:
  275. return 0;
  276. }
  277. }
  278. public boolean isSlidingEnabled() {
  279. return mEnabled;
  280. }
  281. public void setSlidingEnabled(boolean b) {
  282. mEnabled = b;
  283. }
  284. /**
  285. * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
  286. *
  287. * @param x the number of pixels to scroll by on the X axis
  288. * @param y the number of pixels to scroll by on the Y axis
  289. */
  290. void smoothScrollTo(int x, int y) {
  291. smoothScrollTo(x, y, 0);
  292. }
  293. /**
  294. * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
  295. *
  296. * @param x the number of pixels to scroll by on the X axis
  297. * @param y the number of pixels to scroll by on the Y axis
  298. * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)
  299. */
  300. void smoothScrollTo(int x, int y, int velocity) {
  301. if (getChildCount() == 0) {
  302. // Nothing to do.
  303. setScrollingCacheEnabled(false);
  304. return;
  305. }
  306. int sx = getScrollX();
  307. int sy = getScrollY();
  308. int dx = x - sx;
  309. int dy = y - sy;
  310. if (dx == 0 && dy == 0) {
  311. completeScroll();
  312. if (isMenuOpen()) {
  313. if (mOpenedListener != null)
  314. mOpenedListener.onOpened();
  315. } else {
  316. if (mClosedListener != null)
  317. mClosedListener.onClosed();
  318. }
  319. return;
  320. }
  321. setScrollingCacheEnabled(true);
  322. mScrolling = true;
  323. final int width = getBehindWidth();
  324. final int halfWidth = width / 2;
  325. final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
  326. final float distance = halfWidth + halfWidth *
  327. distanceInfluenceForSnapDuration(distanceRatio);
  328. int duration = 0;
  329. velocity = Math.abs(velocity);
  330. if (velocity > 0) {
  331. duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
  332. } else {
  333. final float pageDelta = (float) Math.abs(dx) / width;
  334. duration = (int) ((pageDelta + 1) * 100);
  335. duration = MAX_SETTLE_DURATION;
  336. }
  337. duration = Math.min(duration, MAX_SETTLE_DURATION);
  338. mScroller.startScroll(sx, sy, dx, dy, duration);
  339. invalidate();
  340. }
  341. public void setContent(View v) {
  342. if (mContent != null)
  343. this.removeView(mContent);
  344. mContent = v;
  345. addView(mContent);
  346. }
  347. public View getContent() {
  348. return mContent;
  349. }
  350. public void setCustomViewBehind(CustomViewBehind cvb) {
  351. mViewBehind = cvb;
  352. }
  353. @Override
  354. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  355. int width = getDefaultSize(0, widthMeasureSpec);
  356. int height = getDefaultSize(0, heightMeasureSpec);
  357. setMeasuredDimension(width, height);
  358. final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);
  359. final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);
  360. mContent.measure(contentWidth, contentHeight);
  361. }
  362. @Override
  363. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  364. super.onSizeChanged(w, h, oldw, oldh);
  365. // Make sure scroll position is set correctly.
  366. if (w != oldw) {
  367. // [ChrisJ] - This fixes the onConfiguration change for orientation issue..
  368. // maybe worth having a look why the recomputeScroll pos is screwing
  369. // up?
  370. completeScroll();
  371. scrollTo(getDestScrollX(mCurItem), getScrollY());
  372. }
  373. }
  374. @Override
  375. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  376. final int width = r - l;
  377. final int height = b - t;
  378. mContent.layout(0, 0, width, height);
  379. }
  380. public void setAboveOffset(int i) {
  381. //      RelativeLayout.LayoutParams params = ((RelativeLayout.LayoutParams)mContent.getLayoutParams());
  382. //      params.setMargins(i, params.topMargin, params.rightMargin, params.bottomMargin);
  383. mContent.setPadding(i, mContent.getPaddingTop(),
  384. mContent.getPaddingRight(), mContent.getPaddingBottom());
  385. }
  386. @Override
  387. public void computeScroll() {
  388. if (!mScroller.isFinished()) {
  389. if (mScroller.computeScrollOffset()) {
  390. int oldX = getScrollX();
  391. int oldY = getScrollY();
  392. int x = mScroller.getCurrX();
  393. int y = mScroller.getCurrY();
  394. if (oldX != x || oldY != y) {
  395. scrollTo(x, y);
  396. pageScrolled(x);
  397. }
  398. // Keep on drawing until the animation has finished.
  399. invalidate();
  400. return;
  401. }
  402. }
  403. // Done with scroll, clean up state.
  404. completeScroll();
  405. }
  406. private void pageScrolled(int xpos) {
  407. final int widthWithMargin = getWidth();
  408. final int position = xpos / widthWithMargin;
  409. final int offsetPixels = xpos % widthWithMargin;
  410. final float offset = (float) offsetPixels / widthWithMargin;
  411. onPageScrolled(position, offset, offsetPixels);
  412. }
  413. /**
  414. * This method will be invoked when the current page is scrolled, either as part
  415. * of a programmatically initiated smooth scroll or a user initiated touch scroll.
  416. * If you override this method you must call through to the superclass implementation
  417. * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled
  418. * returns.
  419. *
  420. * @param position Position index of the first page currently being displayed.
  421. *                 Page position+1 will be visible if positionOffset is nonzero.
  422. * @param offset Value from [0, 1) indicating the offset from the page at position.
  423. * @param offsetPixels Value in pixels indicating the offset from position.
  424. */
  425. protected void onPageScrolled(int position, float offset, int offsetPixels) {
  426. if (mOnPageChangeListener != null) {
  427. mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
  428. }
  429. if (mInternalPageChangeListener != null) {
  430. mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
  431. }
  432. }
  433. private void completeScroll() {
  434. boolean needPopulate = mScrolling;
  435. if (needPopulate) {
  436. // Done with scroll, no longer want to cache view drawing.
  437. setScrollingCacheEnabled(false);
  438. mScroller.abortAnimation();
  439. int oldX = getScrollX();
  440. int oldY = getScrollY();
  441. int x = mScroller.getCurrX();
  442. int y = mScroller.getCurrY();
  443. if (oldX != x || oldY != y) {
  444. scrollTo(x, y);
  445. }
  446. if (isMenuOpen()) {
  447. if (mOpenedListener != null)
  448. mOpenedListener.onOpened();
  449. } else {
  450. if (mClosedListener != null)
  451. mClosedListener.onClosed();
  452. }
  453. }
  454. mScrolling = false;
  455. }
  456. protected int mTouchMode = SlidingMenu.TOUCHMODE_MARGIN;
  457. public void setTouchMode(int i) {
  458. mTouchMode = i;
  459. }
  460. public int getTouchMode() {
  461. return mTouchMode;
  462. }
  463. private boolean thisTouchAllowed(MotionEvent ev) {
  464. int x = (int) (ev.getX() + mScrollX);
  465. if (isMenuOpen()) {
  466. return mViewBehind.menuOpenTouchAllowed(mContent, mCurItem, x);
  467. } else {
  468. switch (mTouchMode) {
  469. case SlidingMenu.TOUCHMODE_FULLSCREEN:
  470. return !isInIgnoredView(ev);
  471. case SlidingMenu.TOUCHMODE_NONE:
  472. return false;
  473. case SlidingMenu.TOUCHMODE_MARGIN:
  474. return mViewBehind.marginTouchAllowed(mContent, x);
  475. }
  476. }
  477. return false;
  478. }
  479. private boolean thisSlideAllowed(float dx) {
  480. boolean allowed = false;
  481. if (isMenuOpen()) {
  482. allowed = mViewBehind.menuOpenSlideAllowed(dx);
  483. } else {
  484. allowed = mViewBehind.menuClosedSlideAllowed(dx);
  485. }
  486. if (DEBUG)
  487. Log.v(TAG, "this slide allowed " + allowed + " dx: " + dx);
  488. return allowed;
  489. }
  490. private int getPointerIndex(MotionEvent ev, int id) {
  491. int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);
  492. if (activePointerIndex == -1)
  493. mActivePointerId = INVALID_POINTER;
  494. return activePointerIndex;
  495. }
  496. private boolean mQuickReturn = false;
  497. @Override
  498. public boolean onInterceptTouchEvent(MotionEvent ev) {
  499. if (!mEnabled)
  500. return false;
  501. final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
  502. if (DEBUG)
  503. if (action == MotionEvent.ACTION_DOWN)
  504. Log.v(TAG, "Received ACTION_DOWN");
  505. if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP
  506. || (action != MotionEvent.ACTION_DOWN && mIsUnableToDrag)) {
  507. endDrag();
  508. return false;
  509. }
  510. switch (action) {
  511. case MotionEvent.ACTION_MOVE:
  512. determineDrag(ev);
  513. break;
  514. case MotionEvent.ACTION_DOWN:
  515. int index = MotionEventCompat.getActionIndex(ev);
  516. mActivePointerId = MotionEventCompat.getPointerId(ev, index);
  517. if (mActivePointerId == INVALID_POINTER)
  518. break;
  519. mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);
  520. mLastMotionY = MotionEventCompat.getY(ev, index);
  521. if (thisTouchAllowed(ev)) {
  522. mIsBeingDragged = false;
  523. mIsUnableToDrag = false;
  524. if (isMenuOpen() && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {
  525. mQuickReturn = true;
  526. }
  527. } else {
  528. mIsUnableToDrag = true;
  529. }
  530. break;
  531. case MotionEventCompat.ACTION_POINTER_UP:
  532. onSecondaryPointerUp(ev);
  533. break;
  534. }
  535. if (!mIsBeingDragged) {
  536. if (mVelocityTracker == null) {
  537. mVelocityTracker = VelocityTracker.obtain();
  538. }
  539. mVelocityTracker.addMovement(ev);
  540. }
  541. return mIsBeingDragged || mQuickReturn;
  542. }
  543. @Override
  544. public boolean onTouchEvent(MotionEvent ev) {
  545. if (!mEnabled)
  546. return false;
  547. if (!mIsBeingDragged && !thisTouchAllowed(ev))
  548. return false;
  549. //      if (!mIsBeingDragged && !mQuickReturn)
  550. //          return false;
  551. final int action = ev.getAction();
  552. if (mVelocityTracker == null) {
  553. mVelocityTracker = VelocityTracker.obtain();
  554. }
  555. mVelocityTracker.addMovement(ev);
  556. switch (action & MotionEventCompat.ACTION_MASK) {
  557. case MotionEvent.ACTION_DOWN:
  558. /*
  559. * If being flinged and user touches, stop the fling. isFinished
  560. * will be false if being flinged.
  561. */
  562. completeScroll();
  563. // Remember where the motion event started
  564. int index = MotionEventCompat.getActionIndex(ev);
  565. mActivePointerId = MotionEventCompat.getPointerId(ev, index);
  566. mLastMotionX = mInitialMotionX = ev.getX();
  567. break;
  568. case MotionEvent.ACTION_MOVE:
  569. if (!mIsBeingDragged) {
  570. determineDrag(ev);
  571. if (mIsUnableToDrag)
  572. return false;
  573. }
  574. if (mIsBeingDragged) {
  575. // Scroll to follow the motion event
  576. final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
  577. if (mActivePointerId == INVALID_POINTER)
  578. break;
  579. final float x = MotionEventCompat.getX(ev, activePointerIndex);
  580. final float deltaX = mLastMotionX - x;
  581. mLastMotionX = x;
  582. float oldScrollX = getScrollX();
  583. float scrollX = oldScrollX + deltaX;
  584. final float leftBound = getLeftBound();
  585. final float rightBound = getRightBound();
  586. if (scrollX < leftBound) {
  587. scrollX = leftBound;
  588. } else if (scrollX > rightBound) {
  589. scrollX = rightBound;
  590. }
  591. // Don't lose the rounded component
  592. mLastMotionX += scrollX - (int) scrollX;
  593. scrollTo((int) scrollX, getScrollY());
  594. pageScrolled((int) scrollX);
  595. }
  596. break;
  597. case MotionEvent.ACTION_UP:
  598. if (mIsBeingDragged) {
  599. final VelocityTracker velocityTracker = mVelocityTracker;
  600. velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  601. int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
  602. velocityTracker, mActivePointerId);
  603. final int scrollX = getScrollX();
  604. final float pageOffset = (float) (scrollX - getDestScrollX(mCurItem)) / getBehindWidth();
  605. final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
  606. if (mActivePointerId != INVALID_POINTER) {
  607. final float x = MotionEventCompat.getX(ev, activePointerIndex);
  608. final int totalDelta = (int) (x - mInitialMotionX);
  609. int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);
  610. setCurrentItemInternal(nextPage, true, true, initialVelocity);
  611. } else {
  612. setCurrentItemInternal(mCurItem, true, true, initialVelocity);
  613. }
  614. mActivePointerId = INVALID_POINTER;
  615. endDrag();
  616. } else if (mQuickReturn && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {
  617. // close the menu
  618. setCurrentItem(1);
  619. endDrag();
  620. }
  621. break;
  622. case MotionEvent.ACTION_CANCEL:
  623. if (mIsBeingDragged) {
  624. setCurrentItemInternal(mCurItem, true, true);
  625. mActivePointerId = INVALID_POINTER;
  626. endDrag();
  627. }
  628. break;
  629. case MotionEventCompat.ACTION_POINTER_DOWN: {
  630. final int indexx = MotionEventCompat.getActionIndex(ev);
  631. mLastMotionX = MotionEventCompat.getX(ev, indexx);
  632. mActivePointerId = MotionEventCompat.getPointerId(ev, indexx);
  633. break;
  634. }
  635. case MotionEventCompat.ACTION_POINTER_UP:
  636. onSecondaryPointerUp(ev);
  637. int pointerIndex = getPointerIndex(ev, mActivePointerId);
  638. if (mActivePointerId == INVALID_POINTER)
  639. break;
  640. mLastMotionX = MotionEventCompat.getX(ev, pointerIndex);
  641. break;
  642. }
  643. return true;
  644. }
  645. private void determineDrag(MotionEvent ev) {
  646. final int activePointerId = mActivePointerId;
  647. final int pointerIndex = getPointerIndex(ev, activePointerId);
  648. if (activePointerId == INVALID_POINTER || pointerIndex == INVALID_POINTER)
  649. return;
  650. final float x = MotionEventCompat.getX(ev, pointerIndex);
  651. final float dx = x - mLastMotionX;
  652. final float xDiff = Math.abs(dx);
  653. final float y = MotionEventCompat.getY(ev, pointerIndex);
  654. final float dy = y - mLastMotionY;
  655. final float yDiff = Math.abs(dy);
  656. if (xDiff > (isMenuOpen()?mTouchSlop/2:mTouchSlop) && xDiff > yDiff && thisSlideAllowed(dx)) {
  657. startDrag();
  658. mLastMotionX = x;
  659. mLastMotionY = y;
  660. setScrollingCacheEnabled(true);
  661. // TODO add back in touch slop check
  662. } else if (xDiff > mTouchSlop) {
  663. mIsUnableToDrag = true;
  664. }
  665. }
  666. @Override
  667. public void scrollTo(int x, int y) {
  668. super.scrollTo(x, y);
  669. mScrollX = x;
  670. mViewBehind.scrollBehindTo(mContent, x, y);
  671. ((SlidingMenu)getParent()).manageLayers(getPercentOpen());
  672. if (mTransformer != null) {
  673. invalidate();
  674. }
  675. }
  676. private int determineTargetPage(float pageOffset, int velocity, int deltaX) {
  677. int targetPage = mCurItem;
  678. if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
  679. if (velocity > 0 && deltaX > 0) {
  680. targetPage -= 1;
  681. } else if (velocity < 0 && deltaX < 0){
  682. targetPage += 1;
  683. }
  684. } else {
  685. targetPage = (int) Math.round(mCurItem + pageOffset);
  686. }
  687. return targetPage;
  688. }
  689. protected float getPercentOpen() {
  690. return Math.abs(mScrollX-mContent.getLeft()) / getBehindWidth();
  691. }
  692. @Override
  693. protected void dispatchDraw(Canvas canvas) {
  694. // 这句要注释掉,否则会出现2个右侧的视图,一个有转场动画,一个没有转场动画
  695. // super.dispatchDraw(canvas);
  696. // Draw the margin drawable if needed.
  697. mViewBehind.drawShadow(mContent, canvas);
  698. mViewBehind.drawFade(mContent, canvas, getPercentOpen());
  699. mViewBehind.drawSelector(mContent, canvas, getPercentOpen());
  700. // 设置右侧视图的转场效果,主要是修改Canvas。
  701. if (mTransformer != null) {
  702. canvas.save();
  703. mTransformer.transformCanvas(canvas, getPercentOpen());
  704. super.dispatchDraw(canvas);
  705. canvas.restore();
  706. } else {
  707. super.dispatchDraw(canvas);
  708. }
  709. }
  710. // variables for drawing
  711. private float mScrollX = 0.0f;
  712. private void onSecondaryPointerUp(MotionEvent ev) {
  713. if (DEBUG) Log.v(TAG, "onSecondaryPointerUp called");
  714. final int pointerIndex = MotionEventCompat.getActionIndex(ev);
  715. final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
  716. if (pointerId == mActivePointerId) {
  717. // This was our active pointer going up. Choose a new
  718. // active pointer and adjust accordingly.
  719. final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
  720. mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
  721. mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
  722. if (mVelocityTracker != null) {
  723. mVelocityTracker.clear();
  724. }
  725. }
  726. }
  727. private void startDrag() {
  728. mIsBeingDragged = true;
  729. mQuickReturn = false;
  730. }
  731. private void endDrag() {
  732. mQuickReturn = false;
  733. mIsBeingDragged = false;
  734. mIsUnableToDrag = false;
  735. mActivePointerId = INVALID_POINTER;
  736. if (mVelocityTracker != null) {
  737. mVelocityTracker.recycle();
  738. mVelocityTracker = null;
  739. }
  740. }
  741. private void setScrollingCacheEnabled(boolean enabled) {
  742. if (mScrollingCacheEnabled != enabled) {
  743. mScrollingCacheEnabled = enabled;
  744. if (USE_CACHE) {
  745. final int size = getChildCount();
  746. for (int i = 0; i < size; ++i) {
  747. final View child = getChildAt(i);
  748. if (child.getVisibility() != GONE) {
  749. child.setDrawingCacheEnabled(enabled);
  750. }
  751. }
  752. }
  753. }
  754. }
  755. /**
  756. * Tests scrollability within child views of v given a delta of dx.
  757. *
  758. * @param v View to test for horizontal scrollability
  759. * @param checkV Whether the view v passed should itself be checked for scrollability (true),
  760. *               or just its children (false).
  761. * @param dx Delta scrolled in pixels
  762. * @param x X coordinate of the active touch point
  763. * @param y Y coordinate of the active touch point
  764. * @return true if child views of v can be scrolled by delta of dx.
  765. */
  766. protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
  767. if (v instanceof ViewGroup) {
  768. final ViewGroup group = (ViewGroup) v;
  769. final int scrollX = v.getScrollX();
  770. final int scrollY = v.getScrollY();
  771. final int count = group.getChildCount();
  772. // Count backwards - let topmost views consume scroll distance first.
  773. for (int i = count - 1; i >= 0; i--) {
  774. final View child = group.getChildAt(i);
  775. if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
  776. y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
  777. canScroll(child, true, dx, x + scrollX - child.getLeft(),
  778. y + scrollY - child.getTop())) {
  779. return true;
  780. }
  781. }
  782. }
  783. return checkV && ViewCompat.canScrollHorizontally(v, -dx);
  784. }
  785. @Override
  786. public boolean dispatchKeyEvent(KeyEvent event) {
  787. // Let the focused view and/or our descendants get the key first
  788. return super.dispatchKeyEvent(event) || executeKeyEvent(event);
  789. }
  790. /**
  791. * You can call this function yourself to have the scroll view perform
  792. * scrolling from a key event, just as if the event had been dispatched to
  793. * it by the view hierarchy.
  794. *
  795. * @param event The key event to execute.
  796. * @return Return true if the event was handled, else false.
  797. */
  798. public boolean executeKeyEvent(KeyEvent event) {
  799. boolean handled = false;
  800. if (event.getAction() == KeyEvent.ACTION_DOWN) {
  801. switch (event.getKeyCode()) {
  802. case KeyEvent.KEYCODE_DPAD_LEFT:
  803. handled = arrowScroll(FOCUS_LEFT);
  804. break;
  805. case KeyEvent.KEYCODE_DPAD_RIGHT:
  806. handled = arrowScroll(FOCUS_RIGHT);
  807. break;
  808. case KeyEvent.KEYCODE_TAB:
  809. if (Build.VERSION.SDK_INT >= 11) {
  810. // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD
  811. // before Android 3.0. Ignore the tab key on those devices.
  812. if (KeyEventCompat.hasNoModifiers(event)) {
  813. handled = arrowScroll(FOCUS_FORWARD);
  814. } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
  815. handled = arrowScroll(FOCUS_BACKWARD);
  816. }
  817. }
  818. break;
  819. }
  820. }
  821. return handled;
  822. }
  823. public boolean arrowScroll(int direction) {
  824. View currentFocused = findFocus();
  825. if (currentFocused == this) currentFocused = null;
  826. boolean handled = false;
  827. View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,
  828. direction);
  829. if (nextFocused != null && nextFocused != currentFocused) {
  830. if (direction == View.FOCUS_LEFT) {
  831. handled = nextFocused.requestFocus();
  832. } else if (direction == View.FOCUS_RIGHT) {
  833. // If there is nothing to the right, or this is causing us to
  834. // jump to the left, then what we really want to do is page right.
  835. if (currentFocused != null && nextFocused.getLeft() <= currentFocused.getLeft()) {
  836. handled = pageRight();
  837. } else {
  838. handled = nextFocused.requestFocus();
  839. }
  840. }
  841. } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {
  842. // Trying to move left and nothing there; try to page.
  843. handled = pageLeft();
  844. } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {
  845. // Trying to move right and nothing there; try to page.
  846. handled = pageRight();
  847. }
  848. if (handled) {
  849. playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
  850. }
  851. return handled;
  852. }
  853. boolean pageLeft() {
  854. if (mCurItem > 0) {
  855. setCurrentItem(mCurItem-1, true);
  856. return true;
  857. }
  858. return false;
  859. }
  860. boolean pageRight() {
  861. if (mCurItem < 1) {
  862. setCurrentItem(mCurItem+1, true);
  863. return true;
  864. }
  865. return false;
  866. }
  867. public void setCanvasTransformer(CanvasTransformer t) {
  868. mTransformer = t;
  869. }
  870. }

如果想要使用这个侧滑菜单的动画效果,直接替换这两个类即可。同时,并不会影响SlidingMenu的固有功能。

下面看看如何配置SlidingMenu实例。

  1. SlidingMenu sm = getSlidingMenu();
  2. sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);
  3. sm.setFadeEnabled(false);
  4. sm.setBehindScrollScale(0.25f);
  5. sm.setFadeDegree(0.25f);
  6. // 配置背景图片
  7. sm.setBackgroundImage(R.drawable.img_frame_background);
  8. // 设置专场动画效果
  9. sm.setBehindCanvasTransformer(new SlidingMenu.CanvasTransformer() {
  10. @Override
  11. public void transformCanvas(Canvas canvas, float percentOpen) {
  12. float scale = (float) (percentOpen * 0.25 + 0.75);
  13. canvas.scale(scale, scale, -canvas.getWidth() / 2,
  14. canvas.getHeight() / 2);
  15. }
  16. });
  17. sm.setAboveCanvasTransformer(new SlidingMenu.CanvasTransformer() {
  18. @Override
  19. public void transformCanvas(Canvas canvas, float percentOpen) {
  20. float scale = (float) (1 - percentOpen * 0.25);
  21. canvas.scale(scale, scale, 0, canvas.getHeight() / 2);
  22. }
  23. });

大功告成!

最后,附上Demo的下载地址。

GitHub https://github.com/sunguowei/Android-ResideMenu

CSDN资源   http://download.csdn.net/detail/manoel/7857771

百度网盘    http://pan.baidu.com/s/1jGrASui

关于这个框架后期的优化,请关注我的github。

https://github.com/sunguowei

Okay,要说的就这么多,希望能给大家带来一些帮助。

【转】仿QQ5.0侧滑菜单ResideMenu的更多相关文章

  1. 仿QQ5.0侧滑菜单

    一.概述 侧滑菜单现在已经非常流行了,目前大概有这么几种:最普通的侧滑,抽屉侧滑,QQ侧滑 注:本文来自慕课网 二.最普通的侧滑 先上图 代码如下: public class MainActivity ...

  2. 安卓开发笔记——关于开源项目SlidingMenu的使用介绍(仿QQ5.0侧滑菜单)

    记得去年年末的时候写过这个侧滑效果,当时是利用自定义HorizontalScrollView来实现的,效果如下: 有兴趣的朋友可以看看这篇文件<安卓开发笔记——自定义HorizontalScro ...

  3. DragLayout: QQ5.0侧拉菜单的新特效

    一.项目概要 1.1 项目效果如图: 1.2 需要使用到的技术   ViewDragHelper: 要实现和QQ5.0侧滑的特效,需要借助谷歌在2013年I/O大会上发布的ViewDragHelper ...

  4. 使用DrawerLayout实现QQ5.0侧拉菜单效果

    在上一篇文章中,我们介绍了怎么使用DrawerLayout来实现一个简单的侧拉菜单(使用DrawerLayout实现侧拉菜单),也就是我们常说的抽屉效果,GitHub上类似效果的实现方式非常多,实现出 ...

  5. Android 自定义View修炼-仿QQ5.0 的侧滑菜单效果的实现

    有一段时间没有写博客了,最近比较忙,没什么时间写,刚好今天有点时间, 我就分享下,侧滑菜单的实现原理,一般android侧滑的实现原理和步骤如下:(源码下载在下面最后给出哈) 1.使用ViewGrou ...

  6. 仿qq最新侧滑菜单

    为了后续对这个项目进行优化,比如透明度动画.背景图的位移动画,以及性能上的优化. 我把这个项目上传到github上面,请大家随时关注. github地址https://github.com/sungu ...

  7. 安卓开发笔记——自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)

    对于滑动菜单栏SlidingMenu,大家应该都不陌生,在市场上的一些APP应用里经常可以见到,比如人人网,FaceBook等. 前段时间QQ5.0版本出来后也采用了这种设计风格:(下面是效果图) 之 ...

  8. 仿QQ5.0以上新版本侧滑效果

    1.此效果使用了csdn大神孙国威的代码案例在此感谢附上参考博客地址: http://blog.csdn.net/manoel/article/details/39013095/#plain 2.sl ...

  9. iOS开发资源:几个类似Path 2.0侧滑菜单的效果实现

    IIViewDeckController/ViewDeck 类似 Path 2.0 的视图左右滑动的效果,可向左或者向右顺滑的滑动.支持ARC和non-ARC,默认ARC. https://githu ...

随机推荐

  1. log4j+logback+slf4j+commons-logging的关系与调试(转)

    背景     由于现在开源框架日益丰富,好多开源框架使用的日志组件不尽相同.存在着在一个项目中,不同的版本,不同的框架共存.导致日志输出异常混乱.虽然也不至于对系统造成致命伤害,但是明显可以看出,架构 ...

  2. python 架构简介(转)

    前言:   开发语言python  越来越火 ,作为开发比较火的语言,python 对网页等的支持也很好,当你想用python来写网页的时候你就要选择框架了.到底要选择呢什么样子的框架,最适合你的项目 ...

  3. robot framework-databaselibaray库使用(python)(转)

    公司做项目用到了databaselibaray,刚开始使用时碰到了很多问题,网上也查阅了很多资料终于是可以用了,现在整理记录下来,有需要的同学可随意使用: 另,本文主要是databaselibaray ...

  4. 我的MYSQL学习心得(三)

    原文:我的MYSQL学习心得(三) 我的MYSQL学习心得(三) 我的MYSQL学习心得(一) 我的MYSQL学习心得(二) 我的MYSQL学习心得(四) 我的MYSQL学习心得(五) 我的MYSQL ...

  5. DFS-leetcode Combination Sum I/I I

    深度优先搜索(DFS)它是一个搜索算法.第一次接触DFS它应该是一个二进制树的遍历内部,二叉树预订.序和后序实际上属于深度遍历-first.在本质上,深度优先搜索,遍历中则看到了更纯正的深度优先搜索算 ...

  6. 第23章 访问者模式(Visitor Pattern)

    原文 第23章 访问者模式(Visitor Pattern) 访问者模式 导读:访问者模式是我个人认为所有行为模式中最为复杂的一种模式了,这个模式可能看一遍会看不懂,我也翻了好几个例子,依然不能很好的 ...

  7. 小记 js unicode 编码解析

    原文:小记 js unicode 编码解析 var str = "\\u6211\\u662Funicode\\u7F16\\u7801"; 关于这样的数据转换为中文问题,常用的两 ...

  8. 两个div横向排列,顶端对齐的方式。

    1.左右两个div都设置为float:left,如果右边div没有设置宽度,右边div的宽度会根据div里的内容自动调整. <!DOCTYPE html PUBLIC "-//W3C/ ...

  9. Sql Server 2008R2版本中有关外键Foreign的使用

    原文:Sql Server 2008R2版本中有关外键Foreign的使用 1. 在数据库设计的过程中往往会想让2张表进行关联而使用到Foreign从而加强2张表之间的约束(如图) 以前有个问题一直没 ...

  10. UML九种图汇总

    UML视频读,该文件开始起草.我不知道如何下手啊!我想先UML九图和总结的关系,然后开始用它的文件. 首先在地图上. UML的九种图各自是:用例图.类图.对象图.状态图.活动图.协作图.序列图.组件图 ...