Android——自定义左右菜单的实现
来源:互联网 发布:网络电视如何看央视 编辑:程序博客网 时间:2024/06/03 16:52
在本篇博客,主要介绍android中自定义左右菜单的实现
下面一步一步介绍开始实现的方法:
新建一个CustomMenu 继承自LinearLayout
public class CustomMenu extends RelativeLayout;
1.在 CustomMenu 这个类中 我们要先创建三个视图,leftMenu,rigthMenu,middleMenu,maskMenu,分别为左,中,右,以及一个蒙版视图
private LinearLayout leftMenu, rightMenu, middleMenu, maskMenu;
2.然后建立一个初始化的方法,初始以上的四个视图
public void init(Context context) { this.context = context; leftMenu = new LinearLayout(context); rightMenu = new LinearLayout(context); middleMenu = new LinearLayout(context); maskMenu = new LinearLayout(context); scroller = new Scroller(context, new DecelerateInterpolator()); leftMenu.setBackgroundColor(Color.RED); rightMenu.setBackgroundColor(Color.BLUE); middleMenu.setBackgroundColor(Color.WHITE); maskMenu.setBackgroundColor(0xaa000000); addView(leftMenu); addView(middleMenu); addView(rightMenu); addView(maskMenu); maskMenu.setAlpha(0); }
3.我们要设置四个视图的宽度和高度以及他们的位置,
通过重写onMeasure()这个函数,我们分别对以上四个子视图进行大小设置
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int measureWidth = MeasureSpec.makeMeasureSpec((int) (width * 0.6), MeasureSpec.EXACTLY); middleMenu.measure(widthMeasureSpec, heightMeasureSpec); maskMenu.measure(widthMeasureSpec, heightMeasureSpec); leftMenu.measure(measureWidth, heightMeasureSpec); rightMenu.measure(measureWidth, heightMeasureSpec); }
这里解释一下,函数传递过来的参数widthMeasureSpec和heightMeasureSpec可以看做屏幕的大小。middleMenu和maskMenu 的大小应该和传递过来的参数大小是一样,而左右菜单我们可以设置他们的宽度为中间视图的80%,通过measure()函数设置。
4,设置四个视图在猪布局中的位置onLayout():
protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); leftMenu.layout(l - leftMenu.getMeasuredWidth(), t, l, b); rightMenu.layout(l + middleMenu.getMeasuredWidth(), t, r + rightMenu.getMeasuredWidth(), b); middleMenu.layout(l, t, r, b); maskMenu.layout(l, t, r, b); }
最左边的视图leftMenu它的left应该为当前正中间视图的左边(传递过来的l)宽度为leftMenu.getMeasureWidth()处,右边在原中间视图的l处
顶部和底部一样。同理设置右边的视图rightMenu ,middleMenu和maskMenu在中间所以不用改变,直接调用layout(l,t,r,b)。
5,好了设置完了子视图的大小和位置,然而我们还不能看到效果,我们还得进行屏幕的触摸动作事件的处理 重写父类的dispatchTouchEvent()函数
首先我们要判断是不是已经按下了手指:这里通过设置一个标志量来代表是否按下
if (!isTestCompete) {
getEventType(ev);
return true;
}
当第一次开始触碰到屏幕时,我们进行一个动作类型的判断,调用getEventType(ev),在这个函数里我们判断动作并设置标志量isLeftRight判断是否左右滑动
private void getEventType(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: point.x = (int) ev.getX(); point.y = (int) ev.getY(); super.dispatchTouchEvent(ev); break; case MotionEvent.ACTION_MOVE: isTestCompete = true; int dx = (int) Math.abs(ev.getX() - point.x); int dy = (int) Math.abs(ev.getY() - point.y); if (dx >= 20 && dx > dy) { isLeftRightAction = true; } else isLeftRightAction = false; point.x = (int) ev.getX(); point.y = (int) ev.getY(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } }
用ev.getActionMasked()取得动作的整数标志,然后用switch分别处理不同的动作类型
当动作为down时,我们用一个Point记录下当前的接触点的坐标ev.getX()和ev.getY()
当动作为MOVE时,我们判断在X方向和Y方向上的坐标的改变大小来决定是上下滑动还是左右滑动,并设置一个阈值来判断滑动是否有效
如果判断是左右滑动则将isLeftRight=true;最后设置isTestCompete表示动作完成并更行记录的坐标点Point;
6.再回到dispatchTouchEvent函数中,如果动作已经完成了,那么我们就要进行屏幕的滚动(这里只处理左右滑动)
if (isLeftRight) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_MOVE: int currX = getScrollX();//取得当前滚动的水平位置 int dis_x = (int) (ev.getX() - point.x);//触摸滑动距离 int dx = currX - dis_x;//如果dis_x为正的,代表我们是从左往右滑动(拉出左菜单),那么我们要将滚动条向左滚,所以是currX-dis_x,同理,要是dis_x小于0,那么是从右向左滑动(拉出右菜单),dx=currX+abs(dis_x)=currX-dis_x; if (dx < 0)//dx小于0,要限制最多只能滚动到leftMenu的最左边,dx>0要限制滑动到右菜单的最右边 dx = Math.max(-leftMenu.getMeasuredWidth(), dx); else dx = Math.min(dx, rightMenu.getMeasuredWidth()); scrollTo(dx, 0);//调用函数滚动 point.x = (int) ev.getX();//记录点坐标位置 break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL://当滑出边界或者手离开时,我们设定如果滑动了半个菜单宽度,则完全打开菜单。 dis_x= (int) (ev.getX()-point.x); int currScrollX=getScrollX();//当前的滑动位置 if(Math.abs(currScrollX)>leftMenu.getMeasuredWidth()>>1){ if(currScrollX>0){//大于0代表向右滑动 scroller.startScroll(currScrollX,0,rightMenu.getMeasuredWidth()-currScrollX,0);//startScroll(起始X位置,起始Y位置,X滑动距离,Y滑动距离) } else scroller.startScroll(currScrollX,0,-leftMenu.getMeasuredWidth()-currScrollX,0); } else scroller.startScroll(currScrollX,0,-currScrollX,0); invalidate();//重绘 isLeftRight=false; isTestComplete=false; } } else { isTestComplete = false; isLeftRight = false; }
7.最后重写computeScroll()函数实现动画效果
public void computeScroll() { super.computeScroll(); if (!scroller.computeScrollOffset()) return; int tempX = scroller.getCurrX(); scrollTo(tempX, 0); invalidate(); }
完整代码:
public class CustomMenu extends RelativeLayout implements View.OnClickListener { private LinearLayout leftMenu, rightMenu, middleMenu,maskMenu; private Scroller scroller; private Context context; public static final int LEFT_ID = 0xaabbcc; public static final int RIGHT_ID = 0xbbaacc; public static final int MIDDLE_ID = 0xbbccaa; public CustomMenu(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public CustomMenu(Context context) { super(context); init(context); } @Override public void computeScroll() { super.computeScroll(); if (!scroller.computeScrollOffset()) return; int tempX = scroller.getCurrX(); scrollTo(tempX, 0); } private void init(Context context) { this.context = context; scroller = new Scroller(context, new DecelerateInterpolator()); leftMenu = new LinearLayout(context); middleMenu = new LinearLayout(context); rightMenu = new LinearLayout(context); maskMenu=new LinearLayout(context); leftMenu.setBackgroundColor(Color.RED); rightMenu.setBackgroundColor(Color.BLUE); middleMenu.setBackgroundColor(Color.WHITE); maskMenu.setBackgroundColor(0xaa000000); middleMenu.setOnClickListener(this); leftMenu.setId(LEFT_ID); middleMenu.setId(MIDDLE_ID); rightMenu.setId(MIDDLE_ID); addView(middleMenu); addView(leftMenu); addView(rightMenu); addView(maskMenu); maskMenu.setAlpha(0); measureX=middleMenu.getMeasuredWidth(); measureY=middleMenu.getMeasuredHeight(); } int measureX=0; int measureY=0; @Override public void scrollTo(int x, int y) { super.scrollTo(x, y); int cur=Math.abs(getScrollX()); float scale=cur/(float)leftMenu.getMeasuredWidth(); maskMenu.setAlpha(scale); middleMenu.measure((int) (measureX*(1-scale)), (int) (measureY*(1-scale))); maskMenu.measure((int) (measureX*(1-scale)), (int) (measureY*(1-scale))); invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int realWidth = MeasureSpec.makeMeasureSpec((int) (width * 0.7), MeasureSpec.EXACTLY); leftMenu.measure(realWidth, heightMeasureSpec); rightMenu.measure(realWidth, heightMeasureSpec); middleMenu.measure(widthMeasureSpec, heightMeasureSpec); maskMenu.measure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); leftMenu.layout(l - leftMenu.getMeasuredWidth(), t, l, b); rightMenu.layout(l + middleMenu.getMeasuredWidth(), t, r + rightMenu.getMeasuredWidth(), b); middleMenu.layout(l, t, r, b); maskMenu.layout(l, t, r, b); } private boolean isTestCompete = false; @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!isTestCompete) { getEventType(ev); return true; } if (isLeftRightAction) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_MOVE: int currScorll = getScrollX(); int dis_x = (int) (ev.getX() - point.x); int expectX = -dis_x + currScorll; int finalX = 0; if (expectX < 0) { finalX = Math.max(expectX, -leftMenu.getMeasuredWidth()); } else { finalX = Math.min(expectX, leftMenu.getMeasuredWidth()); } scrollTo(finalX, 0); point.x = (int) ev.getX(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: currScorll = getScrollX(); if (Math.abs(currScorll) > leftMenu.getMeasuredWidth() * 0.3) { if (currScorll < 0) scroller.startScroll(currScorll, 0, -leftMenu.getMeasuredWidth() - currScorll, 0); else scroller.startScroll(currScorll, 0, leftMenu.getMeasuredWidth() - currScorll, 0); } else { scroller.startScroll(currScorll, 0, -currScorll, 0); } invalidate(); isLeftRightAction = false; isTestCompete = false; break; } } else { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: isTestCompete = false; isLeftRightAction = false; } } return super.dispatchTouchEvent(ev); } private boolean isLeftRightAction = false; private Point point = new Point(); private void getEventType(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: point.x = (int) ev.getX(); point.y = (int) ev.getY(); super.dispatchTouchEvent(ev); break; case MotionEvent.ACTION_MOVE: isTestCompete = true; int dx = (int) Math.abs(ev.getX() - point.x); int dy = (int) Math.abs(ev.getY() - point.y); if (dx >= 20 && dx > dy) { isLeftRightAction = true; } else isLeftRightAction = false; point.x = (int) ev.getX(); point.y = (int) ev.getY(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } } @Override public void onClick(View v) { if (getScrollX()==Math.abs(middleMenu.getMeasuredWidth() - leftMenu.getMeasuredWidth())) { int currScrollX = getScrollX(); scroller.startScroll(currScrollX, 0, -currScrollX, 0); Toast.makeText(context, "Onclick", Toast.LENGTH_SHORT).show(); invalidate(); } }}
主函数中的onCreate函数中加载视图:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CustomMenu customMenu=new CustomMenu(this);
setContentView(customMenu);
}
- Android——自定义左右菜单的实现
- Android 自定义左右菜单
- Android项目开发实战—自定义左右菜单
- Android之——自定义下拉菜单的实现
- Android自定义左右菜单(侧滑栏)
- Android高仿QQ左右滑动菜单的效果实现方式之安卓菜单左右滑动效果实现方式
- android自定义控件实现左右划出菜单并添加点击事件
- Android UI开发第二十七篇——实现左右划出菜单
- Android UI开发第二十七篇——实现左右划出菜单
- Android UI开发第二十七篇——实现左右划出菜单
- Android UI开发第二十七篇——实现左右划出菜单
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- JAVA Collection 常用集合 源码解析
- sql 除法中如何防止被除数为0,以及SUM和舍入为指定的小数位数函数
- HDFS Append File
- android平台的视频播放库LanSoSdk发布地址.
- struts2拦截器
- Android——自定义左右菜单的实现
- C语言版字符串查找函数,字符串中查找子串
- PLC项目代码说明
- 第17章 输入输出和文件
- 正向最大匹配中文分词算法
- odex转dex
- docker使用总结
- crontab的正确使用
- c++ primer第五版(中文)习题答案 第三章第一节-命名空间using声明