粘性/懸浮頭部,效果如圖
a.gif
可使用一個開源的框架完成:
配置
1.在 (項目) 的build.gradle中導入
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
2.依賴
implementation 'com.android.support:design:27.1.1'
implementation 'com.github.qdxxxx:StickyHeaderDecoration:1.0.1'
3.使用場景
這個自定義的數(shù)據(jù)顯示的效果
mCars = new ArrayList<>();
mCars.add(new Car("奧迪", "A"));
mCars.add(new Car("阿爾法羅密歐", "A"));
mCars.add(new Car("阿斯頓馬丁", "A"));
mCars.add(new Car("ALPINA", "A"));
mCars.add(new Car("安凱客車", "A"));
mCars.add(new Car("本田", "B"));
mCars.add(new Car("別克", "B"));
mCars.add(new Car("奔馳", "B"));
mCars.add(new Car("寶馬", "B"));
mCars.add(new Car("保時捷", "B"));
mCars.add(new Car("比亞迪", "B"));
mCars.add(new Car("北京", "B"));
mCars.add(new Car("賓利", "B"));
mCars.add(new Car("巴博斯", "B"));
mCars.add(new Car("布加迪威龍", "B"));
mCars.add(new Car("長安", "C"));
mCars.add(new Car("長城", "C"));
mCars.add(new Car("大眾", "D"));
mCars.add(new Car("東南", "D"));
mCars.add(new Car("東風", "D"));
mCars.add(new Car("DS", "D"));
mCars.add(new Car("道奇", "D"));
mCars.add(new Car("東風小康", "D"));
}
private void initView() {
final LayoutInflater inflater = LayoutInflater.from(this);
mRlv = (RecyclerView) findViewById(R.id.rlv);
mRlv.setLayoutManager(new LinearLayoutManager(this));
RlvAdapter rlvAdapter = new RlvAdapter(mCars);
//返回頭布局的內(nèi)容
final NormalDecoration decoration = new NormalDecoration() {
@Override
public String getHeaderName(int i) {
return mCars.get(i).headerName;
}
};
//自定義頭布局,可不設(shè)置
decoration.setOnDecorationHeadDraw(new NormalDecoration.OnDecorationHeadDraw() {
@Override
public View getHeaderView(final int i) {
View inflate = inflater.inflate(R.layout.item_header, null);
TextView tv = inflate.findViewById(R.id.tv);
tv.setText(mCars.get(i).headerName);
return inflate;
}
});
mRlv.addItemDecoration(decoration);
//頭布局的點擊事件
decoration.setOnHeaderClickListener(new NormalDecoration.OnHeaderClickListener() {
@Override
public void headerClick(int i) {
Toast.makeText(MainActivity.this, mCars.get(i).headerName, Toast.LENGTH_SHORT).show();
startActivity(new Intent(MainActivity.this,FlowActivity.class));
}
});
mRlv.setAdapter(rlvAdapter);
}
這個在網(wǎng)絡網(wǎng)絡請求是MVP獲取到集合后
@Override
public void getData(TiXi base) {
Log.e("zhuzhu", "getData: "+base.toString() );
//就是只獲取到請求數(shù)據(jù)里的對象name
final List<TiXi.DataBean> data = base.getData();
final NormalDecoration decoration = new NormalDecoration() {
@Override
public String getHeaderName(int i) {
return data.get(i).getName();
}
};
mRe.addItemDecoration(decoration);
adapter.setData(data); //這個是添加數(shù)據(jù)
}
二:實戰(zhàn)使用
以下是對粘性頭部的工具使用,因為上面的在實際情況下使用會有一些問題,(如果在網(wǎng)絡請求的數(shù)據(jù)中,每個對象是會返回一個要懸浮的屬性,如果用上面的,會把這個對象的那個屬性中的內(nèi)容全都顯示,而造成啦錯亂的問題,所以,在項目實際開發(fā)中,使用工具類比較實用)
1:使用方式其實就一行代碼,在自己要加懸浮頭的 RecyclerView 中添加:
添加的list集合,上下文對象
rv.addItemDecoration(new SuspensionDecoration(getContext(), mList));
2:工具類中的設(shè)置
public class SuspensionDecoration extends RecyclerView.ItemDecoration {
private static final String TAG = SuspensionDecoration.class.getSimpleName();
// private int mTitleHeight;//title的高
private static int COLOR_TITLE_BG = Color.parseColor("#EDEAEA");
private static int COLOR_TITLE_FONT = Color.parseColor("#000000");
private static int mTitleFontSize;//title字體大小
ArrayList<ReMenBean.ContentsBean> classifies;
private Paint mPaint;
private Rect mBounds;//用于存放測量文字Rect
private int mHeaderViewCount = 0;
private int mTitleHeight = 0;
private int paddingLeft;
public SuspensionDecoration(Context context, ArrayList<ReMenBean.ContentsBean> classifies) {
super();
this.classifies = classifies;
mPaint = new Paint();
mBounds = new Rect();
//dp轉(zhuǎn)px
mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, context.getResources().getDisplayMetrics());
paddingLeft = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120, context.getResources().getDisplayMetrics());
mTitleFontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 13, context.getResources().getDisplayMetrics());
mPaint.setTextSize(mTitleFontSize);
mPaint.setAntiAlias(true);
}
public int getHeaderViewCount() {
return mHeaderViewCount;
}
public SuspensionDecoration setHeaderViewCount(int headerViewCount) {
mHeaderViewCount = headerViewCount;
return this;
}
@Override
public void onDrawOver(Canvas c, final RecyclerView parent, RecyclerView.State state) {//最后調(diào)用 繪制在最上層
Log.d(TAG, "onDrawOver");
int pos = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
//這里的position不算ondraw里面添加的view
pos -= getHeaderViewCount();
//pos為1,size為1姆另,1>0? true
if (classifies == null || classifies.isEmpty() || pos > classifies.size() - 1 || pos < 0) {
Log.d(TAG, "越界----");
return;//越界
}
String tag = classifies.get(pos).getDay() + DateUtils.dateToWeek(classifies.get(pos).getDay());
;
View child = parent.findViewHolderForLayoutPosition(pos + getHeaderViewCount()).itemView;//出現(xiàn)一個奇怪的bug珍语,有時候child為空十籍,所以將 child = parent.getChildAt(i)傻丝。-》 parent.findViewHolderForLayoutPosition(pos).itemView
boolean flag = false;//定義一個flag蛾扇,Canvas是否位移過的標志
if ((pos + 1) < classifies.size()) {//防止數(shù)組越界(一般情況不會出現(xiàn))
if (null != tag && !tag.equals(classifies.get(pos + 1).getDay())) {//當前第一個可見的Item的tag咧织,不等于其后一個item的tag囤捻,說明懸浮的View要切換了
Log.d("zxt", "onDrawOver() called with: c = [" + child.getTop());//當getTop開始變負萧豆,它的絕對值奸披,是第一個可見的Item移出屏幕的距離,
//這里計算第一個可見的item的剩余高度是自己的頂部的坐標+item自身的高度涮雷,剩下的就是留下來可見的部分阵面,這里的child.getTop()是一個負值
if (child.getHeight() + child.getTop() < mTitleHeight) {//當?shù)谝粋€可見的item在屏幕中還剩的高度小于title區(qū)域的高度時,我們也該開始做懸浮Title的“交換動畫”
c.save();//每次繪制前 保存當前Canvas狀態(tài)洪鸭,
flag = true;
//一種頭部折疊起來的視效样刷,個人覺得也還不錯~,由于child.getHeight() + child.getTop()是在不斷地變小览爵,因此這里有種漸變的裁剪矩形的感覺
//可與193行 c.drawRect 比較置鼻,只有bottom參數(shù)不一樣,由于 child.getHeight() + child.getTop() < mTitleHeight拾枣,所以繪制區(qū)域是在不斷的減小沃疮,有種折疊起來的感覺
// c.clipRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + child.getHeight() + child.getTop());
//類似餓了么點餐時,商品列表的懸停頭部切換“動畫效果”
//上滑時,將canvas上移 (y為負數(shù)) ,所以后面canvas 畫出來的Rect和Text都上移了梅肤,有種切換的“動畫”感覺
//這里是將下面畫的懸浮的部分往上移動mTitleHeight的距離,這里平移是下面畫的懸浮title部分司蔬,
//這里是從0到-mTitleHeight的過程,直至整個懸浮的title消失
c.translate(0, child.getHeight() + child.getTop() - mTitleHeight);//其實這里移動的是
}
}
}
/**
* 實際上這里是繪制懸浮的title部分姨蝴,永遠在頂部顯示
*/
mPaint.setColor(COLOR_TITLE_BG);
//這里實際上是在一個固定的位置添加一個矩形的title罷了
c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint);
mPaint.setColor(COLOR_TITLE_FONT);
//將文本通過畫筆來算出它占據(jù)的空間
mPaint.getTextBounds(tag, 0, tag.length(), mBounds);
//這里也是算的左下角的坐標啊俊啼,到底是什么回事啊
c.drawText(tag, child.getPaddingLeft() + paddingLeft,
parent.getPaddingTop() + mTitleHeight - (mTitleHeight / 2 - mBounds.height() / 2),
mPaint);
//只有在做了切換懸浮title動畫的時候才會有該操作
if (flag)
c.restore();//恢復畫布到之前保存的狀態(tài)
}
}