進來看看是不是你想要的效果,Android吸頂效果!並有着ViewPager左右切換!


老規矩,先上圖,看看是不是你想要的,美團效果:

圖片

最終效果:圖片

來一個圖形分析

接下來我要寫一個簡單示例,先分析一下布局,見下圖,最外層是NestedScrollView,之后嵌套一個LinearLayout頭部,中間TabLayout選擇器,底部一個ViewPager

ViewPager高度需要動態控制,看自己的需求了,如果是美團那種效果,就是ViewPager高度 = NestedScrollView高度 - TabLayout高度

image.gif

話不多說,代碼實現

接下來我寫一個例子,如果按照普通控件的嵌套方式來實現,那么肯定存在滑動沖突,會出現RecyclerView先進行滑動其次才是ScrollView滑動,那么就需要先重寫NestedScrollView控件,用於控制最大的滑動距離,當達到最大滑動距離,再分發給RecyclerView滑動!

NestedScrollView重寫

需要繼承自NestedScrollView並重寫onStartNestedScrollonNestedPreScroll方法,如下

package com.cyn.mt

import android.content.Context import android.util.AttributeSet import android.view.View import androidx.core.view.NestedScrollingParent2 import androidx.core.widget.NestedScrollView /** * @author cyn */ class CoordinatorScrollview : NestedScrollView, NestedScrollingParent2 { private var maxScrollY = 0 constructor(context: Context?) : super(context!!) constructor(context: Context?, attrs: AttributeSet?) : super( context!!, attrs ) constructor( context: Context?, attrs: AttributeSet?, defStyleAttr: Int ) : super(context!!, attrs, defStyleAttr) override fun onStartNestedScroll( child: View, target: View, axes: Int, type: Int ): Boolean { return true } /** * 設置最大滑動距離 * * @param maxScrollY 最大滑動距離 */ fun setMaxScrollY(maxScrollY: Int) { this.maxScrollY = maxScrollY } /** * @param target 觸發嵌套滑動的View * @param dx 表示 View 本次 x 方向的滾動的總距離 * @param dy 表示 View 本次 y 方向的滾動的總距離 * @param consumed 表示父布局消費的水平和垂直距離 * @param type 觸發滑動事件的類型 */ override fun onNestedPreScroll( target: View, dx: Int, dy: Int, consumed: IntArray, type: Int ) { if (dy > 0 && scrollY < maxScrollY) { scrollBy(0, dy) consumed[1] = dy } } }
布局文件

我按照美團的布局大體寫出這樣的布局

<?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" tools:context=".MainActivity"> <!--titleBar--> <LinearLayout android:id="@+id/titleBar" android:layout_width="match_parent" android:layout_height="45dp" android:gravity="center_vertical" android:orientation="horizontal" android:paddingLeft="18dp" android:paddingRight="18dp"> <EditText android:layout_width="0dp" android:layout_height="35dp" android:layout_marginEnd="12dp" android:layout_marginRight="12dp" android:layout_weight="1" android:background="@drawable/edit_style" android:paddingLeft="12dp" android:paddingRight="12dp" /> <TextView android:layout_width="wrap_content" android:layout_height="35dp" android:background="@drawable/button_style" android:gravity="center" android:paddingLeft="15dp" android:paddingRight="15dp" android:text="搜索" android:textColor="#333333" android:textStyle="bold" /> </LinearLayout> <!--coordinatorScrollView--> <com.cyn.mt.CoordinatorScrollview android:id="@+id/coordinatorScrollView" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!--相當於分析圖中頭部的LinearLayout,模擬動態添加的情況--> <LinearLayout android:id="@+id/titleLinerLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" /> <!--相當於分析圖中紅色標記處TabLayout--> <com.google.android.material.tabs.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!--相當於分析圖中綠色標記處ViewPager,代碼中動態設置高度--> <androidx.viewpager.widget.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </com.cyn.mt.CoordinatorScrollview> </LinearLayout>
Fragment

加入,在Fragment中放入RecyclerView,提供給ViewPager使用,這里代碼就不貼了,可以直接下源碼!源碼在文章末尾!

主要代碼(重點來了)

