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);
}

0 0
原创粉丝点击