Android -- RecyclerView实现顶部吸附效果

1,先来看一下今天实现的效果:

2,这次的效果是使用ItemDecoration来实践的,来看一看我们的实现吧

  第一步:首先添加依赖,由于我们这些数据是请求网络的,所以一下我们添加网络框架依赖、RecyclerView、Glide依赖

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.qianmo.stickyitemdecoration"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })

    compile 'com.android.support:appcompat-v7:25.1.1'
    //添加reccycleview
    compile 'com.android.support:recyclerview-v7:25.1.1'
    //添加glide,需要依赖V4包
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'com.android.support:support-v4:25.1.1'
    //添加水波纹效果
    compile 'com.balysv:material-ripple:1.0.2'
    //添加butter注解
    compile 'com.jakewharton:butterknife:8.2.1'
    apt 'com.jakewharton:butterknife-compiler:8.2.1'
    //添加网络框架
    compile 'io.reactivex:rxjava:1.1.0'
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
    compile 'com.google.code.gson:gson:2.6.2'

    testCompile 'junit:junit:4.12'
}

  第二步:主界面的xml文件就只是一个简单的RecyclerView控件,首先来实例化我们的RecyclerView

recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

  第三步:获取网络请求数据,首先我们要创建MovieServiceApi接口,并使用GET请求访问目标接口

  请求地址

http://api.meituan.com/mmdb/movie/v2/list/rt/order/coming.json?ci=1&limit=12&token=&__vhost=api.maoyan.com&utm_campaign=AmovieBmovieCD-1&movieBundleVersion=6801&utm_source=xiaomi&utm_medium=android&utm_term=6.8.0&utm_content=868030022327462&net=255&dModel=MI%205&uuid=0894DE03C76F6045D55977B6D4E32B7F3C6AAB02F9CEA042987B380EC5687C43&lat=40.100673&lng=116.378619&__skck=6a375bce8c66a0dc293860dfa83833ef&__skts=1463704714271&__skua=7e01cf8dd30a179800a7a93979b430b2&__skno=1a0b4a9b-44ec-42fc-b110-ead68bcc2824&__skcy=sXcDKbGi20CGXQPPZvhCU3%2FkzdE%3D

  MovieServiceApi.java

package com.qianmo.stickyitemdecoration.api;

import com.qianmo.stickyitemdecoration.base.BaseResponse;
import com.qianmo.stickyitemdecoration.bean.MovieBean;

import retrofit2.http.GET;
import rx.Observable;

/**
 * Created by wangjitao on 2017/3/3 0003.
 * E-Mail:543441727@qq.com
 * 电影网API
 */

public interface MovieApiService {

    @GET("v2/list/rt/order/coming.json?ci=1&limit=12&token=&__vhost=api.maoyan.com&utm_campaign=AmovieBmovieCD-1&movieBundleVersion=6801&utm_source=xiaomi&utm_medium=android&utm_term=6.8.0&utm_content=868030022327462&net=255&dModel=MI%205&uuid=0894DE03C76F6045D55977B6D4E32B7F3C6AAB02F9CEA042987B380EC5687C43&lat=40.100673&lng=116.378619&__skck=6a375bce8c66a0dc293860dfa83833ef&__skts=1463704714271&__skua=7e01cf8dd30a179800a7a93979b430b2&__skno=1a0b4a9b-44ec-42fc-b110-ead68bcc2824&__skcy=sXcDKbGi20CGXQPPZvhCU3%2FkzdE%3D")
    Observable<BaseResponse<MovieBean>> getMovieDatas();
}

  创建数据接口响应的Bean

  BaseResponse.java

package com.qianmo.stickyitemdecoration.base;


/**
 * Created by wangjitao on 2017/3/3 0003.
 * E-Mail:543441727@qq.com
 * 数据返回体的基本接收类,由于本次接口没有code和message
 */

public class BaseResponse<T> {
    //    private int code;
//    private String message;
    private T data;

//    public String getMessage() {
//        return message;
//    }
//
//    public void setMessage(String message) {
//        this.message = message;
//    }
//
//    public int getCode() {
//        return code;
//    }
//
//    public void setCode(int code) {
//        this.code = code;
//    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

