Android实现Recycleview悬浮粘性头部外加右侧字母导航
程序员文章站
2023-11-30 10:48:28
公司项目要实现这个效果:android实现recycleview悬浮粘性头部外加右侧字母导航
图一是开始的画面,图二是滑动的画面,点击右侧字母需要滑动左侧到指定位...
公司项目要实现这个效果:android实现recycleview悬浮粘性头部外加右侧字母导航
图一是开始的画面,图二是滑动的画面,点击右侧字母需要滑动左侧到指定位置,然后左侧的顶部字母a,b等需要悬浮。
实现思路:
右侧的联动可以用recycyeview中adapter的scrolltopositionwithoffset方法实现。左侧就是recycleview,后台返回的城市数据是这种类型的:
复制代码 代码如下:
{"returncode":1,"returnmsg":"操作成功","data":[{"startword":"a","traincitylist":[{"cityid":531,"cityname":"昂昂溪","code":null},{"cityid":2137,
我进行了一层封装
1.建立实体类用来封装下标和城市名字:
public class contactmodel { private string index; private string name; public contactmodel(string name){ this.index = newfirstletterutil.getfirstletter(name); this.name = name; } public string getindex() { return index; } public string getname() { return name; } }
2.讲服务器返回的数据进行封装:
list<contactmodel> contacts = new arraylist<>(); for (int i=0;i<mtraincitylist.size();i++){ // contactmodel contactmodel = new contactmodel(mtraincitylist.get(i).getcityname()); contacts.add(new contactmodel(mtraincitylist.get(i).getcityname())); collections.sort(contacts, new lettercomparator()); } mcontactmodels.addall(contacts); mshowmodels.addall(mcontactmodels);
3.设置适配器
private void setnewadapter() { contactsadapter madapter = new contactsadapter(mshowmodels); mmainrecycleview.setlayoutmanager(new linearlayoutmanager(this)); final stickyrecyclerheadersdecoration headersdecor = new stickyrecyclerheadersdecoration(madapter); mmainrecycleview.additemdecoration(headersdecor); madapter.setonitemclicklisttener(new contactsadapter.onitemclicklisttener() { @override public void onitemclick(int pos) { // toast.maketext(trainnewstartactivity.this,"惦记的pos:"+pos+"数据:"+mshowmodels.get(pos).getname(),toast.length_short).show(); intent intent = new intent(); intent.putextra("data", mshowmodels.get(pos).getname()); log.d("lwp","data:"+mshowmodels.get(pos).getname()); setresult(result_ok, intent); finish(); } }); mmainrecycleview.setadapter(madapter); mmain_side_bar.setlazyrespond(false); // 侧边设置相关 mmain_side_bar.setonselectindexitemlistener(new wavesidebarview.onselectindexitemlistener() { @override public void onselectindexitem(string letter) { for (int i = 0; i< mcontactmodels.size(); i++) { if (mcontactmodels.get(i).getindex().equals(letter)) { ((linearlayoutmanager) mmainrecycleview.getlayoutmanager()).scrolltopositionwithoffset(i, 0); return; } } } }); }
4.适配器代码:
public class contactsadapter extends recyclerview.adapter<contactsadapter.contactsviewholder> implements stickyrecyclerheadersadapter { private list<contactmodel> contacts; private static final string tag = "contactsadapter"; private contactmodel contact; public contactsadapter(list<contactmodel> contacts) { this.contacts = contacts; } @override public contactsviewholder oncreateviewholder(viewgroup parent, int viewtype) { layoutinflater inflater = layoutinflater.from(parent.getcontext()); view view = inflater.inflate(r.layout.layaout_item_contacts, null); return new contactsviewholder(view); } @override public void onbindviewholder(contactsviewholder holder, final int position) { contact = contacts.get(position); log.e(tag, "onbindviewholder: index:" + contact.getindex()); if (position == 0 || !contacts.get(position-1).getindex().equals(contact.getindex())) { holder.tvindex.setvisibility(view.gone); holder.tvindex.settext(contact.getindex()); } else { holder.tvindex.setvisibility(view.gone); } holder.tvname.settext(contact.getname()); holder.tvname.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { log.d("lwp","惦记的pos:"+position); onitemclicklisttener.onitemclick(position); } }); } public interface onitemclicklisttener{ void onitemclick(int pos); } public onitemclicklisttener onitemclicklisttener; public void setonitemclicklisttener(onitemclicklisttener onitemclicklisttener) { this.onitemclicklisttener = onitemclicklisttener; } @override public long getheaderid(int position) { if (contacts.get(position).getindex().equals("a")){ return 0; }else if (contacts.get(position).getindex().equals("b")){ return 1; }else if (contacts.get(position).getindex().equals("c")){ return 2; }else if (contacts.get(position).getindex().equals("d")){ return 3; }else if (contacts.get(position).getindex().equals("e")){ return 4; }else if (contacts.get(position).getindex().equals("f")){ return 5; }else if (contacts.get(position).getindex().equals("g")){ return 6; }else if (contacts.get(position).getindex().equals("h")){ return 7; }else if (contacts.get(position).getindex().equals("i")){ return 8; }else if (contacts.get(position).getindex().equals("j")){ return 9; }else if (contacts.get(position).getindex().equals("k")){ return 10; }else if (contacts.get(position).getindex().equals("l")){ return 11; }else if (contacts.get(position).getindex().equals("m")){ return 12; }else if (contacts.get(position).getindex().equals("n")){ return 13; }else if (contacts.get(position).getindex().equals("o")){ return 14; }else if (contacts.get(position).getindex().equals("p")){ return 15; }else if (contacts.get(position).getindex().equals("q")){ return 16; }else if (contacts.get(position).getindex().equals("r")){ return 17; }else if (contacts.get(position).getindex().equals("s")){ return 18; }else if (contacts.get(position).getindex().equals("t")){ return 19; }else if (contacts.get(position).getindex().equals("u")){ return 20; }else if (contacts.get(position).getindex().equals("v")){ return 21; }else if (contacts.get(position).getindex().equals("y")){ return 22; }else if (contacts.get(position).getindex().equals("x")){ return 23; }else if (contacts.get(position).getindex().equals("y")){ return 24; }else if (contacts.get(position).getindex().equals("z")){ return 25; }else { return -1; } } @override public recyclerview.viewholder oncreateheaderviewholder(viewgroup parent) { view view = layoutinflater.from(parent.getcontext()) .inflate(r.layout.view_header, parent, false); return new recyclerview.viewholder(view) { }; } @override public void onbindheaderviewholder(recyclerview.viewholder holder, int position) { textview textview = (textview) holder.itemview; textview.settext(string.valueof(contacts.get(position).getindex())); } @override public int getitemcount() { return contacts.size(); } class contactsviewholder extends recyclerview.viewholder { textview tvindex; imageview ivavatar; textview tvname; contactsviewholder(view itemview) { super(itemview); tvindex = (textview) itemview.findviewbyid(r.id.tv_index); ivavatar = (imageview) itemview.findviewbyid(r.id.iv_avatar); tvname = (textview) itemview.findviewbyid(r.id.tv_name); } } }
5.两个布局文件:
layaout_item_contacts.xml:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <textview android:id="@+id/tv_index" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingleft="12dp" android:text="a" android:textsize="14sp" android:background="#e0e0e0"/> <relativelayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/selectableitembackground"> <imageview android:id="@+id/iv_avatar" android:layout_width="40dp" android:layout_height="40dp" android:visibility="gone" android:layout_margin="10dp" /> <textview android:id="@+id/tv_name" android:layout_marginleft="12dp" android:layout_width="wrap_content" android:layout_height="34dp" android:layout_torightof="@+id/iv_avatar" android:text="南尘" android:gravity="center_vertical" android:textcolor="#424242" android:textsize="16sp" android:layout_centervertical="true" /> </relativelayout> <view android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginleft="15dp" android:background="#e8e8e8" /> </linearlayout>
view_header.xml:
<?xml version="1.0" encoding="utf-8"?> <textview xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingleft="12dp" android:textsize="14sp" android:textstyle="bold" android:background="#e0e0e0" tools:text="animals starting with a" />
采用的第三方:
compile 'com.github.nanchen2251:wavesidebar:1.0.6' compile 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar'
右侧字母用的是wavesidebar,但是由于不太符合设计图,所有我没有用他的,而是自己拿过来重新定义了(该类没提供修改,建议完善),如下:
public class slfwaveslidebarview extends view { private final static int default_text_size = 14; // sp private final static int default_max_offset = 80; //dp private final static string[] default_index_items = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}; private string[] mindexitems; /** * the index in {@link #mindexitems} of the current selected index item, * it's reset to -1 when the finger up */ private int mcurrentindex = -1; /** * y coordinate of the point where finger is touching, * the baseline is top of {@link #mstarttouchingarea} * it's reset to -1 when the finger up */ private float mcurrenty = -1; private paint mpaint; private int mtextcolor; private float mtextsize; /** * the height of each index item */ private float mindexitemheight; /** * offset of the current selected index item */ private float mmaxoffset; /** * {@link #mstarttouching} will be set to true when {@link motionevent#action_down} * happens in this area, and the side bar should start working. */ private rectf mstarttouchingarea = new rectf(); /** * height and width of {@link #mstarttouchingarea} */ private float mbarheight; private float mbarwidth; /** * flag that the finger is starting touching. * if true, it means the {@link motionevent#action_down} happened but * {@link motionevent#action_up} not yet. */ private boolean mstarttouching = false; /** * if true, the {@link wavesidebarview.onselectindexitemlistener#onselectindexitem(string)} * will not be called until the finger up. * if false, it will be called when the finger down, up and move. */ private boolean mlazyrespond = false; /** * the position of the side bar, default is {@link #position_right}. * you can set it to {@link #position_left} for people who use phone with left hand. */ private int msidebarposition; public static final int position_right = 0; public static final int position_left = 1; /** * the alignment of items, default is {@link #text_align_center}. */ private int mtextalignment; public static final int text_align_center = 0; public static final int text_align_left = 1; public static final int text_align_right = 2; /** * observe the current selected index item */ private wavesidebarview.onselectindexitemlistener onselectindexitemlistener; /** * the baseline of the first index item text to draw */ private float mfirstitembaseliney; /** * for {@link #dp2px(int)} and {@link #sp2px(int)} */ private displaymetrics mdisplaymetrics; public slfwaveslidebarview(context context) { this(context, null); } public slfwaveslidebarview(context context, attributeset attrs) { this(context, attrs, 0); } public slfwaveslidebarview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); mdisplaymetrics = context.getresources().getdisplaymetrics(); typedarray typedarray = context.obtainstyledattributes(attrs, com.nanchen.wavesidebar.r.styleable.wavesidebarview); mlazyrespond = typedarray.getboolean(com.nanchen.wavesidebar.r.styleable.wavesidebarview_sidebar_lazy_respond, false); mtextcolor = typedarray.getcolor(com.nanchen.wavesidebar.r.styleable.wavesidebarview_sidebar_text_color, color.gray); mmaxoffset = typedarray.getdimension(com.nanchen.wavesidebar.r.styleable.wavesidebarview_sidebar_max_offset, dp2px(default_max_offset)); msidebarposition = typedarray.getint(com.nanchen.wavesidebar.r.styleable.wavesidebarview_sidebar_position, position_right); mtextalignment = typedarray.getint(com.nanchen.wavesidebar.r.styleable.wavesidebarview_sidebar_text_alignment, text_align_center); typedarray.recycle(); mtextsize = sp2px(default_text_size); mindexitems = default_index_items; initpaint(); } private void initpaint() { mpaint = new paint(); mpaint.setantialias(true); mpaint.setcolor(mtextcolor); mpaint.settextsize(mtextsize); switch (mtextalignment) { case text_align_center: mpaint.settextalign(paint.align.center); break; case text_align_left: mpaint.settextalign(paint.align.left); break; case text_align_right: mpaint.settextalign(paint.align.right); break; } } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); int height = measurespec.getsize(heightmeasurespec); int width = measurespec.getsize(widthmeasurespec); paint.fontmetrics fontmetrics = mpaint.getfontmetrics(); mindexitemheight = fontmetrics.bottom - fontmetrics.top; mbarheight = mindexitems.length * mindexitemheight; // calculate the width of the longest text as the width of side bar for (string indexitem : mindexitems) { mbarwidth = math.max(mbarwidth, mpaint.measuretext(indexitem)); } float arealeft = (msidebarposition == position_left) ? 0 : (width - mbarwidth - getpaddingright()); float arearight = (msidebarposition == position_left) ? (getpaddingleft() + arealeft + mbarwidth) : width; float areatop = height/2 - mbarheight/2; float areabottom = areatop + mbarheight; mstarttouchingarea.set( arealeft, areatop, arearight, areabottom); // the baseline y of the first item' text to draw mfirstitembaseliney = (height/2 - mindexitems.length*mindexitemheight/2) + (mindexitemheight/2 - (fontmetrics.descent-fontmetrics.ascent)/2) - fontmetrics.ascent; } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); // draw each item for (int i = 0, mindexitemslength = mindexitems.length; i < mindexitemslength; i++) { float baseliney = mfirstitembaseliney + mindexitemheight*i; // calculate the scale factor of the item to draw float scale = getitemscale(i); int alphascale = (i == mcurrentindex) ? (255) : (int) (255 * (1-scale)); mpaint.setalpha(alphascale); mpaint.settextsize(mtextsize + mtextsize*scale); float baselinex = 0f; if (msidebarposition == position_left) { switch (mtextalignment) { case text_align_center: baselinex = getpaddingleft() + mbarwidth/2 + mmaxoffset*scale; break; case text_align_left: baselinex = getpaddingleft() + mmaxoffset*scale; break; case text_align_right: baselinex = getpaddingleft() + mbarwidth + mmaxoffset*scale; break; } } else { switch (mtextalignment) { case text_align_center: baselinex = getwidth() - getpaddingright() - mbarwidth/2 - mmaxoffset*scale; break; case text_align_right: baselinex = getwidth() - getpaddingright() - mmaxoffset*scale; break; case text_align_left: baselinex = getwidth() - getpaddingright() - mbarwidth - mmaxoffset*scale; break; } } // draw canvas.drawtext( mindexitems[i], //item text to draw baselinex, //baseline x baseliney, // baseline y mpaint); } // reset paint mpaint.setalpha(255); mpaint.settextsize(mtextsize); } /** * calculate the scale factor of the item to draw * * @param index the index of the item in array {@link #mindexitems} * @return the scale factor of the item to draw */ private float getitemscale(int index) { float scale = 0; if (mcurrentindex != -1) { float distance = math.abs(mcurrenty - (mindexitemheight*index+mindexitemheight/2)) / mindexitemheight; scale = 1 - distance*distance/16; scale = math.max(scale, 0); } return scale; } @override public boolean ontouchevent(motionevent event) { if (mindexitems.length == 0) { return super.ontouchevent(event); } float eventy = event.gety(); float eventx = event.getx(); mcurrentindex = getselectedindex(eventy); switch (event.getaction()) { case motionevent.action_down: if (mstarttouchingarea.contains(eventx, eventy)) { mstarttouching = true; if (!mlazyrespond && onselectindexitemlistener != null) { onselectindexitemlistener.onselectindexitem(mindexitems[mcurrentindex]); } // invalidate(); return true; } else { mcurrentindex = -1; return false; } case motionevent.action_move: if (mstarttouching && !mlazyrespond && onselectindexitemlistener != null) { onselectindexitemlistener.onselectindexitem(mindexitems[mcurrentindex]); } // invalidate(); return true; case motionevent.action_up: case motionevent.action_cancel: if (mlazyrespond && onselectindexitemlistener != null) { onselectindexitemlistener.onselectindexitem(mindexitems[mcurrentindex]); } mcurrentindex = -1; mstarttouching = false; // invalidate(); return true; } return super.ontouchevent(event); } private int getselectedindex(float eventy) { mcurrenty = eventy - (getheight()/2 - mbarheight /2); if (mcurrenty <= 0) { return 0; } int index = (int) (mcurrenty / this.mindexitemheight); if (index >= this.mindexitems.length) { index = this.mindexitems.length - 1; } return index; } private float dp2px(int dp) { return typedvalue.applydimension(typedvalue.complex_unit_dip, dp, this.mdisplaymetrics); } private float sp2px(int sp) { return typedvalue.applydimension(typedvalue.complex_unit_sp, sp, this.mdisplaymetrics); } public void setindexitems(string... indexitems) { mindexitems = arrays.copyof(indexitems, indexitems.length); requestlayout(); } public void settextcolor(int color) { mtextcolor = color; mpaint.setcolor(color); invalidate(); } public void setposition(int position) { if (position != position_right && position != position_left) { throw new illegalargumentexception("the position must be position_right or position_left"); } msidebarposition = position; requestlayout(); } public void setmaxoffset(int offset) { mmaxoffset = offset; invalidate(); } public void setlazyrespond(boolean lazyrespond) { mlazyrespond = lazyrespond; } public void settextalign(int align) { if (mtextalignment == align) { return; } switch (align) { case text_align_center: mpaint.settextalign(paint.align.center); break; case text_align_left: mpaint.settextalign(paint.align.left); break; case text_align_right: mpaint.settextalign(paint.align.right); break; default: throw new illegalargumentexception( "the alignment must be text_align_center, text_align_left or text_align_right"); } mtextalignment = align; invalidate(); } public void setonselectindexitemlistener(wavesidebarview.onselectindexitemlistener onselectindexitemlistener) { this.onselectindexitemlistener = onselectindexitemlistener; } public interface onselectindexitemlistener { void onselectindexitem(string letter); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。