用电信固定IP做网站,asp网站的配置,众意网站建设zyecn,北京网页设计 网页制作在实际的各类App开发中#xff0c;经常会需要做一个左侧的侧滑栏#xff0c;类似于QQ这种。
今天这篇文章总结下自己在开发中遇到的这类可以跟随移动且可以缩放的侧滑栏。
一、实现原理
使用 HorizontalScrollView 实现一个水平方向的可滑动的View#xff0c;左布局为侧滑…在实际的各类App开发中经常会需要做一个左侧的侧滑栏类似于QQ这种。
今天这篇文章总结下自己在开发中遇到的这类可以跟随移动且可以缩放的侧滑栏。
一、实现原理
使用 HorizontalScrollView 实现一个水平方向的可滑动的View左布局为侧滑栏右布局为自己的主页内容。
来看下android的官方解释我用谷歌翻译了 用户可以滚动的视图层次结构的布局容器允许其大于物理显示。 HorizontalScrollView 是一种 FrameLayout这意味着您应该在其中放置一个包含要滚动的全部内容的子级这个子级本身可能是一个具有复杂对象层次结构的布局管理器。经常使用的子级是水平方向的 LinearLayout它呈现用户可以滚动的顶级项目的水平数组。 TextView 类还负责自己的滚动因此不需要 HorizontalScrollView但将两者结合使用可以在更大的容器中实现文本视图的效果。 HorizontalScrollView 仅支持水平滚动。对于垂直滚动请使用 ScrollView 或 ListView。 属性 关键点
1、用户可以滚动的视图层次结构的布局容器允许其大于物理显示。证明就像我们平时的用到的垂直方向的scrollView嵌套几个列表一样。
2、HorizontalScrollView 是一种 FrameLayout这意味着您应该在其中放置一个包含要滚动的全部内容的子级这个代表你需要在HorizontalScrollView先放一个总布局再在这个布局里放左右布局内容。
二、实现过程
第一步xml布局
?xml version1.0 encodingutf-8?wanwan.and.lx.lxslideviewdemo.SlideMenuLayoutxmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:idid/slidingandroid:backgroundmipmap/img_bgtools:context.MainActivityLinearLayoutandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:orientationhorizontalLinearLayoutandroid:layout_width200dpandroid:layout_heightmatch_parentandroid:layout_gravitystartandroid:orientationverticalRelativeLayoutandroid:idid/sidebarLayoutandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroidx.appcompat.widget.AppCompatImageViewandroid:idid/sidebar_image_app_iconandroid:layout_width80dpandroid:layout_height80dpandroid:layout_centerHorizontaltrueandroid:layout_marginTop106dpandroid:scaleTypefitXYandroid:srcmipmap/ic_launcher /androidx.constraintlayout.widget.ConstraintLayoutandroid:idid/slide_item_privacyandroid:layout_width200dpandroid:layout_height57dpandroid:layout_belowid/sidebar_image_app_iconandroid:layout_marginTop30dpandroidx.appcompat.widget.AppCompatImageViewandroid:idid/set_privacy_lock_imgandroid:layout_width16dpandroid:layout_height16dpandroid:layout_marginStart32dpandroid:srcmipmap/privacyapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintStart_toStartOfparentapp:layout_constraintTop_toTopOfparent /androidx.appcompat.widget.AppCompatTextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginStart13.7dpandroid:textPrivacy Policyandroid:textColorcolor/blackandroid:textSize17spapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintStart_toEndOfid/set_privacy_lock_imgapp:layout_constraintTop_toTopOfparent //androidx.constraintlayout.widget.ConstraintLayoutandroidx.constraintlayout.widget.ConstraintLayoutandroid:idid/slide_item_shareandroid:layout_width200dpandroid:layout_height57dpandroid:layout_belowid/slide_item_privacyandroid:layout_marginTop10dpandroidx.appcompat.widget.AppCompatImageViewandroid:idid/set_share_lock_imgandroid:layout_width16dpandroid:layout_height16dpandroid:layout_marginStart32dpandroid:srcmipmap/shareapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintStart_toStartOfparentapp:layout_constraintTop_toTopOfparent /androidx.appcompat.widget.AppCompatTextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginStart13.7dpandroid:textShareandroid:textColorcolor/blackandroid:textSize17spapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintStart_toEndOfid/set_share_lock_imgapp:layout_constraintTop_toTopOfparent //androidx.constraintlayout.widget.ConstraintLayoutandroidx.constraintlayout.widget.ConstraintLayoutandroid:idid/slide_item_updateandroid:layout_width200dpandroid:layout_height57dpandroid:layout_belowid/slide_item_shareandroid:layout_marginTop10dpandroidx.appcompat.widget.AppCompatImageViewandroid:idid/set_update_lock_imgandroid:layout_width16dpandroid:layout_height16dpandroid:layout_marginStart32dpandroid:srcmipmap/updateapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintStart_toStartOfparentapp:layout_constraintTop_toTopOfparent /androidx.appcompat.widget.AppCompatTextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginStart13.7dpandroid:textUpdateandroid:textColorcolor/blackandroid:textSize17spapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintStart_toEndOfid/set_update_lock_imgapp:layout_constraintTop_toTopOfparent //androidx.constraintlayout.widget.ConstraintLayout/RelativeLayout/LinearLayoutRelativeLayoutandroid:layout_widthmatch_parentandroid:backgroundcolor/whiteandroid:layout_heightmatch_parentandroidx.appcompat.widget.AppCompatTextViewandroid:idid/textandroid:layout_widthwrap_contentandroid:layout_centerInParenttrueandroid:textStyleboldandroid:textSize18spandroid:textColorcolor/blackandroid:layout_heightwrap_contentandroid:text这是主页/androidx.appcompat.widget.AppCompatImageViewandroid:layout_marginTop20dpandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:srcmipmap/setandroid:layout_centerHorizontaltrueandroid:layout_belowid/textandroid:idid/set//RelativeLayout/LinearLayout/wanwan.and.lx.lxslideviewdemo.SlideMenuLayout1.SlideMenuLayout其实就是HorizontalScrollView这是个自定义控件待会儿代码附上。
2.可以看到SlideMenuLayout只有一个子View为LinearLayoutLinearLayout它是全屏且水平布局且有两个子布局分为左右。
第二步自定义控件HorizontalScrollView
class SlideMenuLayout : HorizontalScrollView {/*** 当菜单页显示时右侧内容页显示宽度*/private var menuRightWidth 0private lateinit var menuView: Viewprivate lateinit var contentView: View/*** 用于处理飞速滑动*/private var gestureDetector: GestureDetectorvar isMenuOpen: Boolean falseprivate var btn: AppCompatImageView? 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) {gestureDetector GestureDetector(getContext(), GestureDetectorListener())}//用于处理飞速滑动inner class GestureDetectorListener : SimpleOnGestureListener() {override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {//屏蔽向右滑动if (e2.x - e1.x 30) {Log.e(TAG, right, right, go go go ---)return true}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.childCountif (childCount ! 2) {throw IllegalArgumentException(LinearLayout child size must be 2!)}menuView linearLayout.getChildAt(0)val menuLayoutParams menuView.layoutParamsmenuLayoutParams.width getScreenWidth() - menuRightWidth - SizeUtils.dp2px(100f)menuView.layoutParams menuLayoutParams//设置右侧内容页宽度为屏幕宽度contentView linearLayout.getChildAt(1)linearLayout.removeView(contentView)val contentRelativeLayout RelativeLayout(context)contentRelativeLayout.addView(contentView)val contentLayoutParams contentView.layoutParamscontentLayoutParams.width getScreenWidth()contentRelativeLayout.layoutParams contentLayoutParamslinearLayout.addView(contentRelativeLayout)btn contentView.findViewById(R.id.set)btn?.setOnClickListener {openMenu()}}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}override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {if (ev.action MotionEvent.ACTION_MOVE) {return false}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 (ev.action MotionEvent.ACTION_MOVE) {return false}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)}/*** 打开菜单*/fun openMenu() {smoothScrollTo(0, 0)isMenuOpen true}override fun dispatchTouchEvent(me: MotionEvent?): Boolean {if (me ! null) {this.gestureDetector.onTouchEvent(me)}return super.dispatchTouchEvent(me)}/*** 关闭菜单*/fun closeMenu() {smoothScrollTo(menuView.measuredWidth, 0)isMenuOpen false}}关键点 1、所谓的控制左右滑动用到的是 smoothScrollTo()方法 2、使用SimpleOnGestureListener来进行手势监听并且把onTouchEvent和dispatchTouchEvent的部分事件交由其处理。使用重写的onFling方法进行各类手势处理由于需求原因上面的代码我禁止掉了右滑可根据自己实际需求进行开发。 3、使用onInterceptTouchEvent拦截部分事件。 4、重写onScrollChanged监听滑动时对左右布局进行缩放这样会显得更流畅些。 5、重写onFinishInflate方法拿到左右子布局对其设置layoutparam属性点击事件赋值等等。 tipsmenuLayoutParams.width getScreenWidth() - menuRightWidth - SizeUtils.dp2px(100f) 这个就是设置左侧侧滑栏宽度因为一般都不会全屏所以会拿屏幕宽度去减去自己想要的值来展示右侧主页的内容这儿可以给menuRightWidth设定一个值不过我需求固定了就直接在这儿减去了SizeUtils.dp2px(100f)。 第三步代码调用
class MainActivity : AppCompatActivity() {private var setImg: AppCompatImageView? nullprivate var sliding: SlideMenuLayout? nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)setImg findViewById(R.id.set)sliding findViewById(R.id.sliding)setImg?.setOnClickListener {sliding?.openMenu()}}
}
代码全在这儿了我就不贴github地址了。
三、实现效果