现在app很多有粘性头部的效果,网上搜了下实现方法,发现大同小异,大部分都是根据recyclerview的itemdecoration来实现。我在做这个功能的时候可能没有这么麻烦,直接根据recyclerview的滑动监听来实现这个效果的,好了,先看图:
一、实现布局
<?xml version="1.0" encoding="utf-8"?> <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"> <View android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="@dimen/statusbar_view_height" android:background="@color/gray_f2f2f2" /> <com.aspsine.swipetoloadlayout.SwipeToLoadLayout android:id="@+id/swipeToLoadLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="blocksDescendants" android:overScrollMode="never" app:default_to_loading_more_scrolling_duration="500" app:load_more_complete_delay_duration="0" app:load_more_final_drag_offset="@dimen/load_more_final_offset_google" app:load_more_trigger_offset="@dimen/load_more_trigger_offset_google" app:refresh_final_drag_offset="@dimen/refresh_final_offset_google" app:refresh_trigger_offset="@dimen/refresh_trigger_offset_google" app:swipe_style="blew"> <include android:id="@id/swipe_refresh_header" layout="@layout/layout_refresh_header" /> <android.support.v7.widget.RecyclerView android:id="@id/swipe_target" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:overScrollMode="never" /> <include android:id="@id/swipe_load_more_footer" layout="@layout/layout_refresh_footer" /> </com.aspsine.swipetoloadlayout.SwipeToLoadLayout> <!-- 标题栏布局 --> <include layout="@layout/item_autotrophy_title_bar" /> <!-- 筛选栏 --> <include layout="@layout/item_autotrophy_type" /> </RelativeLayout>
标题栏布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_bg" android:layout_width="match_parent" android:layout_height="@dimen/home_banner_det" android:layout_below="@id/tv" android:layout_marginTop="-1.5dp" android:background="@color/white_ffffff" android:orientation="horizontal"> <LinearLayout android:id="@+id/ll_back" android:layout_width="44dp" android:layout_height="44dp" android:gravity="center_vertical|left" android:paddingBottom="10dp" android:paddingTop="10dp"> <ImageView android:id="@+id/iv_back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:src="@drawable/back_gray" /> </LinearLayout> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center" android:layout_marginRight="44dp" android:layout_weight="1" android:gravity="center" android:text="" android:textColor="@color/gray_333333" android:textSize="16sp" /> </LinearLayout>
筛选栏布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_title_type" android:layout_width="match_parent" android:layout_height="@dimen/autotrophy_type_height" android:layout_marginTop="@dimen/autotrophy_height" android:background="@color/white_ffffff" android:orientation="vertical"> <View android:id="@+id/view_line" android:layout_width="match_parent" android:layout_height="0.5dp" android:layout_alignParentBottom="true" android:background="@color/gray_dcdcdc" /> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp" android:orientation="horizontal"> <LinearLayout android:id="@+id/ll_autotrophy_price" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="horizontal"> <ImageView android:id="@+id/iv_sort_price" style="@style/sort_icon_style" /> <TextView android:id="@+id/tv_autotrophy_price" style="@style/autotrohpy_text" android:text="按价格" /> <ImageView android:id="@+id/iv_autotrophy_price" style="@style/autotrophy_imageview" android:src="@drawable/autotrofy_bottom" /> </LinearLayout> <View style="@style/view_height" /> <LinearLayout android:id="@+id/ll_autotrophy_by_sales" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="horizontal"> <ImageView android:id="@+id/iv_sort_sales" style="@style/sort_icon_style" /> <TextView android:id="@+id/tv_autotrophy_by_sales" style="@style/autotrohpy_text" android:text="按销量" /> <ImageView android:id="@+id/iv_autotrophy_by_sales" style="@style/autotrophy_imageview" android:src="@drawable/autotrofy_bottom" /> </LinearLayout> <View style="@style/view_height" /> <LinearLayout android:id="@+id/ll_autotrophy_by_star" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="horizontal"> <ImageView android:id="@+id/iv_sort_star" style="@style/sort_icon_style" /> <TextView android:id="@+id/tv_autotrophy_by_star" style="@style/autotrohpy_text" android:text="按评分" /> <ImageView android:id="@+id/iv_autotrophy_by_star" style="@style/autotrophy_imageview" android:src="@drawable/autotrofy_bottom" /> </LinearLayout> <View style="@style/view_height" /> <LinearLayout android:id="@+id/ll_autotrophy_by_rent" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1.2" android:gravity="center" android:orientation="horizontal"> <TextView android:id="@+id/tv_autotrophy_by_rent" style="@style/autotrohpy_text" android:text="出租类型" /> <ImageView android:id="@+id/iv_autotrophy_by_rent" style="@style/autotrophy_imageview" android:src="@drawable/autotrofy_bottom" /> </LinearLayout> </LinearLayout> <View style="@style/ViewStyle" android:background="@color/gray_dcdcdc" /> </LinearLayout>
二、标题栏透明度的滑动变化
首先得默认标题栏的透明度为0,添加代码:
/** * 设置标题栏背景透明度 * * @param alpha 透明度 */ private void setSystemBarAlpha(int alpha) { // if (alpha >= 125) { // alpha = 125; // } else { //标题栏渐变。a:alpha透明度 r:红 g:绿 b蓝 // titlebar.setBackgroundColor(Color.rgb(57, 174, 255));//没有透明效果 // titlebar.setBackgroundColor(Color.argb(alpha, 57, 174, 255));//透明效果是由参数1决定的,透明范围[0,255] // titlebar.getBackground().setAlpha(alpha); llBg.setBackgroundColor(Color.argb(alpha, 255, 255, 255)); viewLine.setBackgroundColor(Color.argb(alpha, 220, 220, 220)); tvTitle.setTextColor(Color.argb(alpha, 51, 51, 51)); // } }
然后就可以设置recyclerview的滑动监听方法addOnScrollListener(),来进行透明度的变化。
在onScrolled()方法中做透明度变化的操作:
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); int firstPosition = layoutManager.findFirstVisibleItemPosition();
if (firstPosition > 1 || getScollYDistance() >= UIUtils.getDimensResource(mContext, R.dimen.autotrophy_height) - UIUtils.getDimensResource(mContext, R.dimen.title_bar_auto)) { setSystemBarAlpha(255); StatusBarUtil.setColor(AutoTrophyActivity.this, UIUtils.getColorResource(mContext, R.color.gray_f2f2f2)); } else { float dimen = UIUtils.getDimensResource(mContext, R.dimen.autotrophy_height) - UIUtils.getDimensResource(mContext, R.dimen.title_bar_auto); int alpha = (int) ((getScollYDistance() / dimen) * 255); setSystemBarAlpha(alpha); StatusBarUtil.setTranslucentForImageView(AutoTrophyActivity.this, 0, null); }
其中setSystemBarAlpha(int alpha)这个方法就是根据滑动的距离来进行透明度变化的设置,方法在前面已经展示过了。
三、设置筛选栏固定到标题栏底部
其实原理和标题栏透明度的变化差不多,只不过一个是根据滑动的距离来设置透明度的变化,一个是根据滑动的距离来设置其在竖直方向的位置。好了,下面就是实现的方法,同样是在onScrolled()方法中来进行操作:
top = llTitleType.getTop(); // 筛选栏的y方向位置 titleTop = llBg.getTop(); //标题栏的y方向的位置 detTop = top - getScollYDistance();//滑动过程中筛选栏的位置 = 当前位置 - y方向滑动的距离 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 4.4以上 if (detTop <= (titleTop + UIUtils.getDimensResource(mContext, R.dimen.home_banner_det)) && detTop > 0) { // detTop小于标题栏所在的位置 llTitleType.setY(titleTop + UIUtils.getDimensResource(mContext, R.dimen.autotrophy_type_height) + headHeight); } else if (detTop > 0) {// llTitleType.setY(detTop + footerHead + headHeight); } } else { if (detTop <= titleTop && detTop > 0) { llTitleType.setY(titleTop + UIUtils.getDimensResource(mContext, R.dimen.autotrophy_type_height)); } else if (detTop > 0) { llTitleType.setY(detTop + footerHead); } }
其中的footerHead 以及 headHeight 是上拉加载更多高度和下拉刷新的高度,这两个参数是在你添加了刷新和上拉加载更多功能的时候需要添加这两个参数来进行位置适配,如果没有这两个功能,那可以忽略这两个参数。。。
如果有这两个功能,那么首先需要在刷新控件中添加监听方法,可以获取到每次刷新或者上拉加载时移动的距离:
刷新监听
public interface OnHeaderRefreshListener { void onHeaderRefresh(int y, HeaderRefreshView headerRefreshView); }
@Override public void onMove(int y, boolean isComplete, boolean automatic) { float alpha = y / (float) mTriggerOffset; ViewCompat.setAlpha(progressView, alpha); if (onHeaderRefreshListener != null) { onHeaderRefreshListener.onHeaderRefresh(y, this); } }
上拉监听
public interface OnFooterRefreshListener { void onFooterRefresh(int y, FooterRefreshView footerRefreshView); }
@Override public void onMove(int y, boolean isComplete, boolean automatic) { // float alpha = -y / (float) mTriggerOffset; // ViewCompat.setAlpha(progressView, alpha); // if (!isComplete) { // progressView.setProgressRotation(-y / (float) mFinalOffset); // } if (onFooterRefreshListener != null) { onFooterRefreshListener.onFooterRefresh(y, this); } }
然后在recyclerview 监听的同时也需要做这两个监听来进行位置的变化:
@Override public void onHeaderRefresh(int y, HeaderRefreshView headerRefreshView) { headHeight = y; int titleHeght = llBg.getTop(); // 标题栏位置 if (y > 0) { //向下下拉刷新 llBg.setY((titleHeght + y)); } else {//没有刷新 llBg.setY(titleHeght); } int top = llTitleType.getTop();//筛选栏位置 if (y > 0) {//向下下拉刷新 llTitleType.setY(top + y); } }
@Override public void onFooterRefresh(int y, FooterRefreshView footerRefreshView) { footerHead = y; if (getScollYDistance() == 0 && y < 0) {//滑动距离为0 并且向上上拉加载更多 llTitleType.setY(llTitleType.getTop() + footerHead); } else if (getScollYDistance() < UIUtils.getDimensResource(this, R.dimen.autotrophy_height) - llBg.getTop()) { // recyclerview 列表不满一屏的时候处理方式 llTitleType.setY(llTitleType.getTop() - getScollYDistance() + footerHead); } }
做了这个监听后,记得在刷新或者上拉结束后,让headHeight 以及 footerHead 归零,要不然会存在问题。。。
四、小结
按照这个思路来做就可以做出gif图的效果了。现在觉得还是挺简单方便的,当时做的过程中碰到了很多问题,刚开始我们没要求刷新,基本没问题,快做完产品觉得加个刷新比较好,添加刷新后就出现新的问题了,重新捋了下思路终于实现了。不过应该还会存在问题,后期大家发现问题欢迎给我留言指正~~~