大家对抽屉控件的第一反应就是系统提供的 如下:
其实 该控件的原理说白了 很简单 即:
* ViewGroup 如:LinearLayout 用于放置各种View
* Button 用于 展开/收起 ViewGroup
所以该控件的大致布局应如下:
- <Panel>
- <Button />
- <LinearLayout >
- <TextView />
- <ImageView />
- </LinearLayout>
- </Panel>
为了降低开发难度 我打算 定义 Panel extends LinearLayout
[代码 步骤]
1. 定义一些XML用到的属性
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <declare-styleable name="Panel">
- //动画演变时长
- <attr name="animationDuration" format="integer" />
- //摆放位置 只能取下面的4个值
- <attr name="position">
- <enum name="top" value="0" />
- <enum name="bottom" value="1" />
- <enum name="left" value="2" />
- <enum name="right" value="3" />
- </attr>
- //开合是否有动画效果
- <attr name="animationEnable" format="boolean" />
- </declare-styleable>
- </resources>
2. 一个标准的XML为:
- <org.panel.Panel
- android:id="@+id/leftPanel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- panel:position="left"
- panel:animationDuration="10"
- panel:animationEnable="true"
- android:layout_gravity="left"
- >
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <CheckBox
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Top Panel!"
- android:textSize="16dip"
- android:textColor="#eee"
- android:textStyle="bold"
- />
- <EditText
- android:layout_width="200dip"
- android:layout_height="wrap_content"
- />
- <Button
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="OK!"
- />
- </LinearLayout>
- </org.panel.Panel>
3. 解析该XML 并设置之
- public Panel(Context context, AttributeSet attrs) {
- super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel);
- mDuration = a.getInteger(R.styleable.Panel_animationDuration, 750);
- mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM);
- isAnimation = a.getBoolean(R.styleable.Panel_animationEnable, true);
- a.recycle();
- //根据mPosition 决定LinearLayout的android:orientation属性
- mOrientation = (mPosition == TOP || mPosition == BOTTOM)? VERTICAL : HORIZONTAL;
- setOrientation(mOrientation);
- //初始化mHandle 背景图
- initialHandlerBg();
- }
4. 设定Button 背景图
- //设置mHandle所用背景图
- private void initialHandlerBg(){
- if(mPosition == TOP){
- mOpenedHandle = getResources().getDrawable(R.drawable.top_switcher_expanded_background);
- mClosedHandle = getResources().getDrawable(R.drawable.top_switcher_collapsed_background);
- }
- else if(mPosition == BOTTOM) {
- mOpenedHandle = getResources().getDrawable(R.drawable.bottom_switcher_expanded_background);
- mClosedHandle = getResources().getDrawable(R.drawable.bottom_switcher_collapsed_background);
- }
- else if(mPosition == LEFT) {
- mOpenedHandle = getResources().getDrawable(R.drawable.left_switcher_expanded_background);
- mClosedHandle = getResources().getDrawable(R.drawable.left_switcher_collapsed_background);
- }
- else if(mPosition == RIGHT) {
- mOpenedHandle = getResources().getDrawable(R.drawable.right_switcher_expanded_background);
- mClosedHandle = getResources().getDrawable(R.drawable.right_switcher_collapsed_background);
- }
- }
5. 取出其中的 ViewGroup & Button
- //回调函数 界面初始化快结束时调用 用于得到 mHandle/mContent
- protected void onFinishInflate() {
- super.onFinishInflate();
- //得到mHandle实例
- mHandle = this.getChildAt(0);
- if (mHandle == null) {
- throw new RuntimeException("Your Panel must have a View - mHandle");
- }
- mHandle.setOnClickListener(clickListener);
- //得到mContent实例
- mContent = this.getChildAt(1);
- if (mContent == null) {
- throw new RuntimeException("Your Panel must have a View - mContent");
- }
- //先移除mHandle/mContent 然后根据position决定二者的添加次序
- removeView(mHandle);
- removeView(mContent);
- if (mPosition == TOP || mPosition == LEFT) {
- addView(mContent);
- addView(mHandle);
- } else {
- addView(mHandle);
- addView(mContent);
- }
- if (mClosedHandle != null) {
- mHandle.setBackgroundDrawable(mClosedHandle);
- }
- //隐藏 mContent
- mContent.setVisibility(GONE);
- }
6. 得到ViewGroup 宽度/高度 以决定动画演变范围
注意其位置 并非放在开始地方 因为那时候返回值都是0
- @Override //回调函数 此时其内所有子View 宽度/高度 都已确定
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- mContentWidth = mContent.getWidth();
- mContentHeight = mContent.getHeight();
- paddingTop = this.getPaddingTop();
- paddingLeft = this.getPaddingLeft();
- }
7. 定义Button 响应事情 执行 开合ViewGroup
- // 定义mHandle监听器 用于开合mContent
- OnClickListener clickListener = new OnClickListener(){
- public void onClick(View v) {
- // TODO Auto-generated method stub
- if(!isContentExpand){
- open();
- }
- else {
- close();
- }
- //置反 即:开-合-开-合-开-...
- isContentExpand = !isContentExpand;
- }
- };
8. 定义开合的回调函数 具体效果 见:setOnClickListener(OnClickListener listener)
- //回调函数 用于监听 Panel 的开合 效果见:setOnClickLstener(OnClickListener listener)
- public static interface OnPanelListener {
- //- open
- public void onPanelOpened(Panel panel);
- //- close
- public void onPanelClosed(Panel panel);
- }
10. 开 即: 打开ViewGroup
- public void open(){
- if(isAnimation){
- doAnimationOpen();
- }
- else {
- doOpen();
- }
- }
- public void doOpen(){
- mContent.setVisibility(VISIBLE);
- }
- public void doAnimationOpen(){
- mContent.setVisibility(VISIBLE);
- post(aOpen);
- }
11. 定义开的Animation
- //- open
- Runnable aOpen = new Runnable() {
- public void run() {
- TranslateAnimation animation;
- int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0;
- int calculatedDuration = 0;
- if(mPosition == TOP){
- fromYDelta = -1 * mContentHeight;
- toXDelta = 0;
- calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight;
- }
- else if(mPosition == BOTTOM){
- fromYDelta = paddingTop;
- toYDelta = fromYDelta + 1 * mContentHeight;
- calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight;
- }
- else if(mPosition == LEFT){
- fromXDelta = -1 * mContentWidth;
- toXDelta = 0;
- calculatedDuration = mDuration * Math.abs(toXDelta - fromXDelta) / mContentWidth;
- }
- else if(mPosition == RIGHT){
- fromXDelta = paddingLeft;
- toXDelta = fromYDelta + 1 * mContentHeight;
- calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight;
- }
- animation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);
- animation.setDuration(calculatedDuration);
- animation.setAnimationListener(aOListener);
- startAnimation(animation);
- }
- };
12. 合 即:关闭ViewGroup
- public void close(){
- if(isAnimation){
- doAnimationClose();
- }
- else {
- doClose();
- }
- }
- public void doClose(){
- mContent.setVisibility(GONE);
- }
- public void doAnimationClose(){
- post(aClose);
- }
13. 定义合的Animation
- //- close
- Runnable aClose = new Runnable() {
- public void run() {
- TranslateAnimation animation;
- int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0;
- int calculatedDuration = 0;
- if(mPosition == TOP){
- toYDelta = -1 * mContentHeight;
- calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight;
- }
- else if(mPosition == BOTTOM){
- fromYDelta = 1 * mContentHeight;
- toYDelta = paddingTop;
- calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight;
- }
- else if(mPosition == LEFT){
- toXDelta = -1 * mContentWidth;
- calculatedDuration = mDuration * Math.abs(toXDelta - fromXDelta) / mContentWidth;
- }
- else if(mPosition == RIGHT){
- }
- animation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);
- animation.setDuration(calculatedDuration);
- animation.setAnimationListener(aCListener);
- startAnimation(animation);
- }
- };
14. 定义二者Animation 的回调函数 即:结束后 更改Button背景图 通知OnPanelListener
- //善后工作 比如:改变mHandle背景图 通知开合监听器
- private void postProcess() {
- // to update mHandle 's background
- if (!isContentExpand ) {
- mHandle.setBackgroundDrawable(mClosedHandle);
- }
- else {
- mHandle.setBackgroundDrawable(mOpenedHandle);
- }
- // invoke listener if any
- if (panelListener != null) {
- if (isContentExpand) {
- panelListener.onPanelOpened(Panel.this);
- }
- else {
- panelListener.onPanelClosed(Panel.this);
- }
- }
- }
15. emulator 运行截图:
* 先贴其布局
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:panel="http://schemas.android.com/apk/res/org.panel"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- >
- <org.panel.Panel
- android:id="@+id/leftPanel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- panel:position="left"
- panel:animationDuration="10"
- panel:animationEnable="true"
- android:layout_gravity="left"
- >
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <CheckBox
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Top Panel!"
- android:textSize="16dip"
- android:textColor="#eee"
- android:textStyle="bold"
- />
- <EditText
- android:layout_width="200dip"
- android:layout_height="wrap_content"
- />
- <Button
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="OK!"
- />
- </LinearLayout>
- </org.panel.Panel>
- <org.panel.Panel
- android:id="@+id/rightPanel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- panel:position="right"
- panel:animationDuration="10"
- panel:animationEnable="true"
- android:layout_gravity="right"
- >
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/beijing4_b"
- />
- </LinearLayout>
- </org.panel.Panel>
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- >
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textSize="16dip"
- android:textColor="#ddd"
- android:text="other area!!!!!!!!"
- />
- <Button
- android:id="@+id/button"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="Yes!"
- />
- </LinearLayout>
- </LinearLayout>
* 运行截图:
- 开
- 合