Android:下拉上滑显示与隐藏导航栏和状态栏(NestedScrollView实现 + RecyclerView实现)

话不多说先看效果:

小米商店效果图:
这里写图片描述

个人实现效果图:

这里写图片描述

前几天小米手机应用商店更新,看到这样的效果,之后看到其他app也有这样的效果,闲来无事搞一下。

首先怎么实现呢?

思路:不考虑滑动,先要实现全屏,而且上方状态栏显示,之后重写Scrollview,监听滑动事件,当滑动一定距离时不断更新action bar的透明度。

第一步实现全屏:全屏样式实现参考
这里写图片描述

主要代码:

  protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
        setContentView(R.layout.activity_nac);
        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decorView.setSystemUiVisibility(option);
            getWindow().setNavigationBarColor(Color.TRANSPARENT);
            getWindow().setStatusBarColor(Color.TRANSPARENT);
        }
        ActionBar actionBar = getSupportActionBar();
        actionBar.hide();
    }

然后就是重写ScrollView:

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.util.LogPrinter;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

/**
 * Created by ZHL on 2016/12/19.
 */

public class FadingScrollView extends ScrollView {

    private static String TAG = "-----------FadingScrollView----------";
    //渐变view
    private View fadingView;
    //滑动view的高度,如果这里fadingHeightView是一张图片,
    // 那么就是这张图片上滑至完全消失时action bar 完全显示,
    // 过程中透明度不断增加,直至完全显示
    private View fadingHeightView;
    private int oldY;
    //滑动距离,默认设置滑动500 时完全显示,根据实际需求自己设置
    private int fadingHeight = 500;

    public FadingScrollView(Context context) {
        super(context);
    }

    public FadingScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FadingScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setFadingView(View view){this.fadingView = view;}
    public void setFadingHeightView(View v){this.fadingHeightView = v;}

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if(fadingHeightView != null)
        fadingHeight = fadingHeightView.getMeasuredHeight();
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
//        l,t  滑动后 xy位置,
//        oldl lodt 滑动前 xy 位置-----
        float fading = t>fadingHeight ? fadingHeight : (t > 30 ? t : 0);
        updateActionBarAlpha( fading / fadingHeight);

    }

    void updateActionBarAlpha(float alpha){
        try {
            setActionBarAlpha(alpha);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setActionBarAlpha(float alpha) throws Exception{
        if(fadingView==null){
            throw new Exception("fadingView is null...");
        }
        fadingView.setAlpha(alpha);
    }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_nac"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.qienanxiang.activity.NacActivity">
    <com.qienanxiang.widget.FadingScrollView
        android:id="@+id/nac_root"
        android:overScrollMode="never"
        android:scrollbars="none"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/nac_image"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:scaleType="fitXY"
                android:src="@mipmap/icon_bg2"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:textSize="22dp"
                android:text="其他布局:最好是listview、recycleView"/>
                <!--......
                建议这里放置listview 或者 recycleView ,LZ为了省事直接放了多个textview来撑起ScrollView。。。--!>
        </LinearLayout>
    </com.qienanxiang.widget.FadingScrollView>
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:background="@android:color/transparent">
        <View
            android:id="@+id/nac_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/colorPrimary"/>
        <TextView
            android:layout_marginRight="@dimen/activity_horizontal_margin"
            android:layout_marginLeft="@dimen/activity_horizontal_margin"
            android:layout_marginTop="25dp"
            android:layout_marginBottom="10dp"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:background="#FFFFFF"
            android:text="自定义布局"/>
    </FrameLayout>
</RelativeLayout>

activity 使用:

public class NacActivity extends AppCompatActivity {

    private View layout;
    private FadingScrollView fadingScrollView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
        setContentView(R.layout.activity_nac);
        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decorView.setSystemUiVisibility(option);
            getWindow().setNavigationBarColor(Color.TRANSPARENT);
            getWindow().setStatusBarColor(Color.TRANSPARENT);
        }
        ActionBar actionBar = getSupportActionBar();
        actionBar.hide();

        layout = findViewById(R.id.nac_layout);
        layout.setAlpha(0);

        fadingScrollView = (FadingScrollView)findViewById(R.id.nac_root);
        fadingScrollView.setFadingView(layout);
        fadingScrollView.setFadingHeightView(findViewById(R.id.nac_image));
    }
}

