[转]抽屉 Panel 研究

大家对抽屉控件的第一反应就是系统提供的 如下:

其实 该控件的原理说白了 很简单 即:

* ViewGroup 如:LinearLayout 用于放置各种View

* Button 用于 展开/收起 ViewGroup

所以该控件的大致布局应如下:

Java代码 收藏代码

  1. <Panel> 
  2. <Button /> 
  3. <LinearLayout > 
  4.     <TextView /> 
  5.     <ImageView /> 
  6. </LinearLayout> 
  7. </Panel> 

为了降低开发难度 我打算 定义 Panel extends LinearLayout

[代码 步骤]

1. 定义一些XML用到的属性

Xml代码 收藏代码

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="Panel">
  4.          //动画演变时长 
  5. <attr name="animationDuration" format="integer" />
  6.         //摆放位置 只能取下面的4个值 
  7. <attr name="position">
  8. <enum name="top" value="0" />
  9. <enum name="bottom" value="1" />
  10. <enum name="left" value="2" />
  11. <enum name="right" value="3" />
  12. </attr>
  13.         //开合是否有动画效果 
  14. <attr name="animationEnable" format="boolean" />
  15. </declare-styleable>
  16. </resources>

2. 一个标准的XML为:

Xml代码 收藏代码

  1. <org.panel.Panel
  2. android:id="@+id/leftPanel"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content"
  5. panel:position="left"
  6. panel:animationDuration="10"
  7. panel:animationEnable="true"
  8. android:layout_gravity="left"
  9. >
  10. <Button
  11. android:layout_width="wrap_content"
  12. android:layout_height="wrap_content"
  13. />
  14. <LinearLayout
  15. android:orientation="vertical"
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. >
  19. <CheckBox
  20. android:layout_width="fill_parent"
  21. android:layout_height="wrap_content"
  22. android:text="Top Panel!"
  23. android:textSize="16dip"
  24. android:textColor="#eee"
  25. android:textStyle="bold"
  26. />
  27. <EditText
  28. android:layout_width="200dip"
  29. android:layout_height="wrap_content"
  30. />
  31. <Button
  32. android:layout_width="100dp"
  33. android:layout_height="wrap_content"
  34. android:text="OK!"
  35. />
  36. </LinearLayout>
  37. </org.panel.Panel>

3. 解析该XML 并设置之

Java代码 收藏代码

  1. public Panel(Context context, AttributeSet attrs) { 
  2. super(context, attrs); 
  3.         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel); 
  4.         mDuration = a.getInteger(R.styleable.Panel_animationDuration, 750); 
  5.         mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM); 
  6.         isAnimation = a.getBoolean(R.styleable.Panel_animationEnable, true); 
  7.         a.recycle(); 
  8. //根据mPosition 决定LinearLayout的android:orientation属性
  9.         mOrientation = (mPosition == TOP || mPosition == BOTTOM)? VERTICAL : HORIZONTAL; 
  10.         setOrientation(mOrientation); 
  11. //初始化mHandle 背景图
  12.         initialHandlerBg(); 
  13.     } 

4. 设定Button 背景图

Java代码 收藏代码

  1. //设置mHandle所用背景图   
  2. private void initialHandlerBg(){ 
  3. if(mPosition == TOP){ 
  4.             mOpenedHandle = getResources().getDrawable(R.drawable.top_switcher_expanded_background); 
  5.             mClosedHandle = getResources().getDrawable(R.drawable.top_switcher_collapsed_background); 
  6.         } 
  7. else if(mPosition == BOTTOM) { 
  8.             mOpenedHandle = getResources().getDrawable(R.drawable.bottom_switcher_expanded_background); 
  9.             mClosedHandle = getResources().getDrawable(R.drawable.bottom_switcher_collapsed_background); 
  10.         } 
  11. else if(mPosition == LEFT) { 
  12.             mOpenedHandle = getResources().getDrawable(R.drawable.left_switcher_expanded_background); 
  13.             mClosedHandle = getResources().getDrawable(R.drawable.left_switcher_collapsed_background); 
  14.         } 
  15. else if(mPosition == RIGHT) { 
  16.             mOpenedHandle = getResources().getDrawable(R.drawable.right_switcher_expanded_background); 
  17.             mClosedHandle = getResources().getDrawable(R.drawable.right_switcher_collapsed_background); 
  18.         } 
  19.     } 

5. 取出其中的 ViewGroup & Button