  MovieBean.java

package com.qianmo.stickyitemdecoration.bean;

import java.util.List;

/**
 * Created by wangjitao on 2017/3/3 0003.
 * E-Mail:543441727@qq.com
 * 用于数据接收实体类
 */

public class MovieBean {

    private List<ComingBean> coming ;

    public List<ComingBean> getComing() {
        return coming;
    }

    public void setComing(List<ComingBean> coming) {
        this.coming = coming;
    }


}

  ComingBean.java

package com.qianmo.stickyitemdecoration.bean;

import java.util.List;

/**
 * Created by Administrator on 2017/3/3 0003.
 * E-Mail:543441727@qq.com
 */

public class ComingBean {
    /**
     * boxInfo : 喵,即将上映
     * cat : 动作,科幻
     * civilPubSt : 0
     * comingTitle : 3月10日 周五
     * desc : 主演:亚伦·保尔,肖恩·宾,琳娜·海蒂
     * dir : 野末武志
     * dur : 116
     * effectShowNum : 0
     * fra : 日本
     * frt : 2016-08-19
     * globalReleased : false
     * headLineShow : false
     * headLinesVO : []
     * id : 346383
     * img : http://p0.meituan.net/w.h/movie/5be09f397d474988afaf930ac5346243715496.png
     * late : false
     * localPubSt : 0
     * mk : 0
     * nm : 最终幻想15:王者之剑
     * pn : 98
     * preShow : false
     * proScore : 0
     * proScoreNum : 0
     * pubDate : 1489075200000
     * pubDesc : 2017-03-10大陆上映
     * pubShowNum : 0
     * recentShowDate : 0
     * recentShowNum : 0
     * rt : 2017-03-10
     * sc : 0
     * scm : 法力渐微弱,勇士卫祖国
     * showCinemaNum : 0
     * showInfo : 2017-03-10 下周五上映
     * showNum : 0
     * showst : 4
     * snum : 903
     * star : 亚伦·保尔,肖恩·宾,琳娜·海蒂
     * ver : 2D
     * videoId : 83711
     * videoName : 魔法机械版预告片
     * videourl : http://maoyan.meituan.net/movie/videos/854x480df7f69dd367b4dd5829c98d1a5a07dfa.mp4
     * vnum : 9
     * weight : 1
     * wish : 22799
     * wishst : 0
     */

    private String boxInfo;
    private String cat;
    private int civilPubSt;
    private String comingTitle;
    private String desc;
    private String dir;
    private int dur;
    private int effectShowNum;
    private String fra;
    private String frt;
    private boolean globalReleased;
    private boolean headLineShow;
    private int id;
    private String img;
    private boolean late;
    private int localPubSt;
    private int mk;
    private String nm;
    private int pn;
    private boolean preShow;
    private int proScore;
    private int proScoreNum;
    private long pubDate;
    private String pubDesc;
    private int pubShowNum;
    private int recentShowDate;
    private int recentShowNum;
    private String rt;
    private int sc;
    private String scm;
    private int showCinemaNum;
    private String showInfo;
    private int showNum;
    private int showst;
    private int snum;
    private String star;
    private String ver;
    private int videoId;
    private String videoName;
    private String videourl;
    private int vnum;
    private int weight;
    private int wish;
    private int wishst;
    private List<?> headLinesVO;

    public String getBoxInfo() {
        return boxInfo;
    }

    public void setBoxInfo(String boxInfo) {
        this.boxInfo = boxInfo;
    }

    public String getCat() {
        return cat;
    }

    public void setCat(String cat) {
        this.cat = cat;
    }

    public int getCivilPubSt() {
        return civilPubSt;
    }

    public void setCivilPubSt(int civilPubSt) {
        this.civilPubSt = civilPubSt;
    }

    public String getComingTitle() {
        return comingTitle;
    }

