可动态布局的Android抽屉之完整篇

本文来自http://blog.csdn.net/hellogv/ ,欢迎转摘,引用必须注明出处!      

       上次介绍了基础篇,讲解了自定义抽屉控件的基础实现,这次就在基础篇的基础上加入拖拉功能。拖拉功能基于GestureDetector,GestureDetector的基本使用方式不是本文介绍的重点,有兴趣的童鞋可以上网查询相关的教程。

       本文的抽屉控件相对于基础篇的抽屉控件多了以下功能:

1.支持手势拖拉

2.拖拉到一半时,可以自动展开或者收缩。
具体如下图:

 

 

本文的源码可以到这里下载:http://download.csdn.net/detail/hellogv/3642418

只贴出抽屉组件的源码,其他源文件与基础篇的一样:

public class Panel extends LinearLayout implements GestureDetector.OnGestureListener{
	
	public interface PanelClosedEvent {
		void onPanelClosed(View panel);
	}
	
	public interface PanelOpenedEvent {
		void onPanelOpened(View panel);
	}
	
	private final static int HANDLE_WIDTH=30;
	private final static int MOVE_WIDTH=20;
	private Button btnHandler;
	private LinearLayout panelContainer;
	private int mRightMargin=0;
	private Context mContext;
	private GestureDetector mGestureDetector;
	private boolean mIsScrolling=false;
	private float mScrollX;
	private PanelClosedEvent panelClosedEvent=null;
	private PanelOpenedEvent panelOpenedEvent=null;
	
