前言

如今安卓开发用抽屉的APP基本上很少见了,并不常用,无聊突然翻到以前自己写的自定义抽屉,特此来跟大家详解一下


介绍

实现原理:自定义继承自分层布局,使用事件分发,根据手指滑动的方向和距离进行判断抽屉打开的方向和位置


使用

1. 系统提供的抽屉框架包的使用

public class MainActivity extends AppCompatActivity implements View.OnDragListener, DrawerLayout.DrawerListener, View.OnClickListener {private DrawerLayout activity_main;//GOOGLE 框架包private RelativeLayout rl_left;//  左边抽屉private ListView lv_left;// ListViewprivate List<String> list = new ArrayList<>();private Button button;//开启左边抽屉@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initList();initView();}private void initList() {for (int i = 0; i < 30; i++) {list.add("sasf" + i);}}private void initView() {activity_main = (DrawerLayout) findViewById(R.id.activity_main);rl_left = (RelativeLayout) findViewById(R.id.rl_left);lv_left = (ListView) findViewById(R.id.lv_left);button = (Button) findViewById(R.id.button);lv_left.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, list));//给左边菜单写入数据activity_main.setScrimColor(Color.TRANSPARENT);activity_main.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);activity_main.setDrawerListener(this);button.setOnClickListener(this);}//当抽屉正在滑动的时候@Overridepublic void onDrawerSlide(View drawerView, float slideOffset) {//获取第一个主页面的布局View mContext = activity_main.getChildAt(0);//获取滑动的抽屉的布局View draw = drawerView;float scale = 1 - slideOffset;// 意思就是  如果不滑动  scale 是1 , 没有发生变化,滑动后 会缩小float suofang = 0.8f + scale * 0.2f;//测量 抽屉的宽度int drawWidth = draw.getMeasuredWidth();//主页区域ViewHelper.setScaleX(mContext, suofang);ViewHelper.setScaleY(mContext, suofang);//抽屉区域ViewHelper.setScaleX(draw, 0.7f + slideOffset * 0.3f);ViewHelper.setScaleY(draw, 0.7f + slideOffset * 0.3f);ViewHelper.setAlpha(draw, 0.6f + slideOffset * 0.4f);//如果两个抽屉的话 可以对每一个抽屉  设置一个TAG  然后判断ViewHelper.setTranslationX(mContext, drawWidth * slideOffset);}@Overridepublic void onDrawerOpened(View drawerView) {}//当抽屉关闭的时候会调用@Overridepublic void onDrawerClosed(View drawerView) {activity_main.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);}@Overridepublic void onDrawerStateChanged(int newState) {}@Overridepublic boolean onDrag(View v, DragEvent event) {return false;}@Overridepublic void onClick(View v) {activity_main.openDrawer(Gravity.LEFT);activity_main.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);}
}

2. 自定义 继承 FrameLayout 布局