    public void setComingTitle(String comingTitle) {
        this.comingTitle = comingTitle;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getDir() {
        return dir;
    }

    public void setDir(String dir) {
        this.dir = dir;
    }

    public int getDur() {
        return dur;
    }

    public void setDur(int dur) {
        this.dur = dur;
    }

    public int getEffectShowNum() {
        return effectShowNum;
    }

    public void setEffectShowNum(int effectShowNum) {
        this.effectShowNum = effectShowNum;
    }

    public String getFra() {
        return fra;
    }

    public void setFra(String fra) {
        this.fra = fra;
    }

    public String getFrt() {
        return frt;
    }

    public void setFrt(String frt) {
        this.frt = frt;
    }

    public boolean isGlobalReleased() {
        return globalReleased;
    }

    public void setGlobalReleased(boolean globalReleased) {
        this.globalReleased = globalReleased;
    }

    public boolean isHeadLineShow() {
        return headLineShow;
    }

    public void setHeadLineShow(boolean headLineShow) {
        this.headLineShow = headLineShow;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getImg() {
        return img;
    }

    public void setImg(String img) {
        this.img = img;
    }

    public boolean isLate() {
        return late;
    }

    public void setLate(boolean late) {
        this.late = late;
    }

    public int getLocalPubSt() {
        return localPubSt;
    }

    public void setLocalPubSt(int localPubSt) {
        this.localPubSt = localPubSt;
    }

    public int getMk() {
        return mk;
    }

    public void setMk(int mk) {
        this.mk = mk;
    }

    public String getNm() {
        return nm;
    }

    public void setNm(String nm) {
        this.nm = nm;
    }

    public int getPn() {
        return pn;
    }

    public void setPn(int pn) {
        this.pn = pn;
    }

    public boolean isPreShow() {
        return preShow;
    }

    public void setPreShow(boolean preShow) {
        this.preShow = preShow;
    }

    public int getProScore() {
        return proScore;
    }

    public void setProScore(int proScore) {
        this.proScore = proScore;
    }

    public int getProScoreNum() {
        return proScoreNum;
    }

    public void setProScoreNum(int proScoreNum) {
        this.proScoreNum = proScoreNum;
    }

    public long getPubDate() {
        return pubDate;
    }

    public void setPubDate(long pubDate) {
        this.pubDate = pubDate;
    }

    public String getPubDesc() {
        return pubDesc;
    }

    public void setPubDesc(String pubDesc) {
        this.pubDesc = pubDesc;
    }

    public int getPubShowNum() {
        return pubShowNum;
    }

    public void setPubShowNum(int pubShowNum) {
        this.pubShowNum = pubShowNum;
    }

    public int getRecentShowDate() {
        return recentShowDate;
    }

    public void setRecentShowDate(int recentShowDate) {
        this.recentShowDate = recentShowDate;
    }

    public int getRecentShowNum() {
        return recentShowNum;
    }

    public void setRecentShowNum(int recentShowNum) {
        this.recentShowNum = recentShowNum;
    }

    public String getRt() {
        return rt;
    }

    public void setRt(String rt) {
        this.rt = rt;
    }

    public int getSc() {
        return sc;
    }

    public void setSc(int sc) {
        this.sc = sc;
    }

    public String getScm() {
        return scm;
    }

    public void setScm(String scm) {
        this.scm = scm;
    }

    public int getShowCinemaNum() {
        return showCinemaNum;
    }

    public void setShowCinemaNum(int showCinemaNum) {
        this.showCinemaNum = showCinemaNum;
    }

    public String getShowInfo() {
        return showInfo;
    }

    public void setShowInfo(String showInfo) {
        this.showInfo = showInfo;
    }

    public int getShowNum() {
        return showNum;
    }

    public void setShowNum(int showNum) {
        this.showNum = showNum;
    }

    public int getShowst() {
        return showst;
    }

    public void setShowst(int showst) {
        this.showst = showst;
    }

    public int getSnum() {
        return snum;
    }

    public void setSnum(int snum) {
        this.snum = snum;
    }

    public String getStar() {
        return star;
    }

    public void setStar(String star) {
        this.star = star;
    }

    public String getVer() {
        return ver;
    }

    public void setVer(String ver) {
        this.ver = ver;
    }

    public int getVideoId() {
        return videoId;
    }

    public void setVideoId(int videoId) {
        this.videoId = videoId;
    }

    public String getVideoName() {
        return videoName;
    }

    public void setVideoName(String videoName) {
        this.videoName = videoName;
    }

    public String getVideourl() {
        return videourl;
    }

    public void setVideourl(String videourl) {
        this.videourl = videourl;
    }

    public int getVnum() {
        return vnum;
    }

    public void setVnum(int vnum) {
        this.vnum = vnum;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public int getWish() {
        return wish;
    }

    public void setWish(int wish) {
        this.wish = wish;
    }

    public int getWishst() {
        return wishst;
    }

    public void setWishst(int wishst) {
        this.wishst = wishst;
    }

    public List<?> getHeadLinesVO() {
        return headLinesVO;
    }

    public void setHeadLinesVO(List<?> headLinesVO) {
        this.headLinesVO = headLinesVO;
    }
}

  第四部:创建适配器

  MyAdapter.java

package com.qianmo.stickyitemdecoration.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.qianmo.stickyitemdecoration.R;
import com.qianmo.stickyitemdecoration.bean.ComingBean;

import java.util.List;

import static java.lang.System.load;

/**
 * Created by Administrator on 2017/3/3 0003.
 * E-Mail:543441727@qq.com
 */

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private Context context;
    private List<ComingBean> datas;

    public MyAdapter(Context context, List<ComingBean> datas) {
        this.context = context;
        this.datas = datas;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_recyclerview, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        String imagUrl = datas.get(position).getImg();
        String newImagUrl = imagUrl.replaceAll("w.h", "50.80");
        Glide
                .with(context)
                .load(newImagUrl)
                .error(R.mipmap.ic_launcher)
                .into(holder.img);

        holder.tv_time.setText(datas.get(position).getPubDesc());
        holder.tv_title.setText(datas.get(position).getNm());
        holder.tv_introduce.setText(datas.get(position).getDesc());
        holder.tv_score_major_real.setText(datas.get(position).getDur()+"");
        holder.tv_score_people_real.setText(datas.get(position).getPn()+"");
    }

    @Override
    public int getItemCount() {
        return datas == null ? 0 : datas.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        ImageView img;
        TextView tv_title;
        TextView tv_introduce;
        TextView tv_score_major_real;
        TextView tv_score_people_real;
        TextView tv_time;

        public ViewHolder(View view) {
            super(view);

            img = (ImageView) view.findViewById(R.id.img);
            tv_title = (TextView) view.findViewById(R.id.tv_title);
            tv_introduce = (TextView) view.findViewById(R.id.tv_introduce);
            tv_score_major_real = (TextView) view.findViewById(R.id.tv_score_major_real);
            tv_score_people_real = (TextView) view.findViewById(R.id.tv_score_people_real);
            tv_time = (TextView) view.findViewById(R.id.tv_time);
        }
    }
}

  第五步:自定义ItemDecoration,使头部悬浮,具体思路代码中有了,就不在分析了

  FloatingItemDecoration.java

package com.qianmo.stickyitemdecoration.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.Dimension;
import android.support.annotation.DrawableRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.View;


import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 2017/3/3 0003.
 * E-Mail:543441727@qq.com
 */

public class FloatingItemDecoration extends RecyclerView.ItemDecoration {
    private static final String TAG = "FloatingItemDecoration";
    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
    private Drawable mDivider;
    private int dividerHeight;
    private int dividerWidth;
    private Map<Integer, String> keys = new HashMap<>();
    private int mTitleHeight;
    private Paint mTextPaint;
    private Paint mBackgroundPaint;
    private float mTextHeight;
    private float mTextBaselineOffset;
    private Context mContext;
    /**
     * 滚动列表的时候是否一直显示悬浮头部
     */
    private boolean showFloatingHeaderOnScrolling = true;

    public FloatingItemDecoration(Context context) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        this.dividerHeight = mDivider.getIntrinsicHeight();
        this.dividerWidth = mDivider.getIntrinsicWidth();
        init(context);
    }

    /**
     * 自定义分割线
     *
     * @param context
     * @param drawableId 分割线图片
     */
    public FloatingItemDecoration(Context context, @DrawableRes int drawableId) {
        mDivider = ContextCompat.getDrawable(context, drawableId);
        this.dividerHeight = mDivider.getIntrinsicHeight();
        this.dividerWidth = mDivider.getIntrinsicWidth();
        init(context);
    }

    /**
     * 自定义分割线
     * 也可以使用{@link Canvas#drawRect(float, float, float, float, Paint)}或者{@link Canvas#drawText(String, float, float, Paint)}等等
     * 结合{@link Paint}去绘制各式各样的分割线
     *
     * @param context
     * @param color         整型颜色值,非资源id
     * @param dividerWidth  单位为dp
     * @param dividerHeight 单位为dp
     */
    public FloatingItemDecoration(Context context, @ColorInt int color, @Dimension float dividerWidth, @Dimension float dividerHeight) {
        mDivider = new ColorDrawable(color);
        this.dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, context.getResources().getDisplayMetrics());
        this.dividerHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerHeight, context.getResources().getDisplayMetrics());
        init(context);
    }