	public Panel(Context context,View otherView,int width,int height) {
		super(context);
		this.mContext=context;
		
		//定义手势识别
		mGestureDetector = new GestureDetector(mContext,this);
		mGestureDetector.setIsLongpressEnabled(false);
		
		//改变Panel附近组件的属性
		LayoutParams otherLP=(LayoutParams) otherView.getLayoutParams();
		otherLP.weight=1;
		otherView.setLayoutParams(otherLP);
		
		//设置Panel本身的属性
		LayoutParams lp=new LayoutParams(width, height);
		lp.rightMargin=-lp.width+HANDLE_WIDTH;
		mRightMargin=Math.abs(lp.rightMargin);
		this.setLayoutParams(lp);
		this.setOrientation(LinearLayout.HORIZONTAL);
		
		//设置Handler的属性
		btnHandler=new Button(context);
		btnHandler.setLayoutParams(new LayoutParams(HANDLE_WIDTH,height));
		//btnHandler.setOnClickListener(handlerClickEvent);
		btnHandler.setOnTouchListener(handlerTouchEvent);
		this.addView(btnHandler);
		
		//设置Container的属性
		panelContainer=new LinearLayout(context);
		panelContainer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT));
		this.addView(panelContainer);

	}

	private View.OnTouchListener handlerTouchEvent=new View.OnTouchListener() {
		
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			if(event.getAction()==MotionEvent.ACTION_UP && //onScroll时的ACTION_UP
					mIsScrolling==true)
			{
				LayoutParams lp=(LayoutParams) Panel.this.getLayoutParams();
				if (lp.rightMargin >= (-mRightMargin/2)) {//往左超过一半
					new AsynMove().execute(new Integer[] { MOVE_WIDTH });// 正数展开
				} 
				else if (lp.rightMargin < (-mRightMargin/2)) {//往右拖拉
					new AsynMove().execute(new Integer[] { -MOVE_WIDTH });// 负数收缩
				}
			}
			return mGestureDetector.onTouchEvent(event); 
		}
	};

	/**
	 * 定义收缩时的回调函数
	 * @param event
	 */
	public void setPanelClosedEvent(PanelClosedEvent event)
	{
		this.panelClosedEvent=event;
	}
	
	/**
	 * 定义展开时的回调函数
	 * @param event
	 */
	public void setPanelOpenedEvent(PanelOpenedEvent event)
	{
		this.panelOpenedEvent=event;
	}
	
	/**
	 * 把View放在Panel的Container
	 * @param v
	 */
	public void fillPanelContainer(View v)
	{
		panelContainer.addView(v);
	}
	
	/**
	 * 异步移动Panel
	 * @author hellogv 
	 */
	class AsynMove extends AsyncTask<Integer, Integer, Void> {

		@Override
		protected Void doInBackground(Integer... params) {
			int times;
			if (mRightMargin % Math.abs(params[0]) == 0)// 整除
				times = mRightMargin / Math.abs(params[0]);
			else
				// 有余数
				times = mRightMargin / Math.abs(params[0]) + 1;

			for (int i = 0; i < times; i++) {
				publishProgress(params);
				try {
					Thread.sleep(Math.abs(params[0]));
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(Integer... params) {
			LayoutParams lp = (LayoutParams) Panel.this.getLayoutParams();
			if (params[0] < 0)
				lp.rightMargin = Math.max(lp.rightMargin + params[0],
						(-mRightMargin));
			else
				lp.rightMargin = Math.min(lp.rightMargin + params[0], 0);

			if(lp.rightMargin==0 && panelOpenedEvent!=null){//展开之后
				panelOpenedEvent.onPanelOpened(Panel.this);//调用OPEN回调函数
			}
			else if(lp.rightMargin==-(mRightMargin) && panelClosedEvent!=null){//收缩之后
				panelClosedEvent.onPanelClosed(Panel.this);//调用CLOSE回调函数
			}
			Panel.this.setLayoutParams(lp);
		}
	}

	@Override
	public boolean onDown(MotionEvent e) {
		mScrollX=0;
		mIsScrolling=false;
		return false;
	}

	@Override
	public boolean onSingleTapUp(MotionEvent e) {
		LayoutParams lp = (LayoutParams) Panel.this.getLayoutParams();
		if (lp.rightMargin < 0)// CLOSE的状态
			new AsynMove().execute(new Integer[] { MOVE_WIDTH });// 正数展开
		else if (lp.rightMargin >= 0)// OPEN的状态
			new AsynMove().execute(new Integer[] { -MOVE_WIDTH });// 负数收缩
		return false;
	}
	
	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
			float distanceY) {
		mIsScrolling=true;
		mScrollX+=distanceX;
		
		LayoutParams lp=(LayoutParams) Panel.this.getLayoutParams();
		if (lp.rightMargin < -1 && mScrollX > 0) {//往左拖拉
			lp.rightMargin = Math.min((lp.rightMargin + (int) mScrollX),0);
			Panel.this.setLayoutParams(lp);
			Log.e("onScroll",lp.rightMargin+"");
		} 
		else if (lp.rightMargin > -(mRightMargin) && mScrollX < 0) {//往右拖拉
			lp.rightMargin = Math.max((lp.rightMargin + (int) mScrollX),-mRightMargin);
			Panel.this.setLayoutParams(lp);
		}
		
		if(lp.rightMargin==0 && panelOpenedEvent!=null){//展开之后
			panelOpenedEvent.onPanelOpened(Panel.this);//调用OPEN回调函数
		}
		else if(lp.rightMargin==-(mRightMargin) && panelClosedEvent!=null){//收缩之后
			panelClosedEvent.onPanelClosed(Panel.this);//调用CLOSE回调函数
		}
		Log.e("onScroll",lp.rightMargin+"");
		
		return false;
	}
	
	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {return false;}
	@Override
	public void onLongPress(MotionEvent e) {}
	@Override
	public void onShowPress(MotionEvent e) {}

}


 

 

  • 27
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 38
    评论
Android 抽屉布局是一种常用的 UI 设计模式,允许用户通过向左或向右滑动屏幕来打开或关闭一个侧边栏菜单。以下是一个简单的实现步骤: 1. 在 XML 布局文件中添加 DrawerLayout 和两个子视图:主视图和抽屉视图。 ``` <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent"/> <ListView android:id="@+id/drawer_list" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" android:background="#ffffff"/> </android.support.v4.widget.DrawerLayout> ``` 2. 在 Java 代码中找到 DrawerLayout 和两个子视图的引用。 ``` DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); FrameLayout mainContent = (FrameLayout) findViewById(R.id.main_content); ListView drawerList = (ListView) findViewById(R.id.drawer_list); ``` 3. 创建一个 ArrayAdapter 以提供抽屉视图的数据。 ``` String[] menuItems = {"Item 1", "Item 2", "Item 3"}; ArrayAdapter<String> drawerListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, menuItems); drawerList.setAdapter(drawerListAdapter); ``` 4. 设置 ActionBarDrawerToggle 作为 DrawerLayout 的开关。 ``` ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close); drawerLayout.addDrawerListener(drawerToggle); drawerToggle.syncState(); ``` 5. 在 onOptionsItemSelected() 方法中调用 ActionBarDrawerToggle 的 onOptionsItemSelected() 方法。 ``` @Override public boolean onOptionsItemSelected(MenuItem item) { if (drawerToggle.onOptionsItemSelected(item)) { return true; } return super.onOptionsItemSelected(item); } ``` 完成以上步骤后,就可以在应用程序中添加抽屉布局了。用户可以通过向左或向右滑动屏幕来打开或关闭侧边栏菜单。
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值