public class SildingView extends FrameLayout {private boolean IsFirst=true;//第一次进入的标志private boolean IsSping=false;//抽屉菜单打开关闭的标志----》默认情况下关闭的private PointF pf=new PointF();//记录坐标private PointF pf1=new PointF();private LinearLayout mBootomlinear;//底层的布局private LinearLayout mToplinear;//顶层的布局private int maxWidth=0;//抽屉打开的最大宽度public SildingView(Context context, AttributeSet attrs) {super(context, attrs);initView();}private void initView() {//实列化顶部底部布局mBootomlinear=new LinearLayout(getContext());mToplinear=new LinearLayout(getContext());//设置线性方向-垂直mBootomlinear.setOrientation(LinearLayout.VERTICAL);mToplinear.setOrientation(LinearLayout.VERTICAL);//方便查看设置背景颜色mBootomlinear.setBackgroundColor(Color.BLACK);mToplinear.setBackgroundColor(Color.WHITE);}//重写onmesure方法,获取底部布局的最大宽度@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取view的宽度----》设置底部的宽度if (IsFirst) {maxWidth=(int) (getMeasuredWidth()*0.7);mBootomlinear.setLayoutParams(new FrameLayout.LayoutParams(maxWidth, FrameLayout.LayoutParams.MATCH_PARENT));mToplinear.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));//将布局添加到自定义里面addView(mBootomlinear);//先添加哪个就在最底层addView(mToplinear);}IsFirst=false;}//设置底部linear布局的方法public void setBootom(View v){//给要再bootom里面添加的布局设置其在父容器所占位置的宽高属性v.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));//将布局添加到底部linear中mBootomlinear.addView(v);}public void setTop(View v){v.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));mToplinear.addView(v);}//底层事件的处理:return :1.true :自己处理了,不往下发 2.return super.dispatchtouchenvent(ev),交给系统自己处理-----》往下发@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {if (ev.getAction()==MotionEvent.ACTION_DOWN) {//记录手指的坐标pf.x=ev.getX();pf.y=ev.getY();pf1.x=ev.getX();pf1.y=ev.getY();}else if (ev.getAction()==MotionEvent.ACTION_MOVE) {//获取当前手指滑动后的坐标int x=(int) ev.getX();int y=(int) ev.getY();//计算手指滑动后的坐标距离: disX  disYint disX=(int) (x-pf.x);//x轴移动的距离int disY=(int) (y-pf.y);//y轴移动的距离//根据正余玹定理来判断, 水平滑动或者是垂直滑动if (Math.abs(disX)/2-Math.abs(disY)>0) {//不做处理}else {//垂直状态--》抽屉应该关闭//TODO 通过抽屉的开关来判断上层可否移动if (!IsSping) {return super.dispatchTouchEvent(ev);}else {return true;}}//设置一个边界值:防止手指按下出现抽屉抖动的情况if (Math.abs(disX)<10) {return super.dispatchTouchEvent(ev);}//根据手指滑动的x轴移动的距离的正负,判断抽屉打开的方向if (disX>0) {//FrameLayout.LayoutParams lp=(LayoutParams) mToplinear.getLayoutParams();//判断左边距超过最大边距。将最大边距设置给滑动 距离if (lp.leftMargin>=maxWidth) {disX=maxWidth;IsSping=true;//抽屉开启}lp.leftMargin=disX;//将移动的距离设置给左边距lp.rightMargin=-disX;mToplinear.setLayoutParams(lp);//将属性设置给顶部布局}else if (disX<0) {//获取到顶部布局的属性lpFrameLayout.LayoutParams lp=(LayoutParams) mToplinear.getLayoutParams();if (lp.leftMargin<=0) {disX=0;IsSping=false;}lp.leftMargin=lp.leftMargin-Math.abs(disX);lp.rightMargin=-lp.leftMargin;mToplinear.setLayoutParams(lp);pf.x=x;//将移动后的坐标赋值给初始坐标,解决再次移动的问题}requestLayout();//刷新界面return true;}else if (ev.getAction()==MotionEvent.ACTION_UP) {//区分是点击还是滑动int disX=(int) Math.abs(ev.getX()-pf1.x);if (disX>10) {//以底部linear的宽度的一半为分割线,超过分割线,手指抬起,手指抬起,抽屉自动打开或关闭FrameLayout.LayoutParams lp=(LayoutParams) mToplinear.getLayoutParams();if (lp.leftMargin>maxWidth/2) {//抽屉自动打开lp.leftMargin=maxWidth;lp.rightMargin=-maxWidth;IsSping=true;}else {//抽屉关闭lp.leftMargin=0;lp.rightMargin=0;IsSping=false;}mToplinear.setLayoutParams(lp);requestLayout();return true;//自己处理}}return super.dispatchTouchEvent(ev);}
}

3. 系统提供的横向 滑动的 布局 HorizontalScrollView