    private void init(Context mContext) {
        this.mContext = mContext;
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, mContext.getResources().getDisplayMetrics()));
        mTextPaint.setColor(Color.WHITE);
        Paint.FontMetrics fm = mTextPaint.getFontMetrics();
        mTextHeight = fm.bottom - fm.top;//计算文字高度
        mTextBaselineOffset = fm.bottom;

        mBackgroundPaint = new Paint();
        mBackgroundPaint.setAntiAlias(true);
        mBackgroundPaint.setColor(Color.MAGENTA);
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        drawVertical(c, parent);
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        if (!showFloatingHeaderOnScrolling) {
            return;
        }
        int firstVisiblePos = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
        if (firstVisiblePos == RecyclerView.NO_POSITION) {
            return;
        }
        String title = getTitle(firstVisiblePos);
        if (TextUtils.isEmpty(title)) {
            return;
        }
        boolean flag = false;
        if (getTitle(firstVisiblePos + 1) != null && !title.equals(getTitle(firstVisiblePos + 1))) {
            //说明是当前组最后一个元素,但不一定碰撞了
//            Log.e(TAG, "onDrawOver: "+"==============" +firstVisiblePos);
            View child = parent.findViewHolderForAdapterPosition(firstVisiblePos).itemView;
            if (child.getTop() + child.getMeasuredHeight() < mTitleHeight) {
                //进一步检测碰撞
//                Log.e(TAG, "onDrawOver: "+child.getTop()+"$"+firstVisiblePos );
                c.save();//保存画布当前的状态
                flag = true;
                c.translate(0, child.getTop() + child.getMeasuredHeight() - mTitleHeight);//负的代表向上
            }
        }
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int top = parent.getPaddingTop();
        int bottom = top + mTitleHeight;
        c.drawRect(left, top, right, bottom, mBackgroundPaint);
        float x = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, mContext.getResources().getDisplayMetrics());
        float y = bottom - (mTitleHeight - mTextHeight) / 2 - mTextBaselineOffset;//计算文字baseLine
        c.drawText(title, x, y, mTextPaint);
        if (flag) {
            //还原画布为初始状态
            c.restore();
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int pos = parent.getChildViewHolder(view).getAdapterPosition();
        if (keys.containsKey(pos)) {//留出头部偏移
            outRect.set(0, mTitleHeight, 0, 0);
        } else {
            outRect.set(0, dividerHeight, 0, 0);
        }
    }

    /**
     * *如果该位置没有,则往前循环去查找标题,找到说明该位置属于该分组
     *
     * @param position
     * @return
     */
    private String getTitle(int position) {
        while (position >= 0) {
            if (keys.containsKey(position)) {
                return keys.get(position);
            }
            position--;
        }
        return null;
    }

    private void drawVertical(Canvas c, RecyclerView parent) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int top = 0;
        int bottom = 0;
        for (int i = 0; i < parent.getChildCount(); i++) {
            View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            if (!keys.containsKey(params.getViewLayoutPosition())) {
                //画普通分割线
                top = child.getTop() - params.topMargin - dividerHeight;
                bottom = top + dividerHeight;
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            } else {
                //画头部
                top = child.getTop() - params.topMargin - mTitleHeight;
                bottom = top + mTitleHeight;
                c.drawRect(left, top, right, bottom, mBackgroundPaint);
//                float x=child.getPaddingLeft()+params.leftMargin;
                float x = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, mContext.getResources().getDisplayMetrics());
                float y = bottom - (mTitleHeight - mTextHeight) / 2 - mTextBaselineOffset;//计算文字baseLine
//                Log.e(TAG, "drawVertical: "+bottom );
                c.drawText(keys.get(params.getViewLayoutPosition()), x, y, mTextPaint);
            }
        }
    }

    public void setShowFloatingHeaderOnScrolling(boolean showFloatingHeaderOnScrolling) {
        this.showFloatingHeaderOnScrolling = showFloatingHeaderOnScrolling;
    }

    public void setKeys(Map<Integer, String> keys) {
        this.keys.clear();
        this.keys.putAll(keys);
    }

    public void setmTitleHeight(int titleHeight) {
        this.mTitleHeight = titleHeight;
    }
}

  第六步:设置ItemDecoration,设置适配器

 final FloatingItemDecoration floatingItemDecoration=new FloatingItemDecoration(this, Color.GRAY,1,1);
        floatingItemDecoration.setKeys(keys);
        floatingItemDecoration.setmTitleHeight((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50,getResources().getDisplayMetrics()));
        recycleview.addItemDecoration(floatingItemDecoration);


        adapter = new MyAdapter(mContext, datas);
        recycleview.setHasFixedSize(true);
        recycleview.setAdapter(adapter);

  完整的MainActivity代码如下:

