Android——仿QQ的多界面的ViewPager

先上效果图:

Android——仿QQ的多界面的ViewPager

首先这个界面背景透明,所以我们肯定需要在一个弹窗中嵌套ViewPager进行操作,先自定义一个DialogFragment:

class TestDialogFragment : DialogFragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return LayoutInflater.from(context).inflate(R.layout.layout_test, container, false)

    }

    fun show(manager: FragmentManager?) {
        show(manager, this::class.java.simpleName)
    }

    companion object {
        fun newInstance(): TestDialogFragment {
            return TestDialogFragment()
        }
    }
}

然后我们确定页面布局,上方是一个ViewPager,下方是一个指示栏加上一个关闭按钮,并且ViewPager与指示栏有部分重合,为了快速实现指示栏效果,我使用了开源项目MagicIndicator:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:orientation="vertical">
    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_100"
        android:background="@color/color_ff3a455e"
        android:orientation="horizontal"
        android:layout_alignParentBottom="true">

        <net.lucode.hackware.magicindicator.MagicIndicator
            android:id="@+id/reportDetailIndicator"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginStart="@dimen/dp_16"
            android:layout_weight="1" />

        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/ivClose"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginEnd="@dimen/dp_16"
            android:src="@drawable/ic_delete" />
    </androidx.appcompat.widget.LinearLayoutCompat>
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/reportDetailViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="40dp"
        android:layout_marginRight="40dp"
        android:layout_marginBottom="@dimen/dp_60"
        android:layout_weight="1"
        android:clipChildren="false" />

</RelativeLayout>

注意,在父布局和ViewPager中要加上clipChildren=“false”,该属性表示当子view超出限制时是否切割,默认为true

然后我们在DialogFragment的onViewCreated方法中初始化控件:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    initView(view)
}
private fun initView(view: View?) {
    view?.apply {
//初始化magicIndicator
        val commonNavigator =  CommonNavigator(activity)
     //设置缓存三页
        reportDetailViewPager.offscreenPageLimit = 3
//设置页面之间的间隔,这里的效果需要重合,所以我们设置为-200
        reportDetailViewPager.pageMargin = -200
        reportDetailViewPager.adapter = object : FragmentStatePagerAdapter(childFragmentManager) {
            override fun getItem(p0: Int): Fragment {
                return list[p0]
            }

            override fun getCount(): Int {
                return list.size
            }

        }
        initIndicator(commonNavigator, this)
    }
}
private fun initIndicator(commonNavigator: CommonNavigator, view: View?) {
    view?.apply {
//设置滑动
        commonNavigator.scrollPivotX = 0.65f
//设置指示器adapter
        commonNavigator.adapter = object : CommonNavigatorAdapter() {
            override fun getTitleView(context: Context, index: Int): IPagerTitleView {
                val simpleTitleView = TestTitleView(context)
//设置指示器内容
                with(simpleTitleView) {
                    isSingleLine = false
                    normalColor = ContextCompat.getColor(context, R.color.color_d7d7d9)
                    selectedColor = ContextCompat.getColor(context, R.color.white)
                    selectedSize = 14f
                    normalSize = 12f
                    text = "2019/2/2 \n 2:22"
                    gravity = Gravity.CENTER
                    setOnClickListener { [email protected] = index }
                    return this
                }
            }
//暂定数据源为5页
            override fun getCount(): Int {
                return 5
            }

            override fun getIndicator(context: Context?): IPagerIndicator? {
                return null
            }

        }
        reportDetailIndicator.navigator = commonNavigator
        //绑定ViewPager
        ViewPagerHelper.bind(reportDetailIndicator, reportDetailViewPager)
        ivClose.setOnClickListener {
            dialog.dismiss()
        }
    }
}

这里我们看到ViewPager是有不同大小的,前一页和后一页都要比当前页小一点,所以我们新建一个DepthPageTransformer继承自ViewPager的PageTransformer:

class DepthPageTransformer : ViewPager.PageTransformer {
//透明度和高度最小值
private val MAX_SCALE = 0.90f
private val MIN_SCALE = 0.70f
private val MIN_ALPHA = 0.3f
override fun transformPage(page: View, position: Float) {
    val width = page.width

    if (position < -1) {
        page.alpha = MIN_ALPHA
        page.scaleY = MIN_SCALE
        page.scaleX = MIN_SCALE
    } else if (position <= 1) {
        if (position == 0f) { //当前页面
            page.alpha = 1.0f
            page.scaleY = MAX_SCALE
            page.scaleX = MAX_SCALE
        } else {
            if (position < 0) {
                //平滑变化
                val f = MIN_ALPHA + (1 - MIN_ALPHA) * (1 + position)
                page.alpha = f
                val s = MIN_SCALE + (MAX_SCALE - MIN_SCALE) * (1 + position)
                page.scaleY = s
                page.scaleX = s
            } else {
                //平滑变化
                val f = MIN_ALPHA + (1 - MIN_ALPHA) * (1 - position)
                page.alpha = f
                val s = MIN_SCALE + (MAX_SCALE - MIN_SCALE) * (1 - position)
                page.scaleY = s
                page.scaleX = s
            }

        }
    }
}

嗯,现在我们加入数据源:

val list by lazy {
    listOf(TestFragment(), TestFragment(), TestFragment(), TestFragment(), TestFragment())
}

设置ViewPager的Adapter:

reportDetailViewPager.adapter = object : FragmentStatePagerAdapter(childFragmentManager) {
    override fun getItem(p0: Int): Fragment {
        return list[p0]
    }

    override fun getCount(): Int {
        return list.size
    }

}

然后设置PageTransformer:

reportDetailViewPager.setPageTransformer(true, DepthPageTransformer())

重写show方法:

fun show(manager: FragmentManager?) {
    show(manager, this::class.java.simpleName)
}

然后调用

TestDialogFragment.newInstance().show(fragmentManager);

运行代码,发现效果与我们的不同,背景是白色,并且大小不正常,那么我们再在dialogFragment中重新设置一下大小与背景:

override fun onStart() {
    super.onStart()
    val window = dialog.window
    val windowParams = window!!.attributes
    window.setLayout(-1, -2) //高度自适应,宽度全屏
    windowParams.gravity = Gravity.CENTER //在顶部显示
    dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)//设置背景透明
    window.attributes = windowParams
}

