文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android CoordinatorLayout+AppBarLayout顶部栏吸顶效果的实现

2023-10-06 21:31

关注

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

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