界面中局部(部分)区域嵌套滑动

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的滑动冲突?

ios 中scrollview上面嵌套tableView,左右滑动出现数据多次刷新的问题

better-scroll中嵌套原生滚动组件,原生滚动组件失效问题