700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Android自定义HorizontalScrollView实现侧滑菜单效果

Android自定义HorizontalScrollView实现侧滑菜单效果

时间:2021-02-02 11:36:52

相关推荐

Android自定义HorizontalScrollView实现侧滑菜单效果

最终效果

具体实现

布局文件

自定义属性

<!--自定义内容宽度--><declare-styleable name="SlideMenuLayout"><attr name="menuRightWidth" format="dimension" /></declare-styleable>

布局文件

### 主布局<?xml version="1.0" encoding="utf-8"?><com.crystal.view.SlideMenuLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/home_menu_bg"app:menuRightWidth="60dp"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><!--左侧菜单页--><include layout="@layout/layout_home_menu" /><!--右侧内容页--><include layout="@layout/layout_home_content" /></LinearLayout></com.crystal.view.SlideMenuLayout>

### 左侧菜单页<RelativeLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="72dp"android:orientation="vertical"><LinearLayoutandroid:id="@+id/enter_login"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="23dp"android:orientation="horizontal"><ImageViewandroid:id="@+id/user_head_iv"android:layout_width="56dp"android:layout_height="56dp"android:src="@drawable/morentouxiang" /><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="22dp"android:orientation="vertical"><TextViewandroid:id="@+id/user_name_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:drawablePadding="10dp"android:drawableRight="@drawable/user_write_paint"android:text="请登录"android:textColor="#c6b178"android:textSize="18dp" /><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="42dp"android:orientation="horizontal"><TextViewandroid:id="@+id/user_attention_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:drawablePadding="10dp"android:text="关注 0"android:textColor="#c6b178"android:textSize="12dp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="40dp"android:drawablePadding="10dp"android:text="粉丝 0"android:textColor="#c6b178"android:textSize="12dp" /></LinearLayout></LinearLayout></LinearLayout><ListViewandroid:id="@+id/menu_item_lv"android:layout_width="match_parent"android:layout_height="wrap_content"android:divider="@null"android:dividerHeight="0dp"android:layout_marginTop="60dp"/></LinearLayout><TextViewandroid:layout_width="wrap_content"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_margin="20dp"android:text="退出"android:textColor="#FFFFFF"android:layout_height="wrap_content" /></RelativeLayout>

### 右侧内容页<RelativeLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#FFFFFF"android:orientation="vertical"><Buttonandroid:id="@+id/btn"android:layout_width="60dp"android:layout_height="40dp"android:text="按钮"android:textSize="12sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="主页内容" /></RelativeLayout>

自定义SlideMenuLayout

