一、概述
侧滑的实现效果有很多,有谷歌官方的Drawerlayout实现侧滑,不过今天要介绍的是用HorizontalScrollview实现侧滑效果,其实HorizontalScrollview实现侧滑非常简单,我们只要自定义一个View然后继承HorizontalScrollview就可以的,我先说一下思路,首先我们要在HorizontalScrollview中放入两个布局一个是要拉出来的布局,一个是我们主界面的布局。 第二我们先要通过屏幕的宽度和我们设置的与屏幕右边的距离来计算出来滑出菜单的距离。第三我们在这些都计算出来之后我们首先需要设置一下默认滑动的距离,这个距离也就是菜单的宽度。然后我们还需要通过监听滑动的距离来判断这个菜单是否需要滑动过去,如果滑动距离超过了菜单宽度的一半那么我们就需要滑动过去,如果没超过一半就退回。好了下面我们一步步实现。
二、实现
1、首先看一下布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:andya="http://schemas.android.com/apk/res/com.transfar.andya.sildelayoutdemo"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@drawable/bg1">
<com.transfar.andya.sildelayoutdemo.ui.SlidingLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:id="@+id/sll_slide_menu"
andya:slidepadding="200dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<include layout="@layout/layout_menu" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/qq" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="toggleMenu"
android:text="切换菜单"
/>
</LinearLayout>
</LinearLayout>
</com.transfar.andya.sildelayoutdemo.ui.SlidingLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1"
android:gravity="center" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2"
android:gravity="center" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="3"
android:gravity="center" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="4"
android:gravity="center" />
</LinearLayout>
</RelativeLayout>
此段代码应该很好理解 我们重写了一个view,Sildinglayout有一个属性是slidepadding,这个值的含义就是主界面的左边距和屏幕的右边的距离。这个值是可以设置的,所以我们可以动态设置侧滑出来的距离。关于此值的用法后面我会降到。我们还需要在value中新增一个attr属性,然后在去设置一下slidepadding看一下attr文件的代码。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="slidepadding" format="dimension"/>
<declare-styleable name="SlidingLayout">
<attr name="slidepadding"/>
</declare-styleable>
</resources>
第二步的实现,第二步我们首先要获取我们设置的值,然后通过这个值去计算侧滑的宽度,下面代码:
mScreenWidth = ScreenUtils.getScreenWidth(context); //这个是写的一个工具类获取屏幕的宽度
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.SlidingLayout, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.SlidingLayout_slidepadding:
// 默认50
mMenuRightPadding = a.getDimensionPixelSize(attr, //这个字段就是我们设置的字段
(int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 50f,
getResources().getDisplayMetrics()));// 默认为10DP
break;
}
}
a.recycle();
此处的代码应该很好理解的,但是细心的人应该发现这个地方的扩展性是非常好的,我们还可以在attr中定义一些新的属性,然后我们可以在上面的代码中去获取属性的值,这也就是这个地方为什么要去写一个switch的原因。那么我们第二步的工作基本上也算完成了侧滑的宽度就用屏幕的宽度减去mMenuRigthPadding就可以了。
其实第三步相对简单了很多,相关的值我们都得到了下面我们只需在onMeasure中去设置一下我们计算得到的值就可以的了下面看代码:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
/**
* 显示的设置一个宽度
*/
if (!once)
{
LinearLayout wrapper = (LinearLayout) getChildAt(0); //此句话是获取SlideLayout的第一个子孩子
mMenu = (ViewGroup) wrapper.getChildAt(0); //这个是获取菜单布局
mContent = (ViewGroup) wrapper.getChildAt(1); //这个是获取主布局
mMenuWidth = mScreenWidth - mMenuRightPadding;
mHalfMenuWidth = mMenuWidth / 2;
mMenu.getLayoutParams().width = mMenuWidth;
mContent.getLayoutParams().width = mScreenWidth;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
此段代码我就不做过多的解释了,下面我们就需要设置一下位置了,设置位置也是非常简单的,就一句话就可以实现的看代码:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
if (changed)
{
// 将菜单隐藏
this.scrollTo(mMenuWidth, 0);
once = true; //此字段是判断我们的SlideLayout有没有生成,如果生成了那么就不要在去画了
}
}
最后我们要去监听一下用户是否滑动了侧滑宽度的一半我们重写 onTouchEvent监听一下滑动的距离就可以了,下面看代码:
@Override
public boolean onTouchEvent(MotionEvent ev)
{
int action = ev.getAction();
switch (action)
{
// Up时,进行判断,如果显示区域大于菜单宽度一半则完全显示,否则隐藏
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
if (scrollX > mHalfMenuWidth)
{
this.smoothScrollTo(mMenuWidth, 0);
isOpen = false;
} else
{
this.smoothScrollTo(0, 0);
isOpen = true;
}
return true;
}
return super.onTouchEvent(ev);
}
这样就完成了我们基本的流程,我们还可以在主界面设置一个按钮来对菜单进行开关,通过几句话就可以完成侧滑的,开的时候就向左滑动,关的时候就向右滑动就可以了看代码:
this.smoothScrollTo(0, 0); //开
this.smoothScrollTo(mMenuWidth, 0); //关
好了这基本就全都完成了主流程,看一下全部代码:
public class SlidingLayout extends HorizontalScrollView
{
/**
* 屏幕宽度
*/
private int mScreenWidth;
/**
* dp
*/
private int mMenuRightPadding;
/**
* 菜单的宽度
*/
private int mMenuWidth;
private int mHalfMenuWidth;
private boolean isOpen;
private boolean once;
private ViewGroup mMenu;
private ViewGroup mContent;
public SlidingLayout(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public SlidingLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
// WindowManager wm = (WindowManager) context
// .getSystemService(Context.WINDOW_SERVICE);
// DisplayMetrics outMetrics = new DisplayMetrics();
// wm.getDefaultDisplay().getMetrics(outMetrics);
// mScreenWidth = outMetrics.widthPixels;
mScreenWidth = ScreenUtils.getScreenWidth(context);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.SlidingLayout, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.SlidingLayout_slidepadding:
// 默认50
mMenuRightPadding = a.getDimensionPixelSize(attr,
(int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 50f,
getResources().getDisplayMetrics()));// 默认为10DP
break;
}
}
a.recycle();
}
public SlidingLayout(Context context)
{
this(context, null, 0);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
/**
* 显示的设置一个宽度
*/
if (!once)
{
LinearLayout wrapper = (LinearLayout) getChildAt(0);
mMenu = (ViewGroup) wrapper.getChildAt(0);
mContent = (ViewGroup) wrapper.getChildAt(1);
mMenuWidth = mScreenWidth - mMenuRightPadding;
mHalfMenuWidth = mMenuWidth / 2;
mMenu.getLayoutParams().width = mMenuWidth;
mContent.getLayoutParams().width = mScreenWidth;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
if (changed)
{
// 将菜单隐藏
this.scrollTo(mMenuWidth, 0);
once = true;
}
}
@Override
public boolean onTouchEvent(MotionEvent ev)
{
int action = ev.getAction();
switch (action)
{
// Up时,进行判断,如果显示区域大于菜单宽度一半则完全显示,否则隐藏
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
if (scrollX > mHalfMenuWidth)
{
this.smoothScrollTo(mMenuWidth, 0);
isOpen = false;
} else
{
this.smoothScrollTo(0, 0);
isOpen = true;
}
return true;
}
return super.onTouchEvent(ev);
}
/**
* 打开菜单
*/
public void openMenu()
{
if (isOpen)
return;
this.smoothScrollTo(0, 0);
isOpen = true;
}
/**
* 关闭菜单
*/
public void closeMenu()
{
if (isOpen)
{
this.smoothScrollTo(mMenuWidth, 0);
isOpen = false;
}
}
/**
* 切换菜单状态
*/
public void toggle()
{
if (isOpen)
{
closeMenu();
} else
{
openMenu();
}
}
}
其实我们还可以通过监听滑动的距离来做很多事情,先看一下效果图:
从这张效果图中我们可以看到主窗口的长度是改变的,然后其实滑动过程中可以设置Menu的透明度也是在改变的,所以我们可以通过监听滑动的距离来动态改变主界面的高度,还有Menu的透明度的,通过网上的大神我知道HorizentorScrollview的
protected void onScrollChanged(int l, int t, int oldl, int oldt)
这个方法中的l就是活动的距离,所以我们可以计算出来窗口的高度是如何改变的,
窗口的高度是从1到0.7,然后平移的话是从0到mMenuWidth
,所有我们可以计算出来窗口的高度比率是
,所有我们可以计算出来窗口的高度比率是
(1 - l/mMenuWidth * 0.3) + 0.7;
透明度的变化是从0.6到1然后比率我们也可以算出来:
l/mMenuWidth * 0.3 * 0.4 + 0.6;所以我们可以得到下面的程序:
@Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); float scale = l * 1.0f / mMenuWidth; ViewHelper.setAlpha(mMenu, 0.6f + 0.4f * (1 - l * 1.0f / mMenuWidth)); //这个地方是需要导入包的 ViewHelper.setScaleY(mContent, 0.7f + (l * 1.0f / mMenuWidth) * 0.3f); }直接把这个代码加入我们自定义view中就可以实现效果了。
总结:好了这样基本就能实现上面的效果了,今天的侧滑就说到这里了,然后这里存在一个问题,就是如果HorizontalScrollview设置背景的话会卡顿,而且是有些图片卡顿,存色的图片不卡顿,然后在API17的时候也不会卡顿,我用的是API24,如果有大神指导为什么请留言指导一下谢谢了。