coordinatorScrollView最大滑動距離即是titleLinerLayout的高度,所以實現titleLinerLayoutpost方法,來監聽titleLinerLayout的高度,由於這一塊布局常常是通過網絡請求后加載,所以,網絡請求完畢后要再次實現post設置coordinatorScrollView最大滑動距離,如第80行代碼第90行代碼,在這里,我並不推薦使用多次回調監聽的方法!使用post只用調用一次,如果使用多次監聽View變化的方法,應該在最后一次網絡請求完畢后將此監聽事件remove掉!

package com.cyn.mt

import android.content.res.Resources import android.os.Bundle import android.os.Handler import android.util.DisplayMetrics import android.view.LayoutInflater.from import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.title_layout.view.* class MainActivity : AppCompatActivity() { //屏幕寬 var screenWidth = 0 //屏幕高 var screenHeight = 0 //tabLayout的文本和圖片 private val tabTextData = arrayOf("常用葯品", "夜間送葯", "隱形眼鏡", "成人用品", "醫療器械", "全部商家") private val tabIconData = arrayOf( R.mipmap.tab_icon, R.mipmap.tab_icon, R.mipmap.tab_icon, R.mipmap.tab_icon, R.mipmap.tab_icon, R.mipmap.tab_icon ) private var fragmentData = mutableListOf<Fragment>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initView() initData() } private fun initView() { //獲取屏幕寬高 val resources: Resources = this.resources val dm: DisplayMetrics = resources.displayMetrics screenWidth = dm.widthPixels screenHeight = dm.heightPixels //狀態欄沉浸 StatusBarUtil.immersive(this) //titleBar填充 StatusBarUtil.setPaddingSmart(this, titleBar) //狀態欄字體顏色設置為黑色 StatusBarUtil.darkMode(this) //動態設置ViewPager高度 coordinatorScrollView.post { val layoutParams = viewPager.layoutParams layoutParams.width = screenWidth layoutParams.height = coordinatorScrollView.height - tabLayout.height viewPager.layoutParams = layoutParams } } private fun initData() { //我模擬在頭部動態添加三個布局,就用圖片代替了,要設置的圖片高度都是我提前算好的,根據屏幕的比例來計算的 val titleView1 = getTitleView(screenWidth * 0.42F, R.mipmap.title1) val titleView2 = getTitleView(screenWidth * 0.262F, R.mipmap.title2) titleLinerLayout.addView(titleView1) titleLinerLayout.addView(titleView2) //設置最大滑動距離 titleLinerLayout.post { coordinatorScrollView.setMaxScrollY(titleLinerLayout.height) } //用於請求網絡后動態添加子布局 Handler().postDelayed({ val titleView3 = getTitleView(screenWidth * 0.589F, R.mipmap.title3) titleLinerLayout.addView(titleView3) //再次設置最大滑動距離 titleLinerLayout.post { coordinatorScrollView.setMaxScrollY(titleLinerLayout.height) } }, 200) //添加TabLayout for (i in tabTextData.indices) { tabLayout.addTab(tabLayout.newTab()) tabLayout.getTabAt(i)!!.setText(tabTextData[i]).setIcon(tabIconData[i]) //添加Fragment fragmentData.add(TestFragment.newInstance(tabTextData[i])) } //Fragment ViewPager viewPager.adapter = ViewPagerAdapter(supportFragmentManager, fragmentData) //TabLayout關聯ViewPager tabLayout.setupWithViewPager(viewPager) //設置TabLayout數據 for (i in tabTextData.indices) { tabLayout.getTabAt(i)!!.setText(tabTextData[i]).setIcon(tabIconData[i]) } } /** * 獲取一個title布局 * 我這里就用三張圖片模擬的 * * @height 要設置的圖片高度 */ private fun getTitleView(height: Float, res: Int): View { val inflate = from(this).inflate(R.layout.title_layout, null, false) val layoutParams = inflate.titleImage.layoutParams layoutParams.width = screenWidth layoutParams.height = height.toInt() inflate.titleImage.setImageResource(res) return inflate } }

最終效果

圖片

源碼Github地址:https://github.com/ThirdGoddess/ViewPager

Android高級開發系統進階筆記、最新面試復習筆記PDF,我的GitHub

文末

您的點贊收藏就是對我最大的鼓勵!
歡迎關注我,分享Android干貨,交流Android技術。
對文章有何見解,或者有何技術問題,歡迎在評論區一起留言討論!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM