第一种原生方法
activity布局 ll_top代表要悬停的部分 这里面我放了 图片和文本

1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:paddingBottom="@dimen/activity_vertical_margin" 8 android:paddingLeft="@dimen/activity_horizontal_margin" 9 android:paddingRight="@dimen/activity_horizontal_margin" 10 android:paddingTop="@dimen/activity_vertical_margin" 11 tools:context="com.demon.testservice.ui.RelistActivity"> 12 13 <FrameLayout 14 android:layout_width="match_parent" 15 android:layout_height="match_parent"> 16 17 <android.support.v7.widget.RecyclerView 18 android:id="@+id/recyclerView" 19 android:layout_width="match_parent" 20 android:layout_height="match_parent"> 21 22 </android.support.v7.widget.RecyclerView> 23 24 25 <LinearLayout 26 android:id="@+id/ll_top" 27 android:layout_width="match_parent" 28 android:layout_height="wrap_content" 29 android:orientation="vertical"> 30 31 32 <LinearLayout 33 android:layout_width="match_parent" 34 android:layout_height="wrap_content" 35 android:background="@color/white" 36 android:gravity="center_vertical" 37 android:orientation="horizontal"> 38 39 <ImageView 40 android:layout_width="30dp" 41 android:layout_height="30dp" 42 android:src="@drawable/ic_launcher"/> 43 44 <TextView 45 android:id="@+id/tv_top" 46 android:layout_width="wrap_content" 47 android:layout_height="wrap_content" 48 android:text="name"/> 49 50 </LinearLayout> 51 52 53 <View 54 android:id="@+id/top_divider" 55 android:layout_width="match_parent" 56 android:layout_height="0.2dp" 57 android:background="#33000000"/> 58 59 </LinearLayout> 60 61 62 </FrameLayout> 63 64 65 </RelativeLayout>
item布局 上面和ll_top一样,下面部分是显示一张大图片

1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:orientation="vertical" 6 > 7 8 <LinearLayout 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:gravity="center_vertical" 12 android:orientation="horizontal" 13 android:background="@color/white" 14 > 15 16 <ImageView 17 android:layout_width="30dp" 18 android:layout_height="30dp" 19 android:src="@drawable/ic_launcher"/> 20 21 <TextView 22 android:layout_width="wrap_content" 23 android:layout_height="wrap_content" 24 android:text="name"/> 25 26 </LinearLayout> 27 28 <View 29 30 android:layout_width="match_parent" 31 android:layout_height="0.2dp" 32 android:background="#33000000" /> 33 34 35 <ImageView 36 android:paddingTop="5dp" 37 android:layout_width="wrap_content" 38 android:layout_height="wrap_content" 39 android:src="@drawable/user"/> 40 41 42 </LinearLayout>
activity 设置addOnScrollListener 是关键
1 public class RelistActivity extends AppCompatActivity { 2 3 @Bind(R.id.recyclerView) 4 RecyclerView recyclerView; 5 6 @Bind(R.id.ll_top) 7 LinearLayout ll_top; 8 9 @Bind(R.id.tv_top) 10 TextView tv_top; 11 12 private int height; 13 private int currentPosition = 0; 14 15 @Override 16 protected void onCreate(Bundle savedInstanceState) { 17 super.onCreate(savedInstanceState); 18 setContentView(R.layout.activity_relist); 19 ButterKnife.bind(this); 20 21 final LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this); 22 RelistAdapter adapter=new RelistAdapter(); 23 24 recyclerView.setLayoutManager(linearLayoutManager); 25 recyclerView.setAdapter(adapter); 26 27 28 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 29 @Override 30 public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 31 super.onScrollStateChanged(recyclerView, newState); 32 33 height=ll_top.getHeight(); 34 35 } 36 37 @Override 38 public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 39 super.onScrolled(recyclerView, dx, dy); 40 View view=linearLayoutManager.findViewByPosition(currentPosition+1); 41 if (view==null)return; 42 43 if (view.getTop()<=height){ 44 ll_top.setY(-(height-view.getTop())); 45 }else { 46 ll_top.setY(0); 47 } 48 49 50 if (currentPosition!=linearLayoutManager.findFirstVisibleItemPosition()){ 51 currentPosition=linearLayoutManager.findFirstVisibleItemPosition(); 52 ll_top.setY(0); 53 54 tv_top.setText(" "+currentPosition); 55 } 56 } 57 }); 58 59 } 60 61 62 63 64 65 66 67 class RelistAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ 68 69 70 @Override 71 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 72 View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_re,parent,false); 73 return new ReHolder(view); 74 } 75 76 @Override 77 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 78 79 } 80 81 @Override 82 public int getItemCount() { 83 return 10; 84 } 85 86 class ReHolder extends RecyclerView.ViewHolder{ 87 88 public ReHolder(View itemView) { 89 super(itemView); 90 ButterKnife.bind(this,itemView); 91 } 92 } 93 } 94 95 96 }
第二种
timehop/sticky-headers-recyclerview: [UNMAINTAINED] Sticky Headers decorator for Android's RecyclerView
https://github.com/timehop/sticky-headers-recyclerview
这个库好在对adapter改动小,只需要实现就行了
这里用 一个省份城市api 测试 http://apistore.baidu.com/apiworks/servicedetail/990.html
取以下四种数据 并添加一个 string类型的 type
"province": "上海",
"region": "华东地区",
"x": 121.473,
"y": 31.2317
CityInfo 是adapter 需要的数据

