抽屉 Panel 研究

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

 

 

 

 

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

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

* Button 用于 展开/收起 ViewGroup

 

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

 

 

 

为了降低开发难度 我打算 定义 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>

 

 

* 运行截图:

 - 开

 

- 合

 

 

完工!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值