Java代码 收藏代码

  1. //回调函数 界面初始化快结束时调用 用于得到 mHandle/mContent
  2. protected void onFinishInflate() { 
  3. super.onFinishInflate(); 
  4. //得到mHandle实例
  5.         mHandle = this.getChildAt(0); 
  6. if (mHandle == null) { 
  7. throw new RuntimeException("Your Panel must have a View - mHandle"); 
  8.         } 
  9.         mHandle.setOnClickListener(clickListener); 
  10. //得到mContent实例
  11.         mContent = this.getChildAt(1); 
  12. if (mContent == null) { 
  13. throw new RuntimeException("Your Panel must have a View - mContent"); 
  14.         } 
  15. //先移除mHandle/mContent 然后根据position决定二者的添加次序
  16.         removeView(mHandle); 
  17.         removeView(mContent); 
  18. if (mPosition == TOP || mPosition == LEFT) { 
  19.             addView(mContent); 
  20.             addView(mHandle); 
  21.         } else { 
  22.             addView(mHandle); 
  23.             addView(mContent); 
  24.         } 
  25. if (mClosedHandle != null) { 
  26.             mHandle.setBackgroundDrawable(mClosedHandle); 
  27.         } 
  28. //隐藏 mContent
  29.         mContent.setVisibility(GONE); 
  30.     } 

6. 得到ViewGroup 宽度/高度 以决定动画演变范围

   注意其位置 并非放在开始地方 因为那时候返回值都是0

Java代码 收藏代码

  1. @Override //回调函数 此时其内所有子View 宽度/高度 都已确定
  2. protected void onLayout(boolean changed, int l, int t, int r, int b) { 
  3. super.onLayout(changed, l, t, r, b); 
  4.         mContentWidth = mContent.getWidth(); 
  5.         mContentHeight = mContent.getHeight(); 
  6.         paddingTop = this.getPaddingTop(); 
  7.         paddingLeft = this.getPaddingLeft(); 
  8.     } 

7.  定义Button 响应事情 执行 开合ViewGroup

Java代码 收藏代码

  1. // 定义mHandle监听器 用于开合mContent
  2.     OnClickListener clickListener = new OnClickListener(){ 
  3. public void onClick(View v) { 
  4. // TODO Auto-generated method stub
  5. if(!isContentExpand){ 
  6.                 open(); 
  7.             } 
  8. else { 
  9.                 close(); 
  10.             } 
  11. //置反 即:开-合-开-合-开-...
  12.             isContentExpand = !isContentExpand; 
  13.         } 
  14.     }; 

8. 定义开合的回调函数 具体效果 见:setOnClickListener(OnClickListener listener)

Java代码 收藏代码

  1. //回调函数 用于监听 Panel 的开合 效果见:setOnClickLstener(OnClickListener listener)
  2. public static interface OnPanelListener { 
  3. //- open
  4. public void onPanelOpened(Panel panel); 
  5. //- close
  6. public void onPanelClosed(Panel panel); 
  7.     } 

10. 开 即: 打开ViewGroup

Java代码 收藏代码

  1. public void open(){ 
  2. if(isAnimation){ 
  3.             doAnimationOpen(); 
  4.         } 
  5. else { 
  6.             doOpen(); 
  7.         } 
  8.     } 
  9. public void doOpen(){ 
  10.         mContent.setVisibility(VISIBLE); 
  11.     } 
  12. public void doAnimationOpen(){ 
  13.         mContent.setVisibility(VISIBLE); 
  14.         post(aOpen); 
  15.     } 

11. 定义开的Animation

Java代码 收藏代码

  1. //- open
  2.     Runnable aOpen = new Runnable() { 
  3. public void run() { 
  4.             TranslateAnimation animation; 
  5. int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0; 
  6. int calculatedDuration = 0; 
  7. if(mPosition == TOP){ 
  8.                 fromYDelta = -1 * mContentHeight; 
  9.                 toXDelta = 0; 
  10.                 calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight; 
  11.             } 
  12. else if(mPosition == BOTTOM){ 
  13.                 fromYDelta = paddingTop; 
  14.                 toYDelta = fromYDelta + 1 * mContentHeight; 
  15.                 calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight; 
  16.             } 
  17. else if(mPosition == LEFT){ 
  18.                 fromXDelta = -1 * mContentWidth; 
  19.                 toXDelta = 0; 
  20.                 calculatedDuration = mDuration * Math.abs(toXDelta - fromXDelta) / mContentWidth; 
  21.             } 
  22. else if(mPosition == RIGHT){ 
  23.                 fromXDelta = paddingLeft; 
  24.                 toXDelta = fromYDelta + 1 * mContentHeight; 
  25.                 calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight; 
  26.             } 
  27.             animation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta); 
  28.             animation.setDuration(calculatedDuration); 
  29.             animation.setAnimationListener(aOListener); 
  30.             startAnimation(animation); 
  31.         } 
  32.     }; 

12. 合 即:关闭ViewGroup

Java代码 收藏代码

  1. public void close(){ 
  2. if(isAnimation){ 
  3.             doAnimationClose(); 
  4.         } 
  5. else { 
  6.             doClose(); 
  7.         } 
  8.     } 
  9. public void doClose(){ 
  10.         mContent.setVisibility(GONE); 
  11.     } 
  12. public void doAnimationClose(){ 
  13.         post(aClose); 
  14.     } 

