Android 实现形态各异的双向侧滑菜单 自定义控件来袭(1)


3、菜单在内容之下的双向侧滑


凑合看下,文章最后会提供源码下载,大家可以安装体验一下~

所有的代码的内容区域都是一个ListView,两侧菜单都包含按钮,基本的冲突都检测过~当然如果有bug在所难免,请直接留言;如果你解决了某些未知bug,希望你也可以留言,或许可以帮助到其他人

下面就开始我们的代码了。

3、代码是最好的老师

==========

1、布局文件


既然是双向菜单,那么我们的布局文件是这样的:

<com.zhy.view.BinarySlidingMenu xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:tools=“http://schemas.android.com/tools”

xmlns:zhy=“http://schemas.android.com/apk/res/com.zhy.zhy_bin_slidingmenu02”

android:id=“@+id/id_menu”

android:layout_width=“wrap_content”

android:layout_height=“fill_parent”

android:scrollbars=“none”

zhy:rightPadding=“100dp” >

<LinearLayout

android:layout_width=“wrap_content”

android:layout_height=“fill_parent”

android:orientation=“horizontal” >

<LinearLayout

android:layout_width=“fill_parent”

android:layout_height=“fill_parent”

android:background=“@drawable/eee”

android:gravity=“center”

android:orientation=“horizontal” >

<ListView

android:id=“@android:id/list”

android:layout_width=“fill_parent”

android:layout_height=“fill_parent” >

</com.zhy.view.BinarySlidingMenu>

最外层是我们的自定义的BinarySlidingMenu,内部一个水平方向的LinearLayout,然后是左边的菜单,内容区域,右边的菜单布局~~

关键就是我们的BinarySlidingMenu

2、BinarySlidingMenu的构造方法


/**

  • 屏幕宽度

*/

private int mScreenWidth;

/**

  • dp 菜单距离屏幕的右边距

*/

private int mMenuRightPadding;

public BinarySlidingMenu(Context context, AttributeSet attrs, int defStyle)

{

super(context, attrs, defStyle);

mScreenWidth = ScreenUtils.getScreenWidth(context);

TypedArray a = context.getTheme().obtainStyledAttributes(attrs,

R.styleable.BinarySlidingMenu, defStyle, 0);

int n = a.getIndexCount();

for (int i = 0; i < n; i++)

{

int attr = a.getIndex(i);

switch (attr)

{

case R.styleable.BinarySlidingMenu_rightPadding:

// 默认50

mMenuRightPadding = a.getDimensionPixelSize(attr,

(int) TypedValue.applyDimension(

TypedValue.COMPLEX_UNIT_DIP, 50f,

getResources().getDisplayMetrics()));// 默认为10DP

break;

}

}

a.recycle();

}

我们在构造方法中,获取我们自定义的一个属性rightPadding,然后赋值给我们的成员变量mMenuRightPadding;关于如何自定义属性参考侧滑菜单的第一篇博文,这里就不赘述了。

3、onMeasure


onMeasure中肯定是对侧滑菜单的宽度、高度等进行设置:

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

{

/**

  • 显示的设置一个宽度

*/

if (!once)

{

mWrapper = (LinearLayout) getChildAt(0);

mLeftMenu = (ViewGroup) mWrapper.getChildAt(0);

mContent = (ViewGroup) mWrapper.getChildAt(1);

mRightMenu = (ViewGroup) mWrapper.getChildAt(2);

mMenuWidth = mScreenWidth - mMenuRightPadding;

mHalfMenuWidth = mMenuWidth / 2;

mLeftMenu.getLayoutParams().width = mMenuWidth;

mContent.getLayoutParams().width = mScreenWidth;

mRightMenu.getLayoutParams().width = mMenuWidth;

}

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

可以看到我们分别给左侧、右侧的菜单设置了宽度(mScreenWidth - mMenuRightPadding);

宽度设置完成以后,肯定就是定位了,把左边的菜单弄到左边去,右边的菜单放置到右边,中间依然是我们的内容区域,那么请看onLayout方法~

4、onLayout


@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;

}

}

哈,出奇的简单,因为我们的内部是个横向的线性布局,所以直接把左侧滑出去即可~~定位也完成了,那么此时应该到了我们的处理触摸了。

5、onTouchEvent


@Override