package com.crystal.viewimport android.content.Contextimport android.graphics.Colorimport android.util.AttributeSetimport android.util.Logimport android.util.TypedValueimport android.view.GestureDetectorimport android.view.GestureDetector.SimpleOnGestureListenerimport android.view.MotionEventimport android.view.Viewimport android.widget.*import kotlin.math.abs/*** 自定义侧滑菜单实现* on /10/27*/class SlideMenuLayout : HorizontalScrollView {/*** 当菜单页显示时,右侧内容页显示宽度*/private var menuRightWidth = 0/*** 左侧菜单页View*/private lateinit var menuView: View/*** 右侧内容页View*/private lateinit var contentView: View/*** 右侧内容页阴影View*/private lateinit var shaderView: View/*** 用于处理飞速滑动*/private var gestureDetector: GestureDetector/*** 菜单当前是否为打开状态*/private var isMenuOpen: Boolean = false/*** 内容页按钮*/private var btn: Button? = null/*** 是否进行事件拦截*/private var isIntercept = falseconstructor(context: Context) : this(context, null)constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {val array = context.obtainStyledAttributes(attrs, R.styleable.SlideMenuLayout)menuRightWidth = array.getDimension(R.styleable.SlideMenuLayout_menuRightWidth,TypedValue.applyDimension(PLEX_UNIT_DIP, 62f, resources.displayMetrics)).toInt()array.recycle()gestureDetector = GestureDetector(getContext(), GestureDetectorListener())}//用于处理飞速滑动private inner class GestureDetectorListener : SimpleOnGestureListener() {override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {// velocityX 向右快速滑动是正值,向左快速滑动是负值//判断飞速滑动方向不是水平时,不处理if (abs(velocityY) > abs(velocityX)) {return false}if (isMenuOpen) {if (velocityX < 0) {closeMenu()return true}} else {if (velocityX > 0) {openMenu()return true}}return super.onFling(e1, e2, velocityX, velocityY)}}/*** 此方法在布局加载完毕时调用*/override fun onFinishInflate() {super.onFinishInflate()val linearLayout: LinearLayout = getChildAt(0) as LinearLayoutval childCount = linearLayout.childCount//这里childCount应该只包含 左侧菜单页以及右侧内容页,childCount 必须为2if (childCount != 2) {throw IllegalArgumentException("LinearLayout child size must be 2!")}//设置左侧菜单页宽度为屏幕宽度 - 主页收缩时宽度menuView = linearLayout.getChildAt(0)val menuLayoutParams = menuView.layoutParamsmenuLayoutParams.width = getScreenWidth() - menuRightWidthmenuView.layoutParams = menuLayoutParams//设置右侧内容页宽度为屏幕宽度contentView = linearLayout.getChildAt(1)/*** 实现在滑动过程中,为右侧内容页添加阴影效果* 1.先把contentView从布局中挖出来* 2.新建RelativeLayout 包裹 此contentView以及一层阴影View* 3.onScrollChanged中根据scale的值动态修改shader view的alpha值*/linearLayout.removeView(contentView)val contentRelativeLayout = RelativeLayout(context)contentRelativeLayout.addView(contentView)shaderView = View(context)shaderView.setBackgroundColor(Color.parseColor("#55000000"))contentRelativeLayout.addView(shaderView)val contentLayoutParams = contentView.layoutParamscontentLayoutParams.width = getScreenWidth()contentRelativeLayout.layoutParams = contentLayoutParamslinearLayout.addView(contentRelativeLayout)shaderView.alpha = 0.0fbtn = contentView.findViewById(R.id.btn)btn?.setOnClickListener {Toast.makeText(context, "This is Button", Toast.LENGTH_SHORT).show()}}override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {super.onLayout(changed, l, t, r, b)//默认情况下,应该全部展示内容页,关闭左侧菜单页scrollTo(menuView.measuredWidth, 0)}/*** 获取当前屏幕的宽度*/private fun getScreenWidth(): Int {return resources.displayMetrics.widthPixels}/*** 重写该方法,用于处理缩放和透明度效果*/override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {super.onScrollChanged(l, t, oldl, oldt)//滚动的时候,不停的回调 l 从屏幕宽度变化到 0val scale = 1 - l * 1f / menuView.measuredWidth //scale 从 0 到1//处理菜单页缩放和透明度menuView.pivotX = menuView.measuredWidth * 1fmenuView.pivotY = menuView.measuredHeight / 2fmenuView.scaleX = 0.5f + scale * 0.5fmenuView.scaleY = 0.5f + scale * 0.5fmenuView.alpha = 0.25f + 0.75f * scale//处理内容页缩放 缩放到0.7fcontentView.pivotX = 0fcontentView.pivotY = contentView.measuredHeight / 2fcontentView.scaleX = 0.7f + (1 - scale) * 0.3fcontentView.scaleY = 0.7f + (1 - scale) * 0.3f//滑动时,根据scale动态调整shader view的alpha值shaderView.alpha = scale}override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {if (isMenuOpen) {//如果点击事件落在内容页,则进行拦截并关闭菜单页if (ev.x > menuView.measuredWidth) {//进行事件拦截,不触发button点击事件isIntercept = truereturn true} else {isIntercept = false}}return super.onInterceptTouchEvent(ev)}override fun onTouchEvent(ev: MotionEvent): Boolean {//当执行快速滑动时,后续不再执行if (gestureDetector.onTouchEvent(ev)) {return gestureDetector.onTouchEvent(ev)}when (ev.action) {MotionEvent.ACTION_UP -> {if (isIntercept) {closeMenu()return true}//当手指抬起时,判断左侧菜单栏应该展示开始关闭//判断逻辑:当滚动x > 屏幕一半是,菜单栏隐藏,否则展开if (mScrollX > getScreenWidth() / 2) {closeMenu()} else {openMenu()}return false}}return super.onTouchEvent(ev)}/*** 打开菜单*/private fun openMenu() {smoothScrollTo(0, 0)isMenuOpen = true}/*** 关闭菜单*/private fun closeMenu() {smoothScrollTo(menuView.measuredWidth, 0)isMenuOpen = false}}

总结

这个案例涉及到事件分发、飞速滑动、view的缩放以及透明度处理,对自定义View的学习还是很有帮助的。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。