13. 定义合的Animation

Java代码 收藏代码

  1. //- close
  2.     Runnable aClose = new Runnable() { 
  3. public void run() { 
  4.             TranslateAnimation animation; 
  5. int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0; 
  6. int calculatedDuration = 0; 
  7. if(mPosition == TOP){ 
  8.                 toYDelta = -1 * mContentHeight; 
  9.                 calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight; 
  10.             } 
  11. else if(mPosition == BOTTOM){ 
  12.                 fromYDelta = 1 *  mContentHeight; 
  13.                 toYDelta = paddingTop; 
  14.                 calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight; 
  15.             } 
  16. else if(mPosition == LEFT){ 
  17.                 toXDelta = -1 * mContentWidth; 
  18.                 calculatedDuration = mDuration * Math.abs(toXDelta - fromXDelta) / mContentWidth; 
  19.             } 
  20. else if(mPosition == RIGHT){ 
  21.             } 
  22.             animation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta); 
  23.             animation.setDuration(calculatedDuration); 
  24.             animation.setAnimationListener(aCListener); 
  25.             startAnimation(animation); 
  26.         } 
  27.     }; 

14. 定义二者Animation 的回调函数 即:结束后 更改Button背景图 通知OnPanelListener

Java代码 收藏代码

  1. //善后工作 比如:改变mHandle背景图 通知开合监听器
  2. private void postProcess() { 
  3. // to update mHandle 's background
  4. if (!isContentExpand ) { 
  5.             mHandle.setBackgroundDrawable(mClosedHandle); 
  6.         }  
  7. else { 
  8.             mHandle.setBackgroundDrawable(mOpenedHandle); 
  9.         } 
  10. // invoke listener if any
  11. if (panelListener != null) { 
  12. if (isContentExpand) { 
  13.                 panelListener.onPanelOpened(Panel.this); 
  14.             } 
  15. else { 
  16.                 panelListener.onPanelClosed(Panel.this); 
  17.             } 
  18.         } 
  19.     } 

15. emulator 运行截图:

* 先贴其布局

Xml代码 收藏代码

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:panel="http://schemas.android.com/apk/res/org.panel"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. android:orientation="vertical"
  7. >
  8. <org.panel.Panel
  9. android:id="@+id/leftPanel"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. panel:position="left"
  13. panel:animationDuration="10"
  14. panel:animationEnable="true"
  15. android:layout_gravity="left"
  16. >
  17. <Button
  18. android:layout_width="wrap_content"
  19. android:layout_height="wrap_content"
  20. />
  21. <LinearLayout
  22. android:orientation="vertical"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. >
  26. <CheckBox
  27. android:layout_width="fill_parent"
  28. android:layout_height="wrap_content"
  29. android:text="Top Panel!"
  30. android:textSize="16dip"
  31. android:textColor="#eee"
  32. android:textStyle="bold"
  33. />
  34. <EditText
  35. android:layout_width="200dip"
  36. android:layout_height="wrap_content"
  37. />
  38. <Button
  39. android:layout_width="100dp"
  40. android:layout_height="wrap_content"
  41. android:text="OK!"
  42. />
  43. </LinearLayout>
  44. </org.panel.Panel>
  45. <org.panel.Panel
  46. android:id="@+id/rightPanel"
  47. android:layout_width="wrap_content"
  48. android:layout_height="wrap_content"
  49. panel:position="right"
  50. panel:animationDuration="10"
  51. panel:animationEnable="true"
  52. android:layout_gravity="right"
  53. >
  54. <Button
  55. android:layout_width="wrap_content"
  56. android:layout_height="wrap_content"
  57. />
  58. <LinearLayout
  59. android:orientation="vertical"
  60. android:layout_width="wrap_content"
  61. android:layout_height="wrap_content"
  62. >
  63. <ImageView
  64. android:layout_width="wrap_content"
  65. android:layout_height="wrap_content"
  66. android:src="@drawable/beijing4_b"
  67. />
  68. </LinearLayout>
  69. </org.panel.Panel>
  70. <LinearLayout
  71. android:layout_width="fill_parent"
  72. android:layout_height="wrap_content"
  73. android:orientation="vertical"
  74. >
  75. <TextView
  76. android:layout_width="fill_parent"
  77. android:layout_height="wrap_content"
  78. android:textSize="16dip"
  79. android:textColor="#ddd"
  80. android:text="other area!!!!!!!!"
  81. />
  82. <Button
  83. android:id="@+id/button"
  84. android:layout_width="100dp"
  85. android:layout_height="wrap_content"
  86. android:text="Yes!"
  87. />
  88. </LinearLayout>
  89. </LinearLayout>

* 运行截图:

- 开

- 合

原文地址:https://www.cnblogs.com/GnagWang/p/2092411.html