public boolean onTouchEvent(MotionEvent ev)

{

int action = ev.getAction();

switch (action)

{

// Up时,进行判断,如果显示区域大于菜单宽度一半则完全显示,否则隐藏

case MotionEvent.ACTION_UP:

int scrollX = getScrollX();

// 如果是操作左侧菜单

if (isOperateLeft)

{

// 如果影藏的区域大于菜单一半,则影藏菜单

if (scrollX > mHalfMenuWidth)

{

this.smoothScrollTo(mMenuWidth, 0);

// 如果当前左侧菜单是开启状态,且mOnMenuOpenListener不为空,则回调关闭菜单

if (isLeftMenuOpen && mOnMenuOpenListener != null)

{

// 第一个参数true:打开菜单,false:关闭菜单;第二个参数 0 代表左侧;1代表右侧

mOnMenuOpenListener.onMenuOpen(false, 0);

}

isLeftMenuOpen = false;

} else

// 关闭左侧菜单

{

this.smoothScrollTo(0, 0);

// 如果当前左侧菜单是关闭状态,且mOnMenuOpenListener不为空,则回调打开菜单

if (!isLeftMenuOpen && mOnMenuOpenListener != null)

{

mOnMenuOpenListener.onMenuOpen(true, 0);

}

isLeftMenuOpen = true;

}

}

// 操作右侧

if (isOperateRight)

{

// 打开右侧侧滑菜单

if (scrollX > mHalfMenuWidth + mMenuWidth)

{

this.smoothScrollTo(mMenuWidth + mMenuWidth, 0);

} else

// 关闭右侧侧滑菜单

{

this.smoothScrollTo(mMenuWidth, 0);

}

}

return true;

}

return super.onTouchEvent(ev);

}

依然是简单~~~我们只需要关注ACTION_UP,然后得到手指抬起后的scrollX,然后我们通过一个布尔值,判断用户现在操作是针对左侧菜单,还是右侧菜单?

如果是操作左侧,那么判断scorllX是否超过了菜单宽度的一半,然后做相应的操作。

如果是操作右侧,那么判断scrollX与 mHalfMenuWidth + mMenuWidth ( 注意下,右侧菜单完全影藏的时候,scrollX 就等于 mMenuWidth ),然后做对应的操作。

我们还给左侧的菜单加上了一个回调:

if (isLeftMenuOpen && mOnMenuOpenListener != null)

{

//第一个参数true:打开菜单,false:关闭菜单;第二个参数 0 代表左侧;1代表右侧

mOnMenuOpenListener.onMenuOpen(false, 0);

}

扫一眼我们的回调接口:

/**

  • 回调的接口

  • @author zhy

*/

public interface OnMenuOpenListener

{

/**

  • @param isOpen true打开菜单,false关闭菜单

  • @param flag 0 左侧, 1右侧

*/

void onMenuOpen(boolean isOpen, int flag);

}

右侧菜单我没有添加回调,大家按照左侧的形式自己添加下就ok ;

好了,接下来,看下我们判断用户操作是左侧还是右侧的代码写在哪。

6、onScrollChanged


@Override

protected void onScrollChanged(int l, int t, int oldl, int oldt)

{

super.onScrollChanged(l, t, oldl, oldt);

if (l > mMenuWidth)

{

isOperateRight = true;

isOperateLeft = false;

} else

{

isOperateRight = false;

isOperateLeft = true;

}

}

如果看过前两篇,对这个方法应该很眼熟了吧。我们直接通过 l 和 菜单宽度进行比较, 如果大于菜单宽度,那么肯定是想操作右侧菜单,否则那么就是想操作左侧菜单;

到此,我们的双向侧滑菜单已经大功告成了,至于你信不信,反正我有效果图。看效果图前,贴一下MainActivity的代码:

7、MainActivity


package com.zhy.zhy_bin_slidingmenu02;

import java.util.ArrayList;

import java.util.List;

import android.app.ListActivity;

import android.os.Bundle;

import android.view.Window;

import android.widget.ArrayAdapter;

import android.widget.Toast;

import com.zhy.view.BinarySlidingMenu;

import com.zhy.view.BinarySlidingMenu.OnMenuOpenListener;

public class MainActivity extends ListActivity

{

private BinarySlidingMenu mMenu;

private List mDatas = new ArrayList();

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_main);