1 public class CityInfo { 2 /* 3 华东地区 a 4 华北地区 b 5 华南地区 c 6 华中地区 d 7 8 东北地区 e 9 西北地区 f 10 西南地区 g 11 12 台港澳地区 h 13 */ 14 15 private String province; 16 private String region; 17 private String type; 18 private double x; 19 private double y; 20 21 public String getType() { 22 return type; 23 } 24 25 public void setType(String type) { 26 this.type = type; 27 } 28 29 public String getProvince() { 30 return province; 31 } 32 33 public void setProvince(String province) { 34 this.province = province; 35 } 36 37 public String getRegion() { 38 return region; 39 } 40 41 public void setRegion(String region) { 42 this.region = region; 43 } 44 45 public double getX() { 46 return x; 47 } 48 49 public void setX(double x) { 50 this.x = x; 51 } 52 53 public double getY() { 54 return y; 55 } 56 57 public void setY(double y) { 58 this.y = y; 59 } 60 }
获取json依然用rxjava和Retrofit city对象还是GsonFormat自动解析的 代码就不贴了
public class NetWork { private static CityApi cityApi; public static CityApi getCityApi(){ if (cityApi==null){ Retrofit retrofit=new Retrofit.Builder().baseUrl("http://apis.baidu.com/").addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build(); cityApi=retrofit.create(CityApi.class); } return cityApi; } }
public interface CityApi { //http://apis.baidu.com/tngou/factory/province @GET("tngou/factory/province") rx.Observable<City>getCity(@Header("apikey")String apikey); }
主布局就一个RecyclerView ,悬停布局一个textview ,item 布局三个textview

1 <?xml version="1.0" encoding="utf-8"?> 2 <TextView 3 android:id="@+id/item_headtv" 4 xmlns:android="http://schemas.android.com/apk/res/android" 5 xmlns:tools="http://schemas.android.com/tools" 6 android:layout_width="match_parent" 7 android:layout_height="wrap_content" 8 android:padding="10dp" 9 tools:text="吸顶文本" 10 android:background="@color/hole_bule" 11 />

1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:padding="11dp" 6 android:background="@color/blue_semi_transparent_pressed" 7 > 8 9 <TextView 10 android:id="@+id/tv_sticky_province" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:layout_alignParentLeft="true" 14 android:layout_centerVertical="true" 15 android:text="province" 16 /> 17 18 <TextView 19 android:id="@+id/tv_sticky_x" 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" 22 android:layout_alignParentRight="true" 23 android:text="X" 24 /> 25 26 <TextView 27 android:id="@+id/tv_sticky_y" 28 android:layout_width="wrap_content" 29 android:layout_height="wrap_content" 30 android:layout_alignParentRight="true" 31 android:layout_below="@+id/tv_sticky_x" 32 android:text="Y" 33 /> 34 </RelativeLayout>
adapter 一如既往 继承 RecyclerView.Adapter 并实现 StickyRecyclerHeadersAdapter 重写head的三个方法
onBindHeaderViewHolder onCreateHeaderViewHolder 和 RecyclerView的一样 创建分组的view 绑定分组的数据
getHeaderId 稍微有点麻烦 返回值类型为long 所以我在最开始 CityInfo 多设置了一个type 如果api自带那就轻松了 。代码中返回-1的意思是为了把位置为0的item设置成置顶,并不会被分组
1 public class StickyAdapter2 extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements StickyRecyclerHeadersAdapter { 2 3 private List<CityInfo>list=new ArrayList<CityInfo>(); 4 5 public void add(CityInfo object) { 6 list.add(object); 7 notifyDataSetChanged(); 8 } 9 10 public void addAll(Collection<? extends CityInfo> collection){ 11 if (collection!=null){ 12 list.addAll(collection); 13 notifyDataSetChanged(); 14 } 15 } 16 17 @Override 18 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 19 View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_stick2,parent,false); 20 return new ItemHolder(view); 21 } 22 23 @Override 24 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 25 if (holder instanceof ItemHolder){ 26 ItemHolder itemHolder= (ItemHolder) holder; 27 itemHolder.tv_province.setText(list.get(position).getProvince()+" "+getItem(position)); 28 29 itemHolder.tv_y.setText("Y轴 "+list.get(position).getY()); 30 itemHolder.tv_x.setText("X轴 "+list.get(position).getX()); 31 } 32 33 } 34 35 @Override 36 public long getHeaderId(int position) { 37 if (position==0){ 38 return -1; 39 }else { 40 return getItem(position).charAt(0); 41 } 42 } 43 44 public String getItem(int position) { 45 return list.get(position).getType(); 46 } 47 48 @Override 49 public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup viewGroup) { 50 View view=LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_headview,viewGroup,false); 51 return new RecyclerView.ViewHolder(view) { 52 53 }; 54 } 55 56 @Override 57 public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder, int position) { 58 TextView textView = (TextView) holder.itemView; 59 textView.setText(list.get(position).getRegion()); 60 // holder.itemView.setBackgroundColor(getRandomColor()); 61 } 62 63 private int getRandomColor() { 64 SecureRandom rgen = new SecureRandom(); 65 return Color.HSVToColor(150, new float[]{ 66 rgen.nextInt(359), 1, 1 67 }); 68 } 69 70 @Override 71 public int getItemCount() { 72 return list.size(); 73 } 74 75 class ItemHolder extends RecyclerView.ViewHolder{ 76 77 @Bind(R.id.tv_sticky_province) 78 TextView tv_province; 79 @Bind(R.id.tv_sticky_x) 80 TextView tv_x; 81 @Bind(R.id.tv_sticky_y) 82 TextView tv_y; 83 84 public ItemHolder(View itemView) { 85 super(itemView); 86 ButterKnife.bind(this,itemView); 87 } 88 } 89 90 }
再看逻辑代码
StickyRecyclerHeadersDecoration 很重要 一定要设置给recyclerView 不然就不会有head了。 可以发现 它继承了 ItemDecoration,后者通常 是为每个Item视图添加子视图,被用来绘制Divider,可以用,可以不用
添加的head 如果想 相应点击事件,那就要用到 StickyRecyclerHeadersTouchListener,构造参数是 recyclerView 和 StickyRecyclerHeadersDecoration ,得到实例 再用 setOnHeaderClickListener 即可
如果只想获取整个item的点击事件,可以直接用 样品 33到39行 就搞定了
添加item之间的间隙 可以用 这个文件 recyclerView.addItemDecoration(new DividerDecoration(mContext));
第一个数据我自行添加了一个,为了看置顶的效果,后面的list数据 先根据地区信息设置了我需要的type ,然后进行了一下根据type字母的排序,不然传进去list很乱
1 public class Sticky2Activity extends BaseActivity { 2 3 @Bind(R.id.rcv_stick) 4 RecyclerView recyclerView; 5 6 private List<CityInfo> list = new ArrayList<CityInfo>(); 7 private StickyAdapter2 adapter; 8 private StickyRecyclerHeadersDecoration headersDecoration; 9 10 @Override 11 protected void onCreate(Bundle savedInstanceState) { 12 super.onCreate(savedInstanceState); 13 setContentView(R.layout.activity_sticky2); 14 ButterKnife.bind(this); 15 adapter = new StickyAdapter2(); 16 headersDecoration=new StickyRecyclerHeadersDecoration(adapter); 17 18 recyclerView.setLayoutManager(new LinearLayoutManager(mContext)); 19 recyclerView.setAdapter(adapter); 20 // recyclerView.addItemDecoration(new StickyRecyclerHeadersDecoration(adapter)); 21 recyclerView.addItemDecoration(headersDecoration); 22 recyclerView.setItemAnimator(new DefaultItemAnimator()); 23 recyclerView.addItemDecoration(new DividerDecoration(mContext)); 24 25 StickyRecyclerHeadersTouchListener headersTouchListener=new StickyRecyclerHeadersTouchListener(recyclerView,headersDecoration); 26 headersTouchListener.setOnHeaderClickListener(new StickyRecyclerHeadersTouchListener.OnHeaderClickListener() { 27 @Override 28 public void onHeaderClick(View view, int position, long headerId) { 29 showToast("Header position: " + position + ", id: " + headerId); 30 } 31 }); 32 33 recyclerView.addOnItemTouchListener(headersTouchListener); 34 recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(mContext, new RecyclerItemClickListener.OnItemClickListener() { 35 @Override 36 public void onItemClick(View view, int position) { 37 showToast("position "+position); 38 } 39 })); 40 41 42 NetWork.getCityApi().getCity("f44bd76826571234e2616746eabf3c3c").subscribeOn(Schedulers.io()).observeOn 43 (AndroidSchedulers.mainThread()).subscribe(new Action1<City>() { 44 45 @Override 46 public void call(City city) { 47 if (city.isStatus()) { 48 int size = city.getTngou().size(); 49 CityInfo info = new CityInfo(); 50 info.setProvince("USA"); 51 info.setRegion("西南地区"); 52 info.setX(20.032); 53 info.setY(20.032); 54 info.setType("a"); 55 list.add(info); 56 57 for (int i = 0; i < size; i++) { 58 City.TngouBean bean = city.getTngou().get(i); 59 CityInfo cityInfo = new CityInfo(); 60 cityInfo.setProvince(bean.getProvince()); 61 cityInfo.setRegion(bean.getRegion()); 62 cityInfo.setX(bean.getX()); 63 cityInfo.setY(bean.getY()); 64 cityInfo.setType(settype(bean.getRegion())); 65 list.add(cityInfo); 66 } 67 Collections.sort(list, new Comparator<CityInfo>() { 68 @Override 69 public int compare(CityInfo lhs, CityInfo rhs) { 70 String a=lhs.getType(); 71 String b=rhs.getType(); 72 return a.compareToIgnoreCase(b); 73 } 74 }); 75 adapter.addAll(list); 76 } 77 } 78 }); 79 } 80 81 private String settype(String region) { 82 switch (region) { 83 case "华东地区": 84 return "a"; 85 case "华北地区": 86 return "b"; 87 case "华南地区": 88 return "c"; 89 case "华中地区": 90 return "d"; 91 case "东北地区": 92 return "e"; 93 case "西北地区": 94 return "f"; 95 case "西南地区": 96 return "g"; 97 default: 98 case "台港澳地区": 99 return "h"; 100 } 101 } 102 }
FastAdapter 这个貌似也不错 以后学习