package com.qianmo.stickyitemdecoration;

import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;

import com.qianmo.stickyitemdecoration.adapter.MyAdapter;
import com.qianmo.stickyitemdecoration.api.MovieApiService;
import com.qianmo.stickyitemdecoration.base.BaseResponse;
import com.qianmo.stickyitemdecoration.bean.ComingBean;
import com.qianmo.stickyitemdecoration.bean.MovieBean;
import com.qianmo.stickyitemdecoration.bean.NameBean;
import com.qianmo.stickyitemdecoration.utils.Constant;
import com.qianmo.stickyitemdecoration.view.FloatingItemDecoration;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import butterknife.BindView;
import butterknife.ButterKnife;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;


public class MainActivity extends AppCompatActivity {

    @BindView(R.id.recycleview)
    RecyclerView recycleview;

    private List<ComingBean> datas;
    private Map<Integer,String> keys=new HashMap<>();//存放所有key的位置和内容
    private MyAdapter adapter;
    private Context mContext = MainActivity.this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        initData();
    }

    /**
     * 请求数据
     */
    private void initData() {
        datas = new ArrayList<>();
        requestNetwork();
    }

    /**
     * 初始化UI
     */
    private void initView() {
        GridLayoutManager manager = new GridLayoutManager(this, 1);
        recycleview.setLayoutManager(manager);

        keys.put(0,"欧美大片");
        keys.put(2,"国产剧透");
        keys.put(4,"印度神剧");

        final FloatingItemDecoration floatingItemDecoration=new FloatingItemDecoration(this, Color.GRAY,1,1);
        floatingItemDecoration.setKeys(keys);
        floatingItemDecoration.setmTitleHeight((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50,getResources().getDisplayMetrics()));
        recycleview.addItemDecoration(floatingItemDecoration);


        adapter = new MyAdapter(mContext, datas);
        recycleview.setHasFixedSize(true);
        recycleview.setAdapter(adapter);
    }



    /**
     * 请求网络
     */
    private void requestNetwork() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Constant.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        MovieApiService movieApiService = retrofit.create(MovieApiService.class);
        movieApiService.getMovieDatas()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<BaseResponse<MovieBean>>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(BaseResponse<MovieBean> movieBeanBaseResponse) {
                        datas.addAll(movieBeanBaseResponse.getData().getComing());
                        initView();
//                        adapter.notifyDataSetChanged();
                    }
                });
    }
}

  再上一些github下载地址:https://github.com/543441727/StickyItemDecoration.git

原文地址:https://www.cnblogs.com/wjtaigwh/p/6498109.html