mMenu = (BinarySlidingMenu) findViewById(R.id.id_menu);

mMenu.setOnMenuOpenListener(new OnMenuOpenListener()

{

@Override

public void onMenuOpen(boolean isOpen, int flag)

{

if (isOpen)

{

Toast.makeText(getApplicationContext(),

flag == 0 ? “LeftMenu Open” : “RightMenu Open”,

Toast.LENGTH_SHORT).show();

} else

{

Toast.makeText(getApplicationContext(),

flag == 0 ? “LeftMenu Close” : “RightMenu Close”,

Toast.LENGTH_SHORT).show();

}

}

});

// 初始化数据

for (int i = ‘A’; i <= ‘Z’; i++)

{

mDatas.add((char) i + “”);

}

// 设置适配器

setListAdapter(new ArrayAdapter(this, R.layout.item, mDatas));

}

}

没撒好说的,为了方便直接继承了ListActivity,然后设置了一下回调,布局文件一定要有ListView,为了测试我们是否有冲突~~不过有咱们也不怕~

效果图:

当然了,最简单的双向侧滑怎么能满足大家的好(Zhao)奇(Nue)心呢,所以我们准备玩点神奇的花样~~

4、打造抽屉式双向侧滑

===========

我们在onScrollChanged添加两行代码~~为mContent设置一个属性动画

@Override

protected void onScrollChanged(int l, int t, int oldl, int oldt)

{

super.onScrollChanged(l, t, oldl, oldt);

if (l > mMenuWidth)

{

isOperateRight = true;

isOperateLeft = false;

} else

{

isOperateRight = false;

isOperateLeft = true;

}

float scale = l * 1.0f / mMenuWidth;

ViewHelper.setTranslationX(mContent, mMenuWidth * (scale - 1));

}

简单分析一下哈:

1、scale,在滑动左侧菜单时:值为1.0~0.0;mMenuWidth * (scale - 1) 的值就是从 0.0~ -mMenuWidth(注意:负的) ; 那么mContent的向偏移量,就是0到mMenuWidth ;也就是说,整个滑动的过程,我们强制让内容区域固定了。

2、scale,在滑动右侧菜单时:值为:1.0~2.0;mMenuWidth * (scale - 1) 的值就是从 0.0~ mMenuWidth(注意:正数) ;那么mContent的向偏移量,就是0到mMenuWidth ;也就是说,整个滑动的过程,我们强制让内容区域固定了。

好了,内容固定了,那么我们此刻的两边菜单应该是在内容之上显示出来~~这不就是我们的抽屉效果么~

嗯,这次木有效果图了,因为测试结果发现,左侧的菜单会被内容区域遮盖住,看不到;右侧菜单符合预期效果;因为,左侧菜单滑动出来以后,被内容区域遮盖住了,这个也很容易理解,毕竟我们的布局,内容在左侧菜单后面,肯定会挡住它的。那么,怎么办呢?

起初,我准备使用bringToFont方法,在拖动的时候,让菜单在上面~不过呢,问题大大的,有兴趣可以试试

于是乎,我换了个方法,我将BinarySlidingMenu内部的Linearlayout进行了自定义,现在布局文件是这样的:

<com.zhy.view.BinarySlidingMenu xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:tools=“http://schemas.android.com/tools”

xmlns:zhy=“http://schemas.android.com/apk/res/com.zhy.zhy_bin_slidingmenu03”

android:id=“@+id/id_menu”

android:layout_width=“wrap_content”

android:layout_height=“fill_parent”

android:scrollbars=“none”

zhy:rightPadding=“100dp” >

<com.zhy.view.MyLinearLayout

android:layout_width=“wrap_content”

android:layout_height=“fill_parent”

android:orientation=“horizontal” >

<LinearLayout

android:layout_width=“fill_parent”

android:layout_height=“fill_parent”

android:background=“@drawable/eee”

android:gravity=“center”

android:orientation=“horizontal” >

<ListView

android:id=“@android:id/list”

android:layout_width=“fill_parent”

android:layout_height=“fill_parent” >
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

文末

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

[外链图片转存中…(img-dnN136Fb-1713708077662)]

文末

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

[外链图片转存中…(img-NL8tSjA6-1713708077663)]

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-hkycDUs1-1713708077664)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值