这两天公司比较闲,所以重新研究了一下侧滑菜单。可能是因为很多东西都是现成的吧,基本上都是可以直接拿来就能用的。但是确实感觉自己的技术方面有很多薄弱的地方,都需要去补,所以虽然有现成的框架,还是打算自己实现。
以前有听老师讲过类似的效果,大概的思路是:自定义一个view,横向排列,左边的布局设置margin为负值,加载的时候先隐藏。然后在onTouchListener中,通过移动的x的值来判断是否需要显示左边的菜单。大概是这样,有兴趣的可以自己试试。
不过这两天查看了很多资料后发现,有一种更简单的方式:重写HorizontalScrollView的onMeasure和onLayout,来实现。
public class SlidingMenu extends HorizontalScrollView {
private int ScreenWidth;//屏幕宽度
private int mMenuRightPadding;//菜单离右边的距离
private int mMenuWidth;//菜单的宽度
private TypedArray typedarray;
private int mHalfMenuWidth;
private boolean once=false;
private String TAG="TAG";
public SlidingMenu(Context context) {
super(context);
this.ScreenWidth=getResources().getDisplayMetrics().widthPixels;
}
public SlidingMenu(Context context, AttributeSet attrs) {
super(context, attrs);
this.ScreenWidth=getResources().getDisplayMetrics().widthPixels;//获取手机屏幕宽度
typedarray=context.obtainStyledAttributes(attrs,R.styleable.SlidingMenu);
//为了不让菜单铺满整个布局,所以设置一个距离。
mMenuRightPadding=typedarray.getDimensionPixelSize(
R.styleable.SlidingMenu_rightPadding, 50);
/**
*官方的解释是:回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。
*在TypedArray后调用recycle主要是为了缓存。当recycle被调用后,这就说明这个对象从现在可以被重用了。
*TypedArray 内部持有部分数组,它们缓存在Resources类中的静态字段中,这样就不用每次使用前都需要分配内存。
*/
typedarray.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置菜单的宽度和内容的宽度
*/
Log.i(TAG, "---------onMeasure-------------");
if(!once){
LinearLayout linearlayout=(LinearLayout) this.getChildAt(0);
ViewGroup menu=(ViewGroup) linearlayout.getChildAt(0);
ViewGroup content=(ViewGroup) linearlayout.getChildAt(1);
mMenuWidth=ScreenWidth-mMenuRightPadding;
mHalfMenuWidth=mMenuWidth/2;
menu.getLayoutParams().width=mMenuWidth;
content.getLayoutParams().width=ScreenWidth;
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
Log.i(TAG, "---------onLayout-------------");
if(changed)
{
this.scrollTo(mMenuWidth, 0);
once=true;
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch(ev.getAction())
{
case MotionEvent.ACTION_UP:
int upX=getScrollX();
if(upX>mHalfMenuWidth)
{
this.scrollTo(mMenuWidth,0);
}else
{
this.scrollTo(0, 0);
}
return true;
}
return super.onTouchEvent(ev);
}
}
在我们自定义view的时候,首先肯定是需要实现构造方法的。主要实现一个参数的构造方法和两个参数的构造方法。其中一个参数的构造方法,是我们如果直接在代码中实例化对象的时候需要用到的,两个参数的构造方法,是我们在xml文件中需要用到的。
在SlidingMenu这两个构造方法中,我们都获取了屏幕的宽度。在两个参数的构造方法中,并且我们在两个参数的构造方法中,获取了自定义的属性。
this.ScreenWidth=getResources().getDisplayMetrics().widthPixels;
typedarray=context.obtainStyledAttributes(attrs,R.styleable.SlidingMenu);
mMenuRightPadding=typedarray.getDimensionPixelSize(
R.styleable.SlidingMenu_rightPadding, 50);
/**
*官方的解释是:回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。
*在TypedArray后调用recycle主要是为了缓存。当recycle被调用后,这就说明这个对象从现在可以被重用了。
*TypedArray 内部持有部分数组,它们缓存在Resources类中的静态字段中,这样就不用每次使用前都需要分配内存。
*/
typedarray.recycle();
然后在onMeasure这个方法中,我们分别拿到根部局的子布局和子布局的子布局,并分别为它们设置了宽度。
然后在onLayout中只做了一件事,就是隐藏菜单栏。
this.scrollTo(mMenuWidth, 0);//隐藏菜单
最后在onTouchEvent中,判断如果在action_up的时候,得到的X坐标值如果大于菜单宽度的一半,就隐藏菜单,反之则显示。这个可能不太好理解,不过自己操作几下应该就能明白。这里需要注意的是,在判断结尾处,必须要加return true,否则会出现菜单隐藏或者出现一半的情况。
case MotionEvent.ACTION_UP:
int upX=getScrollX();
if(upX>mHalfMenuWidth)
{
this.scrollTo(mMenuWidth,0);
}else
{
this.scrollTo(0, 0);
}
return true;