Android CoordinatorLayout+AppBarLayout顶部栏吸顶效果的实现
1.控件简介。
CoordinatorLayout遵循Material 风格,包含在 support Library中,结合AppbarLayout, CollapsingToolbarLayout等 可 产生各种炫酷的折叠悬浮效果。
作为最上层的View
作为一个容器与一个或者多个子View进行交互
CoordinatorLayout is intended for two primary use cases: As a top-level application decor or chrome layout; As a container for a specific interaction with one or more child views.
常见结合体-AppBarLayout:
它是继承与LinearLayout的,默认的方向是Vertical:
appbarLayout的滑动flag:
我们可以通过两种方法设置这个Flag:
方法一:setScrollFlags(int)
方法二:app:layout_scrollFlags="scroll|enterAlways"
AppBarLayout必须作为CoordinatorLayout的直接子View,否则它的大部分功能将不会生效,如layout_scrollFlags等。
2.效果图。
画面収録 2023-05-26 20.10.41
3.所用到的依赖。
//控件用到的依赖api "androidx.appcompat:appcompat:1.4.0"//沉浸式状态栏框架导入// 基础依赖包,必须要依赖api "com.geyifeng.immersionbar:immersionbar:3.2.2"// kotlin扩展(可选)api "com.geyifeng.immersionbar:immersionbar-ktx:3.2.2"//刷新和加载Layout导入,核心必须依赖api "com.scwang.smart:refresh-layout-kernel:2.0.1"api "com.scwang.smart:refresh-header-classics:2.0.1" //经典刷新头api "com.scwang.smart:refresh-header-material:2.0.1" //谷歌刷新头api 'com.github.hackware1993:MagicIndicator:1.7.0'
4.布局代码。
注意一点:那个可滑动的 View 不能是 ListView,ScrollView 这种旧包下的控件,否则也是不起作用的。
app:layout_scrollFlags="scroll|enterAlways,前面已经说到app:layout_scrollFlags=scroll的时候,这个View会跟着滚动事件响应,app:layout_scrollFlags=“enterAlways”的时候这个View会响应下拉事件。所以呈现出来的结果应该是我们在上拉的时候toolBar会隐藏,下拉的时候toolBar会出来;如果当我们的toolBar等于app:layout_scrollFlags="scroll|snap"的时候 ,app:layout_scrollFlags=scroll的时候,这个View会跟着滚动事件响应,app:layout_scrollFlags=“snap”的时候在Scroll滑动事件结束以前 ,如果这个View部分可见,那么这个View会停在最接近当前View的位置。具体就自己研究了。
再说我这个需求的效果:栏常驻,有两个Toolbar,第一个Toolbar实际上是一个占位View,第二个Toolbar才是真正的常驻栏,上滑的时候沉浸式栏渐变,上滑到二级与栏吸顶,app:layout_scrollFlags="scroll|exitUntilCollapsed",上拉的时候,这个View会跟着滑动直到折叠成第一个Toolbar大小,下方的ViewPager设置app:layout_behavior="@string/appbar_scrolling_view_behavior"属性才可以与Toolbar进行吸顶,这样就与第二个Toolbar正常吸顶了。
BaseActivtiy的代码。
package com.phone.library_common.baseimport android.app.ActivityManagerimport android.content.Intentimport android.content.res.Configurationimport android.content.res.Resourcesimport android.graphics.Colorimport android.graphics.drawable.GradientDrawableimport android.os.Bundleimport android.os.Looperimport android.os.Processimport android.view.Gravityimport android.view.Viewimport android.view.WindowManagerimport android.widget.FrameLayoutimport android.widget.TextViewimport android.widget.Toastimport androidx.databinding.DataBindingUtilimport androidx.databinding.ViewDataBindingimport com.gyf.immersionbar.ImmersionBarimport com.phone.library_common.BaseApplicationimport com.phone.library_common.Rimport com.phone.library_common.manager.ActivityPageManagerimport com.phone.library_common.manager.CrashHandlerManagerimport com.phone.library_common.manager.LogManagerimport com.phone.library_common.manager.ResourcesManagerimport com.phone.library_common.manager.ToolbarManagerimport com.phone.library_common.manager.ToolbarManager.Companion.assistActivityimport com.qmuiteam.qmui.widget.QMUILoadingViewimport com.trello.rxlifecycle3.components.support.RxAppCompatActivityabstract class BaseBindingRxAppActivity : RxAppCompatActivity(), IBaseView { private val TAG = BaseBindingRxAppActivity::class.java.simpleName protected lateinit var loadView: QMUILoadingView protected lateinit var layoutParams: FrameLayout.LayoutParams //该类绑定的ViewDataBinding protected lateinit var mDatabind: DB protected lateinit var mRxAppCompatActivity: RxAppCompatActivity protected lateinit var mBaseApplication: BaseApplication private var mActivityPageManager: ActivityPageManager? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mRxAppCompatActivity = this mBaseApplication = application as BaseApplication mActivityPageManager = ActivityPageManager.get() mActivityPageManager?.addActivity(this) mDatabind = DataBindingUtil.setContentView(this, initLayoutId()) mDatabind.lifecycleOwner = mRxAppCompatActivity initData() initViews() loadView = QMUILoadingView(this) loadView.also { it.visibility = View.GONE it.setSize(100) it.setColor(ResourcesManager.getColor(R.color.color_333333)) } layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT ) layoutParams.gravity = Gravity.CENTER addContentView(loadView, layoutParams) initLoadData() } override fun onConfigurationChanged(newConfig: Configuration) { //非默认值 if (newConfig.fontScale != 1f) { resources } super.onConfigurationChanged(newConfig) } override fun getResources(): Resources? { //还原字体大小 val res = super.getResources() //非默认值 if (res.configuration.fontScale != 1f) { val newConfig = Configuration() newConfig.setToDefaults() //设置默认 res.updateConfiguration(newConfig, res.displayMetrics) } return res } protected abstract fun initLayoutId(): Int protected open fun setToolbar(isDarkFont: Boolean) { if (isDarkFont) { ImmersionBar.with(this) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度 .statusBarDarkFont(isDarkFont) .statusBarColor(R.color.color_FFFFFFFF) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .keyboardEnable(true) .init() } else { ImmersionBar.with(this) .statusBarDarkFont(isDarkFont) .statusBarColor(R.color.color_FF198CFF) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .keyboardEnable(true) .init() } ToolbarManager.assistActivity(findViewById(android.R.id.content)) } protected open fun setToolbar(isDarkFont: Boolean, isResizeChildOfContent: Boolean) { if (isDarkFont) { ImmersionBar.with(this) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度 .statusBarDarkFont(isDarkFont) .statusBarColor(R.color.color_FFFFFFFF) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .keyboardEnable(true) .init() } else { ImmersionBar.with(this) .statusBarDarkFont(isDarkFont) .statusBarColor(R.color.color_FF198CFF) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .keyboardEnable(true) .init() } if (isResizeChildOfContent) { ToolbarManager.assistActivity(findViewById(android.R.id.content)) } } protected open fun setToolbar(isDarkFont: Boolean, color: Int) { if (isDarkFont) { ImmersionBar.with(this) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度 .statusBarDarkFont(isDarkFont) .statusBarColor(color) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .init() } else { ImmersionBar.with(this) .statusBarDarkFont(isDarkFont) .statusBarColor(color) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .init() } ToolbarManager.assistActivity(findViewById(android.R.id.content)) } protected open fun setToolbar( isDarkFont: Boolean, color: Int, isResizeChildOfContent: Boolean ) { if (isDarkFont) { ImmersionBar.with(this) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度 .statusBarDarkFont(isDarkFont) .statusBarColor(color) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .init() } else { ImmersionBar.with(this) .statusBarDarkFont(isDarkFont) .statusBarColor(color) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .init() } if (isResizeChildOfContent) { ToolbarManager.assistActivity(findViewById(android.R.id.content)) } } protected open fun setToolbar2(isDarkFont: Boolean, statusBarColor: Int) { if (isDarkFont) { ImmersionBar.with(this) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度 .statusBarDarkFont(isDarkFont) .statusBarColor(statusBarColor) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .keyboardEnable(true) .init() } else { ImmersionBar.with(this) .statusBarDarkFont(isDarkFont) .statusBarColor(statusBarColor) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .keyboardEnable(true) .init() } } protected open fun setToolbar2( isDarkFont: Boolean, statusBarColor: Int, isResizeChildOfContent: Boolean ) { if (isDarkFont) { val window = window window.clearFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION ) window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE) window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) window.statusBarColor = Color.TRANSPARENT // window.setNavigationBarColor(Color.TRANSPARENT); ImmersionBar.with(this) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度 .statusBarDarkFont(isDarkFont) .statusBarColor(statusBarColor) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .keyboardEnable(true) .init() } else { ImmersionBar.with(this) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度 .statusBarDarkFont(isDarkFont) .statusBarColor(statusBarColor) //状态栏颜色,不写默认透明色 // .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色,必须指定状态栏颜色才可以自动变色哦 .keyboardEnable(true) .init() val window = window window.clearFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION ) window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE) window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) window.statusBarColor = Color.TRANSPARENT // window.setNavigationBarColor(Color.TRANSPARENT); } if (isResizeChildOfContent) { assistActivity(findViewById(android.R.id.content)) } } protected abstract fun initData() protected abstract fun initViews() protected abstract fun initLoadData() protected open fun showToast(message: String?, isLongToast: Boolean) { // Toast.makeText(this, message, Toast.LENGTH_LONG).show(); if (!mRxAppCompatActivity.isFinishing) { val toast: Toast val duration: Int duration = if (isLongToast) { Toast.LENGTH_LONG } else { Toast.LENGTH_SHORT } toast = Toast.makeText(mRxAppCompatActivity, message, duration) toast.setGravity(Gravity.CENTER, 0, 0) toast.show() } } protected open fun showCustomToast( left: Int, right: Int, textSize: Int, textColor: Int, bgColor: Int, height: Int, roundRadius: Int, message: String?, isLongToast: Boolean ) { val frameLayout = FrameLayout(this) val layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT ) frameLayout.layoutParams = layoutParams val textView = TextView(this) val layoutParams1 = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, height) textView.layoutParams = layoutParams1 textView.setPadding(left, 0, right, 0) textView.textSize = textSize.toFloat() textView.setTextColor(textColor) textView.gravity = Gravity.CENTER textView.includeFontPadding = false val gradientDrawable = GradientDrawable() //创建drawable gradientDrawable.setColor(bgColor) gradientDrawable.cornerRadius = roundRadius.toFloat() textView.background = gradientDrawable textView.text = message frameLayout.addView(textView) val toast = Toast(this) toast.setView(frameLayout) if (isLongToast) { toast.duration = Toast.LENGTH_LONG } else { toast.duration = Toast.LENGTH_SHORT } toast.show() } open fun isOnMainThread(): Boolean { return Looper.getMainLooper().thread.id == Thread.currentThread().id } protected open fun startActivity(cls: Class<*>?) { val intent = Intent(this, cls) startActivity(intent) } protected open fun startActivityForResult(cls: Class<*>?, requestCode: Int) { val intent = Intent(this, cls) startActivityForResult(intent, requestCode) } open fun getActivityPageManager(): ActivityPageManager? { return mActivityPageManager } private fun killAppProcess() { LogManager.i(TAG, "killAppProcess") val manager = mBaseApplication.getSystemService(ACTIVITY_SERVICE) as ActivityManager val processInfos = manager.runningAppProcesses // 先杀掉相关进程,最后再杀掉主进程 for (runningAppProcessInfo in processInfos) { if (runningAppProcessInfo.pid != Process.myPid()) { Process.killProcess(runningAppProcessInfo.pid) } } LogManager.i(TAG, "执行killAppProcess,應用開始自殺") val crashHandlerManager = CrashHandlerManager.get() crashHandlerManager?.saveTrimMemoryInfoToFile("执行killAppProcess,應用開始自殺") try { Thread.sleep(1000) } catch (e: InterruptedException) { LogManager.i(TAG, "error") } Process.killProcess(Process.myPid()) // 正常退出程序,也就是结束当前正在运行的 java 虚拟机 System.exit(0) } override fun onDestroy() { if (mActivityPageManager?.mIsLastAliveActivity?.get() == true) { killAppProcess() } mActivityPageManager?.removeActivity(mRxAppCompatActivity) super.onDestroy() }}
Activtiy的代码。
package com.phone.module_main.mounting;import android.graphics.Color;import androidx.fragment.app.Fragment;import com.alibaba.android.arouter.facade.annotation.Route;import com.phone.library_common.adapter.TabFragmentStatePagerAdapter;import com.phone.library_common.base.BaseBindingRxAppActivity;import com.phone.library_common.manager.LogManager;import com.phone.library_common.manager.ResourcesManager;import com.phone.library_common.manager.ScreenManager;import com.phone.module_main.R;import com.phone.module_main.databinding.ActivityMountingBinding;import com.phone.module_main.mounting.adapter.MineCommonNavigatorAdapter;import com.phone.module_main.mounting.fragment.CommodityFragment;import net.lucode.hackware.magicindicator.ViewPagerHelper;import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;import java.util.ArrayList;import java.util.List;@Route(path = "/module_main/mounting")public class MountingActivity extends BaseBindingRxAppActivity { private static final String TAG = MountingActivity.class.getSimpleName(); private int imvBannerHeight; private int slideMaxHeight; @Override protected int initLayoutId() { return R.layout.activity_mounting; } @Override protected void initData() { } @Override protected void initViews() { setToolbar2(false, R.color.color_transparent, false); setMounting(); mDatabind.imvBack.setColorFilter(ResourcesManager.getColor(R.color.color_FFFFFFFF)); mDatabind.layoutBack.setOnClickListener(v -> { finish(); }); mDatabind.refreshLayout.setOnRefreshListener(refreshLayout -> { setData(); mDatabind.refreshLayout.finishRefresh(1000); }); setData(); } private void setMounting() { mDatabind.imvBanner.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { if (mDatabind.imvBanner.getHeight() > 0) { imvBannerHeight = mDatabind.imvBanner.getHeight(); slideMaxHeight = imvBannerHeight - ScreenManager.getDimenPx(R.dimen.dp_73) - ScreenManager.getDimenPx(R.dimen.dp_1); } }); mDatabind.appBarLayout.addOnOffsetChangedListener((appBarLayout1, verticalOffset) -> { if (verticalOffset < 0) { setToolbar2(true, R.color.color_transparent, false); double slideHeight = Math.abs(verticalOffset); if (slideHeight < slideMaxHeight) { int proportion = (int) ((slideHeight / slideMaxHeight) * 255); LogManager.i(TAG, "proportion******" + proportion); int color = Color.argb(proportion, 255, 255, 255); mDatabind.toolbar.setBackgroundColor(color); } else { mDatabind.toolbar.setBackgroundColor(ResourcesManager.getColor(R.color.color_FFFFFFFF)); } mDatabind.tevTitle.setTextColor(ResourcesManager.getColor(R.color.color_99000000)); mDatabind.imvBack.setColorFilter(ResourcesManager.getColor(R.color.color_99000000)); } else { setToolbar2(false, R.color.color_transparent, false); mDatabind.toolbar.setBackgroundColor(ResourcesManager.getColor(R.color.color_transparent)); mDatabind.tevTitle.setTextColor(ResourcesManager.getColor(R.color.color_FFFFFFFF)); mDatabind.imvBack.setColorFilter(ResourcesManager.getColor(R.color.color_FFFFFFFF)); } }); } private void setData() { List titleList = new ArrayList<>(); titleList.add("苹果手机"); titleList.add("口红"); titleList.add("包包"); //创建indicator适配器 MineCommonNavigatorAdapter mineCommonNavigatorAdapter = new MineCommonNavigatorAdapter(titleList); CommonNavigator commonNavigator = new CommonNavigator(this); commonNavigator.setAdjustMode(true);//自我调节位置,实现自我平分 commonNavigator.setAdapter(mineCommonNavigatorAdapter); mineCommonNavigatorAdapter.setOnIndicatorTapClickListener(position -> { mDatabind.viewPager.setCurrentItem(position); }); mDatabind.magicIndicator.setNavigator(commonNavigator); List fragmentList = new ArrayList<>(); for (int i = 0; i < titleList.size(); i++) { fragmentList.add(CommodityFragment.get(titleList.get(i))); } TabFragmentStatePagerAdapter tabFragmentStatePagerAdapter = new TabFragmentStatePagerAdapter(getSupportFragmentManager(), fragmentList); mDatabind.viewPager.setAdapter(tabFragmentStatePagerAdapter); ViewPagerHelper.bind(mDatabind.magicIndicator, mDatabind.viewPager); } @Override protected void initLoadData() { } @Override public void showLoading() { } @Override public void hideLoading() { }}
如对此有疑问,请联系qq1164688204。
推荐Android开源项目
项目功能介绍:RxJava2 和Retrofit2 项目,使用Kotlin+RxJava2+Retrofit2+MVP架构+组件化/Kotlin+Retrofit2+协程+MVVM架构+组件化,添加自动管理token 功能,添加RxJava2 生命周期管理,集成极光推送、阿里云Oss对象存储和高德地图定位功能。
项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2
来源地址:https://blog.csdn.net/NakajimaFN/article/details/130892776
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341