效果图如下:
点击置顶ScrollView
这个置顶是滑动的置顶,不包括外层布局。
好了,效果图看到了,你有没有动力开始写代码呢?
创建一个SlideLayoutDemo的项目
然后在res下新建一个network_security_config.xml
里面的代码很少,如下
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
然后进入AndroidManifest.xml
增加了网络权限和http访问许可
在app的build.gradle中添加相关的依赖
先增加DataBind的使用
dataBinding { enabled = true }
//Google Material控件,以及迁移到AndroidX下一些控件的依赖 implementation 'com.google.android.material:material:1.0.0' //图片加载框架 implementation 'com.github.bumptech.glide:glide:4.10.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
然后记得Sync
最后修改样式,打开res下的values下的styles.xml
现在进入到activity_main.xml中
这里面用了两个图片资源
top_bg.jpg
icon_return_top.png
布局中用到了一个自定义VIew, 新建一个GoTopNestedScrollView类
代码如下:
package com.llw.slidelayoutdemo; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.widget.NestedScrollView; /** * 回到顶部ScrollView */ public class GoTopNestedScrollView extends NestedScrollView implements View.OnClickListener { private ImageView goTopBtn;//展示置顶的图片按钮 private int screenHeight = 500;//屏幕高度 没有设置则默认500 public GoTopNestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } //设置滑动到多少出现 public void setScreenHeight(int screenHeight) { this.screenHeight = screenHeight; } //设置滚动置顶按钮以及其点击监听事件, public void setImageViewOnClickGoToFirst(ImageView goTopBtn) { this.goTopBtn = goTopBtn; this.goTopBtn.setOnClickListener(this); } //重写滚动改变返回的回调 // l oldl 分别代表水平位移 // t oldt 代表当前左上角距离Scrollview顶点的距离 @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); /** * 滑动距离超过500px,出现置顶按钮,可以做为自定义属性 * 滑动距离如果用户设置了使用用户的 如果用户没有设置使用默认的 */ //当 当前的左上角距离顶点距离 大于某个值的时候就显现置顶按钮出来 如果小于某个值就隐藏 if (screenHeight != 0) { if (t > screenHeight) { goTopBtn.setVisibility(VISIBLE); } else { goTopBtn.setVisibility(GONE); } } } //置顶按钮的点击事件监听 @Override public void onClick(View view) { //滑动到ScrollView的顶点 this.smoothScrollTo(0, 0); } }
activity_main.xml布局
<?xml version="1.0" encoding="utf-8"?> <layout><!--databind--> <!--相对--> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <!--协调布局--> <androidx.coordinatorlayout.widget.CoordinatorLayout 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" tools:context=".MainActivity"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <com.google.android.material.appbar.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="300dp" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <ImageView android:id="@+id/iv_top" android:layout_width="match_parent" android:layout_height="300dp" android:scaleType="centerCrop" android:src="@drawable/top_bg" /> <!--标题控件--> <androidx.appcompat.widget.Toolbar android:layout_width="match_parent" android:layout_height="80dp" app:contentInsetStart="0dp" app:layout_collapseMode="pin"> <LinearLayout android:id="@+id/fl_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFF" android:gravity="center" android:paddingTop="20dp" app:layout_collapseMode="pin"> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="在座的各位都是正人君子" android:textColor="#000" android:textSize="18sp" /> </LinearLayout> </androidx.appcompat.widget.Toolbar> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <com.llw.slidelayoutdemo.GoTopNestedScrollView android:id="@+id/go_top_scrollview" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/iv_one" android:layout_width="match_parent" android:layout_height="600dp" android:scaleType="centerCrop" /> <ImageView android:id="@+id/iv_two" android:layout_width="match_parent" android:layout_height="600dp" android:scaleType="centerCrop" /> <ImageView android:id="@+id/iv_three" android:layout_width="match_parent" android:layout_height="600dp" android:scaleType="centerCrop" /> <ImageView android:id="@+id/iv_four" android:layout_width="match_parent" android:layout_height="600dp" android:scaleType="centerCrop" /> <ImageView android:id="@+id/iv_five" android:layout_width="match_parent" android:layout_height="600dp" android:scaleType="centerCrop" /> </LinearLayout> </com.llw.slidelayoutdemo.GoTopNestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout> <!--置顶图标--> <ImageView android:id="@+id/ivReturnTop" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentRight="true" android:layout_alignParentBottom="true" android:layout_margin="10dp" android:src="@mipmap/icon_return_top" android:visibility="gone" /> </RelativeLayout> </layout>
这里还有一个状态栏工具类,代码如下:
package com.llw.slidelayoutdemo; import android.app.Activity; import android.graphics.Color; import android.os.Build; import android.view.View; import android.view.Window; import android.view.WindowManager; import java.lang.reflect.Field; import java.lang.reflect.Method; public class StatusBarUtils { /** * 兼容状态栏透明(沉浸式) * * @param activity */ public static void setImmersionStateMode(Activity activity) { StatusBarLightMode(activity); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT != Build.VERSION_CODES.LOLLIPOP) { // 透明状态栏 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 透明导航栏 // getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); window.setNavigationBarColor(Color.TRANSPARENT); } } /** * 设置状态栏黑色字体图标, * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android * * @param activity * @return 1:MIUUI 2:Flyme 3:android6.0 */ public static int StatusBarLightMode(Activity activity) { int result = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (MIUISetStatusBarLightMode(activity.getWindow(), true)) { result = 1; } else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) { result = 2; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); result = 3; } } return result; } /** * 设置状态栏图标为深色和魅族特定的文字风格 * 可以用来判断是否为Flyme用户 * * @param window 需要设置的窗口 * @param dark 是否把状态栏字体及图标颜色设置为深色 * @return boolean 成功执行返回true */ public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) { boolean result = false; if (window != null) { try { WindowManager.LayoutParams lp = window.getAttributes(); Field darkFlag = WindowManager.LayoutParams.class .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); Field meizuFlags = WindowManager.LayoutParams.class .getDeclaredField("meizuFlags"); darkFlag.setAccessible(true); meizuFlags.setAccessible(true); int bit = darkFlag.getInt(null); int value = meizuFlags.getInt(lp); if (dark) { value |= bit; } else { value &= ~bit; } meizuFlags.setInt(lp, value); window.setAttributes(lp); result = true; } catch (Exception e) { } } return result; } /** * 设置状态栏字体图标为深色,需要MIUIV6以上 * * @param window 需要设置的窗口 * @param dark 是否把状态栏字体及图标颜色设置为深色 * @return boolean 成功执行返回true */ public static boolean MIUISetStatusBarLightMode(Window window, boolean dark) { boolean result = false; if (window != null) { Class clazz = window.getClass(); try { int darkModeFlag = 0; Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); darkModeFlag = field.getInt(layoutParams); Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); if (dark) { extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体 } else { extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体 } result = true; } catch (Exception e) { } } return result; } }
接下来进入到MainActivity中
package com.llw.slidelayoutdemo; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import android.graphics.Color; import android.os.Bundle; import android.util.DisplayMetrics; import com.bumptech.glide.Glide; import com.google.android.material.appbar.AppBarLayout; import com.llw.slidelayoutdemo.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView();//初始化视图 showImg();//显示网络图片 } /** * 初始化视图 */ private void initView() { binding = DataBindingUtil.setContentView(this, R.layout.activity_main);//绑定视图 StatusBarUtils.setImmersionStateMode(this);//透明状态栏 //滑动偏移监听事件 binding.appbar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { @Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { int toolbarHeight = appBarLayout.getTotalScrollRange(); int dy = Math.abs(verticalOffset); if (dy <= toolbarHeight) { float scale = (float) dy / toolbarHeight; float alpha = scale * 255; binding.flLayout.setBackgroundColor(Color.argb((int) alpha, 255, 255, 255));//渐变背景透明度 binding.tvTitle.setTextColor(Color.argb((int) alpha,0,0,0));//渐变文字颜色透明度 } } }); //设置点击置顶的ImageView binding.goTopScrollview.setImageViewOnClickGoToFirst(binding.ivReturnTop); //ScrollView滑动超过屏幕高度则显示置顶按钮,不设置的话就会使用自定义View中的默认高度 DisplayMetrics metric = new DisplayMetrics();//获取屏幕高度 getWindowManager().getDefaultDisplay().getMetrics(metric); binding.goTopScrollview.setScreenHeight(metric.heightPixels);//设置高度 } /** * 使用Glide加载显示网络图片 记得加网络权限和http地址url访问许可 */ private void showImg() { Glide.with(this) .load("http://gank.io/images/2c924db2a1b84c5d8fdb9f8c5f6d1b71") .into(binding.ivOne); Glide.with(this) .load("http://gank.io/images/92989b6a707b44dfb1c734e8d53d39a2") .into(binding.ivTwo); Glide.with(this) .load("http://gank.io/images/4817628a6762410895f814079a6690a1") .into(binding.ivThree); Glide.with(this) .load("http://gank.io/images/f9523ebe24a34edfaedf2dd0df8e2b99") .into(binding.ivFour); Glide.with(this) .load("http://gank.io/images/4002b1fd18544802b80193fad27eaa62") .into(binding.ivFive); } }
运行起来效果就是这样的。
点击置顶ScrollView