说一下上方所谓的action bar,这里并没有直接使用actionbar,而是使用自己的布局,因为使用action bar ,就要在activity中对它进行声明设置一些属性,而要实现状态栏透明 这样的效果,代码中已经设置actionbar hide,可能会起冲突,所以在写demo时就直接放弃了actionbar。

这样就大功告成了,如果有说的不正确的地方,欢迎留言一起讨论!

完整Demo

2019.06.26更新:

最近在电商项目中用到这个效果,之前也是用NestedScrollView 实现,但是牵涉到多图时会导致页面非常的卡顿,发现使用NestedScrollView 嵌套recyclerview时 recycleview本身的复用机制就不起作用了,而是直接加载所有的item将滑动交给NestedScrollView 处理,这时图片过多就会导致界面卡顿,后来使用三方图片处理库回收,但是页面打开过多时,就导致多次触发GC,同样会卡顿,所有最后还是改为使用recyclerView 实现复杂的布局,方便同样的item的复用,所以这里更新一下使用recyclerView 实现该效果。

首先想到的是使用同样的方法实现,但是recyclerView的addOnScrollListener中给出的是相对滑动距离,实现上述效果是需要滑动的绝对距离,怎么获得绝对距离就成了关键,看代码:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        			//当然前提是第一个item 存在,并且高度 > 隐藏消失所需要的滑动距离
            		if(manager.findFirstVisibleItemPosition() == 0){
            				//第一个view
	                		View firstView = manager.findViewByPosition(0);
	                        int height = 0 - firstView.getTop(); //第一个view距离顶部的距离
	                        //height 就是我们想要的绝对滑动距离
	                        //Session.getWidth() = 隐藏消失所需要的滑动距离
	                        int baseHeight = Session.getWidth() - view1.getHeight();
	                        //然后就是根据滑动的距离来给view设置Alpha 实现效果
	                        view1.setAlpha((baseHeight - height) * 1.0f / baseHeight);
	                        view2.setAlpha(1 - (baseHeight - height) * 1.0f / baseHeight);
            		}
            }
        });

关于ScrollView 和NestedScrollView 本意上二者没啥区别,就像recyclerView是listview的替代品一样,NestedScrollView 是ScrollView的替代品,Nested既是嵌套的意思,嵌套recyclerView时,只需要将recyclerview的滑动交给NestedScrollView 处理,在NestedScrollView的子布局中添加android:nestedScrollingEnabled=“false” 即可。

  • 11
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
以下是一个Android RecyclerView实现刷新下加载和工具的示例代码: 在build.gradle文件中添加以下依赖项: ``` implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'com.android.support:appcompat-v7:28.0.0' ``` 添加以下布局文件: ``` <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </android.support.v4.widget.SwipeRefreshLayout> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> ``` 在Activity或Fragment中添加以下代码: ``` private SwipeRefreshLayout swipeRefreshLayout; private RecyclerView recyclerView; private Toolbar toolbar; private LinearLayoutManager layoutManager; private MyAdapter adapter; // 在onCreate中添加以下代码 swipeRefreshLayout = findViewById(R.id.swipe_refresh_layout); recyclerView = findViewById(R.id.recycler_view); toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); adapter = new MyAdapter(dataList); recyclerView.setAdapter(adapter); swipeRefreshLayout.setColorSchemeColors(getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorAccent), getResources().getColor(R.color.colorPrimaryDark)); swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { // 下刷新操作 // 可以在这里进行网络请求等操作 // 请求完成后,在主线程调用setRefreshing(false)方法结束刷新动画 swipeRefreshLayout.setRefreshing(false); } }); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE && !recyclerView.canScrollVertically(1)) { // 上加载操作 // 可以在这里进行网络请求等操作 adapter.notifyDataSetChanged(); } } }); ``` 其中,MyAdapter是RecyclerView的适配器,dataList是数据列表。在适配器中,可以定义ViewHolder和绑定数据等方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值