界面中局部(部分)区域嵌套滑动
Posted 夜尽天明89
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了界面中局部(部分)区域嵌套滑动相关的知识,希望对你有一定的参考价值。
最近项目中遇到个局部嵌套滑动的需求,实现后整理成demo
代码不多,就直接贴出来了。
需求:
1、一个界面,顶部有一块区域是 固定区域,不可以滑动;
2、中间有个内容介绍区域(控件);
3、下面是 viewpager+Fragment,多模块界面;
4、下面的 viewpager需要可以往上滑动,把内容介绍区域 顶走,固定区域不动。当 viewpager的tab到了 固定区域 的底部的时候,只能viewpager中Fragment里面的布局可以滑动
示例界面:
需要被顶走的区域
往上滑动时,把 内容介绍 布局顶走后的界面
数据列表可以滑动
---------- 分隔线 ----------
---------- 分隔线 ----------
功能实现
代码、界面的介绍
具体代码:这里就不详细说 MagicIndicator 了
1、颜色值
<color name="color_indicator_selected">#FF111111</color>
<color name="color_indicator_normal">#FF666666</color>
<color name="color_read">#ff0000</color>
2、JudgeNestedScrollView
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewConfiguration
import androidx.core.widget.NestedScrollView
import kotlin.math.abs
class JudgeNestedScrollView : NestedScrollView
private var isNeedScroll = true
private var xDistance: Float = 0.toFloat()
private var yDistance: Float = 0.toFloat()
private var xLast: Float = 0.toFloat()
private var yLast: Float = 0.toFloat()
private var scaledTouchSlop: Int = 0
constructor(context: Context) : super(context, null)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
scaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean
when (ev.action)
MotionEvent.ACTION_DOWN ->
yDistance = 0f
xDistance = yDistance
xLast = ev.x
yLast = ev.y
MotionEvent.ACTION_MOVE ->
val curX = ev.x
val curY = ev.y
xDistance += abs(curX - xLast)
yDistance += abs(curY - yLast)
xLast = curX
yLast = curY
return !(xDistance >= yDistance || yDistance < scaledTouchSlop) && isNeedScroll
try
return super.onInterceptTouchEvent(ev)
catch (e: Exception)
return false
/*
该方法用来处理NestedScrollView是否拦截滑动事件
*/
fun setNeedScroll(isNeedScroll: Boolean)
this.isNeedScroll = isNeedScroll
3、ViewPagerNoScrollHorizontally
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewConfiguration
import androidx.viewpager.widget.ViewPager
import kotlin.math.abs
class ViewPagerNoScrollHorizontally @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) :
ViewPager(context, attrs)
private var downX = 0f
private var downY = 0f
var canScroll = false
override fun canScrollHorizontally(direction: Int): Boolean
return canScroll
var isNestScrollChildEnable = true
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean
when(ev?.action)
MotionEvent.ACTION_DOWN ->
downX = ev.x
downY = ev.y
MotionEvent.ACTION_MOVE ->
val absDeltaX = abs(ev.x - downX)
val absDeltaY = abs(ev.y - downY)
if (absDeltaX > ViewConfiguration.get(context).scaledTouchSlop ||
absDeltaY > ViewConfiguration.get(context).scaledTouchSlop
)
if (!isNestScrollChildEnable)
return true
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL ->
downX = 0f
downY = 0f
return super.onInterceptTouchEvent(ev)
4、ScaleTransitionPagerTitleView 指示器用
import android.content.Context;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ColorTransitionPagerTitleView;
public class ScaleTransitionPagerTitleView extends ColorTransitionPagerTitleView
private float mMinScale = 0.85f;
public ScaleTransitionPagerTitleView(Context context)
super(context);
@Override
public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight)
super.onEnter(index, totalCount, enterPercent, leftToRight); // 实现颜色渐变
setScaleX(mMinScale + (1.0f - mMinScale) * enterPercent);
setScaleY(mMinScale + (1.0f - mMinScale) * enterPercent);
@Override
public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight)
super.onLeave(index, totalCount, leavePercent, leftToRight); // 实现颜色渐变
setScaleX(1.0f + (mMinScale - 1.0f) * leavePercent);
setScaleY(1.0f + (mMinScale - 1.0f) * leavePercent);
public float getMinScale()
return mMinScale;
public void setMinScale(float minScale)
mMinScale = minScale;
5、MyViewPagerAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
class MyViewPagerAdapter(
private var list: MutableList<Fragment>,
fm: FragmentManager
) : FragmentPagerAdapter(fm)
override fun getItem(position: Int): Fragment
return list[position]
override fun getCount(): Int
return list.size
6、MyDemoListAdapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_my_demo.view.*
class MyDemoListAdapter(
var context: Context,
var list: MutableList<String>
) :
RecyclerView.Adapter<MyDemoListAdapter.ViewHolder>()
private val layoutInflater = LayoutInflater.from(context)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ViewHolder(
layoutInflater.inflate(
R.layout.item_my_demo,
parent,
false
)
)
override fun getItemCount(): Int
return list.size
override fun onBindViewHolder(holder: ViewHolder, position: Int)
holder.bindData(list[position], position)
//设置数据
fun setData(dataList: MutableList<String>)
list.clear()
list.addAll(dataList)
notifyDataSetChanged()
inner class ViewHolder(var view: View) : RecyclerView.ViewHolder(view)
fun bindData(str: String, position: Int)
itemView.item_my_demo_tv.text = str
item_my_demo
<?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="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/item_my_demo_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textSize="25dp" />
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#55ff0000" />
</LinearLayout>
上面这写是准备用的控件、adapter等,下面开始使用
1、fragment_one
<?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">
<!--必须具备滑动属性,ScrollView把滑动事件分发下来,这里需要滑动-->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/frag_one_tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:lineSpacingExtra="3dp"
android:textSize="20dp" />
</androidx.core.widget.NestedScrollView>
</LinearLayout>
OneFragment
import androidx.fragment.app.Fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_one.*
class OneFragment : Fragment()
//先于 onViewCreated 执行
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
var view: View = inflater.inflate(R.layout.fragment_one, container, false);
return view
//先于 onStart() 执行
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
init()
private fun init()
val str: String =
"这是第一段内容:内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容\\n\\n" +
"这是第二段内容:内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容\\n\\n" +
"这是第三段内容:内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容\\n\\n" +
"这是第四段内容:内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容\\n\\n" +
"这是第五段内容:内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容"
frag_one_tv.text = str
2、fragment_two
<?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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/my_demo_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:scrollbars="none" />
</LinearLayout>
TwoFragment
import androidx.fragment.app.Fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.fragment_two.*
class TwoFragment : Fragment()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
//先于 onViewCreated 执行
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
var view: View = inflater.inflate(R.layout.fragment_two, container, false);
return view
//先于 onStart() 执行
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
init()
private var myDemoListAdapter: MyDemoListAdapter? = null
private fun init()
//初始化适配器
myDemoListAdapter = MyDemoListAdapter(activity!!, mutableListOf())
my_demo_rv?.apply
layoutManager = LinearLayoutManager(activity)
adapter = myDemoListAdapter
//准备数据
val dataList: MutableList<String> = mutableListOf()
dataList.add("第 1 条数据")
dataList.add("第 2 条数据")
dataList.add("第 3 条数据")
dataList.add("第 4 条数据")
dataList.add("第 5 条数据")
dataList.add("第 6 条数据")
dataList.add("第 7 条数据")
dataList.add("第 8 条数据")
dataList.add("第 9 条数据")
dataList.add("第 10 条数据")
dataList.add("第 11 条数据")
dataList.add("第 12 条数据")
dataList.add("第 13 条数据")
dataList.add("第 14 条数据")
dataList.add("第 15 条数据")
//设置数据
myDemoListAdapter?.setData(dataList)
主界面
activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"以上是关于界面中局部(部分)区域嵌套滑动的主要内容,如果未能解决你的问题,请参考以下文章
浅谈ScrollView嵌套ListView及ListView嵌套的高度计算
Android -- 每日一问:如何解决ScrollView嵌套中一个ListView的滑动冲突?
Android -- 每日一问:如何解决ScrollView嵌套中一个ListView的滑动冲突?