全部代码:



class TestDialogFragment : DialogFragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return LayoutInflater.from(context).inflate(R.layout.layout_test, container, false)

    }

    fun show(manager: FragmentManager?) {
        show(manager, this::class.java.simpleName)
    }

    override fun onStart() {
        super.onStart()
        val window = dialog.window
        val windowParams = window!!.attributes
        window.setLayout(-1, -2) //高度自适应,宽度全屏
        windowParams.gravity = Gravity.CENTER //在顶部显示
        dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
        window.attributes = windowParams
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initView(view)
    }

    val list by lazy {
        listOf(ApprovalFragment(), ApprovalFragment(), ApprovalFragment(), ApprovalFragment(), ApprovalFragment())
    }

    private fun initView(view: View?) {
        view?.apply {
            val commonNavigator =  CommonNavigator(activity)
            reportDetailViewPager.setPageTransformer(true, DepthPageTransformer())
            reportDetailViewPager.offscreenPageLimit = 3
            reportDetailViewPager.pageMargin = -200
            reportDetailViewPager.adapter = object : FragmentStatePagerAdapter(childFragmentManager) {
                override fun getItem(p0: Int): Fragment {
                    return list[p0]
                }

                override fun getCount(): Int {
                    return list.size
                }

            }
            initIndicator(commonNavigator, this)
        }
    }

    private fun initIndicator(commonNavigator: CommonNavigator, view: View?) {
        view?.apply {
            commonNavigator.scrollPivotX = 0.65f
            commonNavigator.adapter = object : CommonNavigatorAdapter() {
                override fun getTitleView(context: Context, index: Int): IPagerTitleView {
                    val simpleTitleView = TaskDetailTabTitleView(context)
                    with(simpleTitleView) {
                        isSingleLine = false
                        normalColor = ContextCompat.getColor(context, R.color.color_d7d7d9)
                        selectedColor = ContextCompat.getColor(context, R.color.white)
                        selectedSize = 14f
                        normalSize = 12f
                        text = "2019/2/2 \n 2:22"
                        gravity = Gravity.CENTER
                        setOnClickListener { [email protected] = index }
                        return this
                    }
                }

                override fun getCount(): Int {
                    return 5
                }

                override fun getIndicator(context: Context?): IPagerIndicator? {
                    return null
                }

            }
            reportDetailIndicator.navigator = commonNavigator
            //绑定ViewPager
            ViewPagerHelper.bind(reportDetailIndicator, reportDetailViewPager)
            ivClose.setOnClickListener {
                dialog.dismiss()
            }
        }
    }


    companion object {
        fun newInstance(): TestDialogFragment {
            return TaskReportDetailDialogFragment()
        }
    }

        class DepthPageTransformer : ViewPager.PageTransformer {
        //透明度和高度最小值
        private val MAX_SCALE = 0.90f
        private val MIN_SCALE = 0.70f
        private val MIN_ALPHA = 0.3f
        override fun transformPage(page: View, position: Float) {
            val width = page.width

            if (position < -1) {
                page.alpha = MIN_ALPHA
                page.scaleY = MIN_SCALE
                page.scaleX = MIN_SCALE
            } else if (position <= 1) {
                if (position == 0f) { //当前页面
                    page.alpha = 1.0f
                    page.scaleY = MAX_SCALE
                    page.scaleX = MAX_SCALE
                } else {
                    if (position < 0) {
                        //平滑变化
                        val f = MIN_ALPHA + (1 - MIN_ALPHA) * (1 + position)
                        page.alpha = f
                        val s = MIN_SCALE + (MAX_SCALE - MIN_SCALE) * (1 + position)
                        page.scaleY = s
                        page.scaleX = s
                    } else {
                        //平滑变化
                        val f = MIN_ALPHA + (1 - MIN_ALPHA) * (1 - position)
                        page.alpha = f
                        val s = MIN_SCALE + (MAX_SCALE - MIN_SCALE) * (1 - position)
                        page.scaleY = s
                        page.scaleX = s
                    }

                }
            }
        }
    }
}

TestTitleView:

class TaskDetailTabTitleView(context: Context) : SimplePagerTitleView(context) {

    var selectedSize = 18f
    var normalSize = 14f

    override fun onSelected(index: Int, totalCount: Int) {
        super.onSelected(index, totalCount)
        textSize = selectedSize
    }

    override fun onDeselected(index: Int, totalCount: Int) {
        super.onDeselected(index, totalCount)
        textSize = normalSize
    }

}