效果展示:
实现方法:
实现的方式多种多样:
2.1.1 自定义ViewGroup ,处理其onTouch事件
2.1.2 FrameLayout + 手势处理类GestureDetector
2.2.3 使用Google自带的DrawerLayout 对其进行修改
2.2.4 继承自水平滚动HorizontalScrollView
代码编写
第一步:创建自定义控件类 继承ViewGroup
第二步:编写布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.wust.myslidingmenu.mySlidingMenu
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="A"
android:background="#f00"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="B"
android:background="#0f0"/>
</com.wust.myslidingmenu.mySlidingMenu>
</LinearLayout>
第三步:写逻辑代码
思路:
1、先分别获取 内容 或 菜单 这两个子代(因为子代数量有限,所以不用遍历),这一步是在 onMeasure 方法里面做的
2、获取屏幕宽高,是在构造方法里做的
3、摆放布局,是在 onLayout 方法里做的
4、根据滑动像素移动
package com.wust.myslidingmenu;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
public class mySlidingMenu extends ViewGroup {
private View mMenu;
private View mContent;
private int mScreenWidth;
private int mScreenHeight;
private int menuWidth;
private int contentWidth;
public mySlidingMenu(Context context) {
this(context,null);
}
public mySlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public mySlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取屏幕的宽高
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
mScreenHeight = metrics.heightPixels;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//定义一下自己的宽高
int width = 0;
int height = 0;
//获取菜单栏,我这里是以 TextView 为例进行讲解
mMenu = getChildAt(0);
//获取内容栏
mContent = getChildAt(1);
//测量两个子代的宽高 只有测量了才能获取子代的宽高
measureChild(mMenu,widthMeasureSpec,heightMeasureSpec);
measureChild(mContent,widthMeasureSpec,heightMeasureSpec);
//设置菜单栏 内容栏的宽度,一般菜单栏要窄一点,所以要减去一个 400
menuWidth = mMenu.getLayoutParams().width = mScreenWidth - 400;
contentWidth = mContent.getLayoutParams().width = mScreenWidth;
//设置自定义ViewGroup的宽高 这里得首先获取 屏幕的宽高
width = menuWidth + contentWidth;
height = mScreenHeight;
setMeasuredDimension(width,height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mMenu.layout(-menuWidth,0,0,mScreenHeight);
mContent.layout(0,0,contentWidth,mScreenHeight);
}
}
做到这一步,我们的基础布局差不多可以出来了
实质图解
第四步:处理人机交互
谈到人机交互,必可少的 重写 onTouch() 事件
@Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//处理手指按下事件,主要是获取一下 按下时的位置,其实我们只用关心 X 方向
mStartX = (int) event.getX();
mStartY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int endX = (int) event.getX();
//获取手指移动了多少像素
int dx = endX - mStartX;
if (dx > 0){
//手指往右滑动了,其实这里大家并不用刻意去记,写的时候自己试一下,不行就反过来呗
if (getScrollX()-dx < -menuWidth){
//如果发现滑动距离超过了 菜单栏的宽度,直接滚动到 菜单栏边界
scrollTo(-menuWidth,0);
}else {
scrollBy(-dx,0);
}
}else {
//手指往左滑了
if (getScrollX()-dx > 0){
//如果发现滑动距离超过了 内容的宽度,直接滚动到 (0,0)
scrollTo(0,0);
}else {
scrollBy(-dx,0);
}
}
break;
}
return true;
}
写到这里,你的屏幕是可以滑动了的
第五步:优化,让体验感更好
优化一:判断手指抬起时页面是否划过 二分之一,然后决定页面的关闭
简单是实现,没有用scroller
@Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
、、、
case MotionEvent.ACTION_UP:
{
if (getScrollX() < -menuWidth/2){
scrollTo(-menuWidth,0);
}else {
scrollTo(0,0);
}
}
break;
}
return true;
}
使用scroller来实现
public mySlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
、、、
//创建一个 scroller
mScroller = new Scroller(context);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
、、、
case MotionEvent.ACTION_UP:
{
if (getScrollX() < -menuWidth/2){
mScroller.startScroll(getScrollX(),0,-(getScrollX()+menuWidth),0,300);
invalidate(); //这个函数不能掉,在他会反复掉computeScroll()来计算是否到达目的坐标
}else {
mScroller.startScroll(getScrollX(),0,-getScrollX(),0,300);
invalidate();
}
}
break;
}
return true;
}
//系统自己会调用
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
invalidate();
}
}
对于scroller 的使用可以参照 Scroller与computeScroll处理滑动
优化二:缩放功能
这个地方值的注意的就是 如果你上面 选用的是 scroller 方法实现的滚动 那这个缩放 就得 在 computeScroll()中也调用一次
@Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_MOVE:
int endX = (int) event.getX();
//只要移动了就得缩放,所以缩放得放在这个地方
//首先得确定缩放因子
float scaleRate = Math.abs(getScrollX())/(float)(menuWidth);
myScale(scaleRate);
、、、
break;
}
return true;
}
private void myScale(float scaleRate) {
mMenu.setScaleX((float) (0.7 + 0.3*scaleRate));
mMenu.setScaleY((float) (0.7 + 0.3*scaleRate));
mContent.setScaleX((float) (1.0 - 0.3*scaleRate));
mContent.setScaleY((float) (1.0 - 0.3*scaleRate));
mContent.setPivotX(0);
}
//下面这个代码块是你用了scoller才需要的
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()){
float scaleRate = Math.abs(getScrollX())/(float)(menuWidth);
myScale(scaleRate);
、、、
}
}
缩放效果展示
有偿提问
如果大家觉得这篇文章帮助你了,可以支持一下。
有偿提问