之前写过一片文章是关于使用Android的DrawerLayout来实现侧滑菜单的——Android侧滑菜单DrawerLayout使用。
本文直接使用自定义的ViewGroup来实现Android侧滑菜单。
用到的技术: 1、Android自定义控件; 2、Android 的事件拦截onInterceptTouchEvent和触摸事件onTouchEvent处理。 3、Scroller的使用。
效果图:
不多说,马上上代码,自定义的MyViewGroup:
public class MyViewGroup extends ViewGroup {
private int mMostRecentX; // 最新的x轴偏移量
private final int MENU_SCREEN = 0; // 菜单界面
private final int MAIN_SCREEN = 1; // 主界面
private int currentScreen = MAIN_SCREEN; // 当前屏幕, 默认为: 主界面
private Scroller mScroller; // 模拟数据对象
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
/**
* 此方法是SlideMenu控件测量宽和高时回调.
* widthMeasureSpec 宽度测量规格: 整个屏幕的宽
* heightMeasureSpec 高度测量规格: 整个屏幕的高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 测量菜单的宽和高
View menuView = getChildAt(0);
menuView.measure(menuView.getLayoutParams().width, heightMeasureSpec);
// 测量主界面的宽和高
View mainView = getChildAt(1);
// 主界面的宽度是整个屏幕的宽.
mainView.measure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 布置SlideMenu中包含的子控件的位置.
* left = 0;
* top = 0;
* right = 整个屏幕的宽度;
* bottom = 整个屏幕的高度;
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 布置菜单界面的位置: left=-菜单的宽度, top=0, right=0, bottom=整个屏幕的高度
View menuView = getChildAt(0);
menuView.layout(-menuView.getMeasuredWidth(), 0, 0, b);
// 布置主界面的位置: left=0, top=0, right=整个屏幕的宽度, bottom=整个屏幕的高度;
View mainView = getChildAt(1);
mainView.layout(l, t, r, b);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mMostRecentX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getX();
// 1. 计算差值: mMostRecentX - moveX = 48 - 42 = 6;
int diff = mMostRecentX - moveX;
// 2. 限定左右边界
int currentX = getScrollX() + diff;
if(currentX < -getChildAt(0).getMeasuredWidth()) {
// 超出了左边界, 已经移动超过-240
scrollTo(-getChildAt(0).getMeasuredWidth(), 0);
} else if(currentX > 0) {
scrollTo(0, 0);
} else {
// 3. 根据差值, 使用scrollBy方法移动屏幕. scrollBy(6, 0);
scrollBy(diff, 0);
}
// 4. 需要把mMostRecentX重新赋值, 赋值为moveX. mMostRecentX = 48;
mMostRecentX = moveX;
break;
case MotionEvent.ACTION_UP:
// 取出当前x轴的偏移量
int scrollX = getScrollX();
if(scrollX > (-getChildAt(0).getMeasuredWidth() / 2)) {
// 当前应该切换到主界面
currentScreen = MAIN_SCREEN;
} else {
// 当前应该切换到菜单界面
currentScreen = MENU_SCREEN;
}
switchScreen();
break;
default:
break;
}
return true; // 完全自己处理事件
}
/**
* 根据currentScreen来切换屏幕显示的界面
*/
private void switchScreen() {
int startX = getScrollX();
int dx = 0;
if(currentScreen == MAIN_SCREEN) {
// 切换到主界面
// scrollTo(0, 0);
// 算法: 目地的值 - startX
dx = 0 - startX;
} else if(currentScreen == MENU_SCREEN) {
// scrollTo(-getChildAt(0).getMeasuredWidth(), 0);
dx = -getChildAt(0).getMeasuredWidth() - startX;
}
// 开始模拟数据了, 只模拟数据
mScroller.startScroll(startX, 0, dx, 0, Math.abs(dx) * 2);
// 重绘刷新.
invalidate(); // invalidate -> drawChild -> child.draw -> computeScroll
}
@Override
public void computeScroll() {
// 更新scrollX或者scrollY的值.
if(mScroller.computeScrollOffset()) {
// 当前正在模拟数据, 取出x轴模拟的值, 设置给scrollTo方法.
int currX = mScroller.getCurrX();
// System.out.println("currX: " + currX);
scrollTo(currX, 0);
invalidate(); // 递归
}
}
/**
* 是否显示菜单
* @return
*/
public boolean isShowMenu() {
return currentScreen == MENU_SCREEN;
}
/**
* 隐藏菜单
*/
public void hideMenu() {
currentScreen = MAIN_SCREEN;
switchScreen();
}
/**
* 显示菜单
*/
public void showMenu() {
currentScreen = MENU_SCREEN;
switchScreen();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mMostRecentX = (int) ev.getX();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) ev.getX();
// 如果移动的偏移量的距离超过了10
int diff = moveX - mMostRecentX;
if(Math.abs(diff) > 10) {
// System.out.println("横着滑动了");
return true;
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
}
复制代码
希望读者可以认真的看下注释,注释已经标注得很明白了。
使用的时候,在xml中使用,需要布局侧滑菜单栏和主界面
activity_custom_view_group:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.linwj.myapplication.CustomViewGroupActivity">
<com.example.linwj.myapplication.com.example.linwj.myapplication.view.MyViewGroup
android:id="@+id/slidemenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include layout="@layout/slidemenu_menu"/>
<include layout="@layout/slidemenu_main"/>
</com.example.linwj.myapplication.com.example.linwj.myapplication.view.MyViewGroup>
</RelativeLayout>
复制代码
slidemenu_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="240dip"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="240dip"
android:layout_height="match_parent"
android:background="@mipmap/menu_bg"
android:orientation="vertical" >
<TextView
style="@style/menu_tab_style"
android:background="#33663300"
android:drawableLeft="@mipmap/tab_news"
android:text="新闻" />
<TextView
style="@style/menu_tab_style"
android:drawableLeft="@mipmap/tab_read"
android:text="订阅" />
<TextView
style="@style/menu_tab_style"
android:drawableLeft="@mipmap/tab_local"
android:text="本地" />
<TextView
style="@style/menu_tab_style"
android:drawableLeft="@mipmap/tab_ties"
android:text="跟帖" />
<TextView
style="@style/menu_tab_style"
android:drawableLeft="@mipmap/tab_pics"
android:text="图片" />
<TextView
style="@style/menu_tab_style"
android:drawableLeft="@mipmap/tab_ugc"
android:text="话题" />
</LinearLayout>
</ScrollView>
复制代码
slidemenu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.linwj.myapplication.CustomViewGroupActivity">
<com.example.linwj.myapplication.com.example.linwj.myapplication.view.MyViewGroup
android:id="@+id/slidemenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include layout="@layout/slidemenu_menu"/>
<include layout="@layout/slidemenu_main"/>
</com.example.linwj.myapplication.com.example.linwj.myapplication.view.MyViewGroup>
</RelativeLayout>
复制代码
Activity:
public class CustomViewGroupActivity extends AppCompatActivity implements View.OnClickListener {
private MyViewGroup myViewGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_custom_view_group);
findViewById(R.id.ib_slidemenu_main_back).setOnClickListener(this);
myViewGroup = (MyViewGroup) findViewById(R.id.slidemenu);
}
@Override
public void onClick(View v) {
// 切换当前屏幕显示的状态
if(myViewGroup.isShowMenu()) {
// 显示菜单, 切换到主界面
myViewGroup.hideMenu();
} else {
// 显示主界面, 切换到菜单
myViewGroup.showMenu();
}
}
public void clickTab(View v) {
TextView tv = (TextView) v;
Toast.makeText(this, tv.getText(), Toast.LENGTH_SHORT).show();
}
}
复制代码
这就是自定义ViewGroup实现Android的侧滑菜单以及使用,希望大家喜欢!!!