public class MySlidingMenu extends HorizontalScrollView {// 屏幕宽度 单位:pxprivate int ScreenWidth;// 内容区域宽度private int contentWidth;// 菜单宽度private int menuWidth;// 菜单一半的宽度private int halfmenuWidth;// 菜单的右边距private int rightPadding;private boolean isMesure;// 是否打开菜单private boolean isOpen;public MySlidingMenu(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);//用来添加viewWindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//获取手机分辨率DisplayMetrics outMetrics = new DisplayMetrics();//获取屏幕的大小wm.getDefaultDisplay().getMetrics(outMetrics);//widthPixels是宽度方向上的像素点的个数。ScreenWidth = outMetrics.widthPixels;//自定义属性TypedArray ta = context.getTheme().obtainStyledAttributes(attrs,R.styleable.MySlidingMenu, defStyle, 0);int count =ta.getIndexCount();for(int i=0;i<count;i++){int attr=ta.getIndex(i);Log.e("attr", " "+attr);switch(attr){case R.styleable.MySlidingMenu_rightPadding://把dp转化为pxrightPadding=ta.getDimensionPixelOffset(attr,(int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 200,getResources().getDisplayMetrics()));break;}}}public MySlidingMenu(Context context) {this(context, null, 0);}public MySlidingMenu(Context context, AttributeSet attrs) {this(context, attrs,0);//      WindowManager wm = (WindowManager) context
//              .getSystemService(Context.WINDOW_SERVICE);
//
//      DisplayMetrics outMetrics = new DisplayMetrics();
//
//      wm.getDefaultDisplay().getMetrics(outMetrics);
//      ScreenWidth = outMetrics.widthPixels;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (!isMesure) {// 避免 Measure方法多次调用isMesure = true;LinearLayout wrapper = (LinearLayout) getChildAt(0);ViewGroup menu = (ViewGroup) wrapper.getChildAt(0);ViewGroup content = (ViewGroup) wrapper.getChildAt(1);
//          rightPadding = (int) TypedValue.applyDimension(
//                  TypedValue.COMPLEX_UNIT_DIP, 100, content.getResources()
//                          .getDisplayMetrics());//屏幕宽度-菜单的右边距 = 菜单的宽度menuWidth = ScreenWidth - rightPadding;halfmenuWidth = menuWidth / 2;//获取菜单的布局属性获得菜单的宽度menu.getLayoutParams().width = menuWidth;//主布局的宽度 = 屏幕的宽度content.getLayoutParams().width = ScreenWidth;}} @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);if (changed) {//(让ScrollView滚动到(menuWidth, 0)的位置,也就是刚好显示主视图)不带效果的滑动this.scrollTo(menuWidth, 0);}}@Overridepublic boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_UP://就是当前view的左上角相对于母视图的左上角的X轴偏移量。int scrollX = getScrollX();if (scrollX > halfmenuWidth) {this.smoothScrollTo(menuWidth, 0);} else {this.smoothScrollTo(0, 0);}return true;}return super.onTouchEvent(ev);}public void openDraw() {if (isOpen) {return;}//让ScrollView滚动到(0,0)的位置this.smoothScrollTo(0,0);isOpen=true;}public void close() {if (isOpen) {//让ScrollView滚动到(menuWidth,0)的位置//带效果的滑动this.smoothScrollTo(menuWidth,0);isOpen=false;}}/*可以设置一个按钮控制抽屉的开关
*/public void toggle(){if(isOpen){close();}else{openDraw();}}
}

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources><attr
        name="rightPadding" format="dimension"/><declare-styleable name="MySlidingMenu"><attr name="rightPadding" /></declare-styleable></resources>

好了,了解一下,现在抽屉已经使用不多了

Android 抽屉用法相关推荐

  1. 【转】 Android - LayoutInflate用法

    [转自]http://blog.csdn.net/scut1135/article/details/7055461 通俗的说,inflate就相当于将一个xml中定义的布局找出来. 因为在一个Acti ...

  2. android抽屉实现不同之处

    相关链接: Android 抽屉效果Demo http://www.eoeandroid.com/thread-203886-1-1.html Android效果之 抽屉效果 http://www.e ...

  3. Android抽屉(SlidingDrawer --类似android通知栏下拉效果)

    Android抽屉(SlidingDrawer)的实现发 - 红黑联盟 http://www.2cto.com/kf/201301/182507.html 可动态布局的Android抽屉之基础 htt ...

  4. Android 抽屉效果Demo

    2019独角兽企业重金招聘Python工程师标准>>> Android 抽屉效果Demo. 转载:http://www.adobex.com/android/source/detai ...

  5. MTK驱动(77)---Android getevent用法

    Android getevent用法 getevent命令用法如下: Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [ ...

  6. Android inflater用法

    Android inflater用法 简介 具体作用 获取layoutInflater的三种方式 源码查看 返回值结果 注意 简介 在实际的开发中layoutInflater这个类的主要的主要的作用类 ...

  7. android plurals用法

    参考 android plurals用法 对一个给定的语言和数字来说,决定使用哪一个case的规则是很复杂的,所以android提供了方法getQuantityString(),它可以用来为你选择合适 ...

  8. android plurals用法(单复数)

    0.相关文章 android plurals用法 Android中的string资源占位符及Plurals string 1.使用 对一个给定的语言和数字来说,决定使用哪一个case的规则是很复杂的, ...

  9. android 4.3抽屉先过,android抽屉

    Android开发之多方向抽屉 2011年7月20日11:37 来源:Android中文网 我有话说(0) 在android上要实现类似Launch的抽屉效果,大家一定首先会想起SlidingDraw ...

  10. Android.mk 用法介绍

    一个Android.mk file用来向编译系统描述你的源代码.具体来说:该文件是GNU Makefile的一小部分,会被编译系统解析一次或多次.你可以在每一个Android.mk file中定义一个 ...

最新文章

  1. angularjs-ngModel 控制页面的宽度
  2. 【结果很简单,过程很艰辛】记阿里云Ons消息队列服务.NET接口填坑过程
  3. MapReduce-Combiner规约-原理分析
  4. chrome postman插件_一款 Postman 的开源替代品: Postwoman
  5. Python操作Jira提交BUG
  6. python popular sites
  7. 融云发送图片消息_融云 SDK 消息结构详解
  8. 快狗打车上市:市值超130亿港元 姚劲波与陈小华收获IPO
  9. 计算机网络:计算路由表下一跳
  10. 移动端html5广告的优势,移动端H5广告的互动类型探析
  11. 如何在PB数据窗口中设置数据窗口的更新属性
  12. python课程改进建议_关于Python课程的思考和意见
  13. 年龄和收入对数的线性回归_金融计算收益率的时候为什么大部分用对数收益率 (Log Return) 而不是用算数收益率?...
  14. c语言五子棋学年论文,基于c语言五子棋小游戏生本科论文.doc
  15. android仿IT之家、炫酷水波纹、Kotlin MVP项目、后台模拟点击、蜂巢迷宫小游戏等源码...
  16. Cadence Allegro PCB设计88问解析(二) 之 Allegro中Artwork层复用(导入导出)
  17. 3D游戏引擎系列十一
  18. java程序员那些提升_Java程序员如何进行自我提升?
  19. hdfs datanode 清除回收站的命令
  20. 使用sqlyog导入数据表到MySql中

热门文章

  1. 使用JAVA编程实现多人聊天室(多线程实践)
  2. eBay以2.4亿美元收购移动支付服务商Zong
  3. 单片机 TDA8023 读 ic 卡 smrat card sync_card
  4. C++默认参数(缺省参数)应该写在哪里,声明还是定义里
  5. 2017年苹果开发者账号申请——账号VISA卡支付流程
  6. c语言求众数,众数求c++程序
  7. 密码学的发展(第二篇:恩尼格码机)
  8. AIR应用:二维码批量生成
  9. Python:寻找回文素数
  10. 求急!!谁会写接口测试,拜托了各位大佬,跪求!!我感激不尽,好好报答他