Android自定义可循环的滚动选择器CycleWheelView
程序员文章站
2024-03-05 22:17:31
最近碰到个项目要使用到滚动选择器,原生的numberpicker可定制性太差,不大符合ui要求。
网上开源的wheelview是用scrollview写的,不能循环滚动,...
最近碰到个项目要使用到滚动选择器,原生的numberpicker可定制性太差,不大符合ui要求。
网上开源的wheelview是用scrollview写的,不能循环滚动,而且当数据量很大时要加载的item太多,性能非常低。
然后,还是自己写一个比较靠谱,用的是listview实现的。写完自己体验了一下,性能不错,再大的数据也不怕了。
感觉不错,重新封装了一下,提供了一些接口可以直接按照自己的需求定制,调用方法在mainactivity中。
补个图片:
不多说了,直接上代码:
cyclewheelview.java:
/** * copyright (c) 2015 * * cyclewheelview.java * * description: * * author: liao longhui * * ver 1.0, 2015-07-15, liao longhui, create file */ package com.example.wheelviewdemo; import android.content.context; import android.graphics.canvas; import android.graphics.color; import android.graphics.colorfilter; import android.graphics.paint; import android.graphics.drawable.drawable; import android.os.handler; import android.util.attributeset; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.abslistview; import android.widget.baseadapter; import android.widget.listview; import android.widget.textview; import java.util.arraylist; import java.util.list; /** * 可循环滚动的选择器 * @author liao longhui * */ public class cyclewheelview extends listview { public static final string tag = cyclewheelview.class.getsimplename(); private static final int color_divider_defalut = color.parsecolor("#747474"); private static final int height_divider_default = 2; private static final int color_solid_default = color.parsecolor("#3e4043"); private static final int color_solid_selet_default = color.parsecolor("#323335"); private static final int wheel_size_default = 3; private handler mhandler; private cyclewheelviewadapter madapter; /** * labels */ private list<string> mlabels; /** * color of selected label */ private int mlabelselectcolor = color.white; /** * color of unselected label */ private int mlabelcolor = color.gray; /** * gradual alph */ private float malphagradual = 0.7f; /** * color of divider */ private int dividercolor = color_divider_defalut; /** * height of divider */ private int dividerheight = height_divider_default; /** * color of selected solid */ private int seletedsolidcolor = color_solid_selet_default; /** * color of unselected solid */ private int solidcolor = color_solid_default; /** * size of wheel , it should be odd number like 3 or greater */ private int mwheelsize = wheel_size_default; /** * res id of wheel item layout */ private int mitemlayoutid; /** * res id of label textview */ private int mitemlabeltvid; /** * height of wheel item */ private int mitemheight; private boolean cylceenable; private int mcurrentpositon; private wheelitemselectedlistener mitemselectedlistener; public cyclewheelview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); } public cyclewheelview(context context, attributeset attrs) { super(context, attrs); init(); } public cyclewheelview(context context) { super(context); } private void init() { mhandler = new handler(); mitemlayoutid = r.layout.item_cyclewheel; mitemlabeltvid = r.id.tv_label_item_wheel; madapter = new cyclewheelviewadapter(); setverticalscrollbarenabled(false); setscrollingcacheenabled(false); setcachecolorhint(color.transparent); setfadingedgelength(0); setoverscrollmode(over_scroll_never); setdividerheight(0); setadapter(madapter); setonscrolllistener(new onscrolllistener() { @override public void onscrollstatechanged(abslistview view, int scrollstate) { if (scrollstate == scroll_state_idle) { view itemview = getchildat(0); if (itemview != null) { float deltay = itemview.gety(); if (deltay == 0) { return; } if (math.abs(deltay) < mitemheight / 2) { smoothscrollby(getdistance(deltay), 50); } else { smoothscrollby(getdistance(mitemheight + deltay), 50); } } } } @override public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) { refreshitems(); } }); } private int getdistance(float scrolldistance) { if (math.abs(scrolldistance) <= 2) { return (int) scrolldistance; } else if (math.abs(scrolldistance) < 12) { return scrolldistance > 0 ? 2 : -2; } else { return (int) (scrolldistance / 6); } } private void refreshitems() { int offset = mwheelsize / 2; int firstposition = getfirstvisibleposition(); int position = 0; if (getchildat(0) == null) { return; } if (math.abs(getchildat(0).gety()) <= mitemheight / 2) { position = firstposition + offset; } else { position = firstposition + offset + 1; } if (position == mcurrentpositon) { return; } mcurrentpositon = position; if (mitemselectedlistener != null) { mitemselectedlistener.onitemselected(getselection(), getselectlabel()); } resetitems(firstposition, position, offset); } private void resetitems(int firstposition, int position, int offset){ for (int i = position - offset - 1; i < position + offset + 1; i++) { view itemview = getchildat(i - firstposition); if (itemview == null) { continue; } textview labeltv = (textview) itemview.findviewbyid(mitemlabeltvid); if (position == i) { labeltv.settextcolor(mlabelselectcolor); itemview.setalpha(1f); } else { labeltv.settextcolor(mlabelcolor); int delta = math.abs(i - position); double alpha = math.pow(malphagradual, delta); itemview.setalpha((float) alpha); } } } /** * 设置滚轮的刻度列表 * * @param labels */ public void setlabels(list<string> labels) { mlabels = labels; madapter.setdata(mlabels); madapter.notifydatasetchanged(); initview(); } /** * 设置滚轮滚动监听 * * @param mitemselectedlistener */ public void setonwheelitemselectedlistener(wheelitemselectedlistener mitemselectedlistener) { this.mitemselectedlistener = mitemselectedlistener; } /** * 获取滚轮的刻度列表 * * @return */ public list<string> getlabels() { return mlabels; } /** * 设置滚轮是否为循环滚动 * * @param enable true-循环 false-单程 */ public void setcycleenable(boolean enable) { if (cylceenable != enable) { cylceenable = enable; madapter.notifydatasetchanged(); setselection(getselection()); } } /* * 滚动到指定位置 */ @override public void setselection(final int position) { mhandler.post(new runnable() { @override public void run() { cyclewheelview.super.setselection(getposition(position)); } }); } private int getposition(int positon) { if (mlabels == null || mlabels.size() == 0) { return 0; } if (cylceenable) { int d = integer.max_value / 2 / mlabels.size(); return positon + d * mlabels.size(); } return positon; } /** * 获取当前滚轮位置 * * @return */ public int getselection() { if (mcurrentpositon == 0) { mcurrentpositon = mwheelsize / 2; } return (mcurrentpositon - mwheelsize / 2) % mlabels.size(); } /** * 获取当前滚轮位置的刻度 * * @return */ public string getselectlabel() { int position = getselection(); position = position < 0 ? 0 : position; try { return mlabels.get(position); } catch (exception e) { return ""; } } /** * 如果需要自定义滚轮每个item,调用此方法设置自定义item布局,自定义布局中需要一个textview来显示滚轮刻度 * * @param itemresid 布局文件id * @param labeltvid 刻度textview的资源id */ public void setwheelitemlayout(int itemresid, int labeltvid) { mitemlayoutid = itemresid; mitemlabeltvid = labeltvid; madapter = new cyclewheelviewadapter(); madapter.setdata(mlabels); setadapter(madapter); initview(); } /** * 设置未选中刻度文字颜色 * * @param labelcolor */ public void setlabelcolor(int labelcolor) { this.mlabelcolor = labelcolor; resetitems(getfirstvisibleposition(), mcurrentpositon, mwheelsize/2); } /** * 设置选中刻度文字颜色 * * @param labelselectcolor */ public void setlabelselectcolor(int labelselectcolor) { this.mlabelselectcolor = labelselectcolor; resetitems(getfirstvisibleposition(), mcurrentpositon, mwheelsize/2); } /** * 设置滚轮刻度透明渐变值 * * @param alphagradual */ public void setalphagradual(float alphagradual) { this.malphagradual = alphagradual; resetitems(getfirstvisibleposition(), mcurrentpositon, mwheelsize/2); } /** * 设置滚轮可显示的刻度数量,必须为奇数,且大于等于3 * * @param wheelsize * @throws cyclewheelviewexception 滚轮数量错误 */ public void setwheelsize(int wheelsize) throws cyclewheelviewexception { if (wheelsize < 3 || wheelsize % 2 != 1) { throw new cyclewheelviewexception("wheel size error , must be 3,5,7,9..."); } else { mwheelsize = wheelsize; initview(); } } /** * 设置块的颜色 * @param unselectedsolidcolor 未选中的块的颜色 * @param selectedsolidcolor 选中的块的颜色 */ public void setsolid(int unselectedsolidcolor, int selectedsolidcolor){ this.solidcolor = unselectedsolidcolor; this.seletedsolidcolor = selectedsolidcolor; initview(); } /** * 设置分割线样式 * @param dividercolor 分割线颜色 * @param dividerheight 分割线高度(px) */ public void setdivider(int dividercolor, int dividerheight){ this.dividercolor = dividercolor; this.dividerheight = dividerheight; } @suppresswarnings("deprecation") private void initview() { mitemheight = measureheight(); viewgroup.layoutparams lp = getlayoutparams(); lp.height = mitemheight * mwheelsize; madapter.setdata(mlabels); madapter.notifydatasetchanged(); drawable backgroud = new drawable() { @override public void draw(canvas canvas) { int viewwidth = getwidth(); paint dividerpaint = new paint(); dividerpaint.setcolor(dividercolor); dividerpaint.setstrokewidth(dividerheight); paint seletedsolidpaint = new paint(); seletedsolidpaint.setcolor(seletedsolidcolor); paint solidpaint = new paint(); solidpaint.setcolor(solidcolor); canvas.drawrect(0, 0, viewwidth, mitemheight * (mwheelsize / 2), solidpaint); canvas.drawrect(0, mitemheight * (mwheelsize / 2 + 1), viewwidth, mitemheight * (mwheelsize), solidpaint); canvas.drawrect(0, mitemheight * (mwheelsize / 2), viewwidth, mitemheight * (mwheelsize / 2 + 1), seletedsolidpaint); canvas.drawline(0, mitemheight * (mwheelsize / 2), viewwidth, mitemheight * (mwheelsize / 2), dividerpaint); canvas.drawline(0, mitemheight * (mwheelsize / 2 + 1), viewwidth, mitemheight * (mwheelsize / 2 + 1), dividerpaint); } @override public void setalpha(int alpha) { } @override public void setcolorfilter(colorfilter cf) { } @override public int getopacity() { return 0; } }; setbackgrounddrawable(backgroud); } private int measureheight() { view itemview = layoutinflater.from(getcontext()).inflate(mitemlayoutid, null); itemview.setlayoutparams(new viewgroup.layoutparams(viewgroup.layoutparams.match_parent, viewgroup.layoutparams.wrap_content)); int w = view.measurespec.makemeasurespec(0, view.measurespec.unspecified); int h = view.measurespec.makemeasurespec(0, view.measurespec.unspecified); itemview.measure(w, h); int height = itemview.getmeasuredheight(); // int width = view.getmeasuredwidth(); return height; } public interface wheelitemselectedlistener { public void onitemselected(int position, string label); } public class cyclewheelviewexception extends exception { private static final long serialversionuid = 1l; public cyclewheelviewexception(string detailmessage) { super(detailmessage); } } public class cyclewheelviewadapter extends baseadapter { private list<string> mdata = new arraylist<string>(); public void setdata(list<string> mwheellabels) { mdata.clear(); mdata.addall(mwheellabels); } @override public int getcount() { if (cylceenable) { return integer.max_value; } return mdata.size() + mwheelsize - 1; } @override public object getitem(int position) { return ""; } @override public long getitemid(int position) { return position; } @override public boolean isenabled(int position) { return false; } @override public view getview(int position, view convertview, viewgroup parent) { if (convertview == null) { convertview = layoutinflater.from(getcontext()).inflate(mitemlayoutid, null); } textview textview = (textview) convertview.findviewbyid(mitemlabeltvid); if (position < mwheelsize / 2 || (!cylceenable && position >= mdata.size() + mwheelsize / 2)) { textview.settext(""); convertview.setvisibility(view.invisible); } else { textview.settext(mdata.get((position - mwheelsize / 2) % mdata.size())); convertview.setvisibility(view.visible); } return convertview; } } }
mainactivity.java:
public class mainactivity extends activity { private cyclewheelview cyclewheelview0,cyclewheelview1, cyclewheelview2; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); cyclewheelview0 = (cyclewheelview) findviewbyid(r.id.cyclewheelview); list<string> labels = new arraylist<>(); for (int i = 0; i < 12; i++) { labels.add("" + i); } cyclewheelview0.setlabels(labels); cyclewheelview0.setalphagradual(0.5f); cyclewheelview0.setonwheelitemselectedlistener(new wheelitemselectedlistener() { @override public void onitemselected(int position, string label) { log.d("test", label); } }); cyclewheelview1 = (cyclewheelview) findviewbyid(r.id.cyclewheelview1); list<string> labels1 = new arraylist<>(); for (int i = 0; i < 24; i++) { labels1.add("" + i); } cyclewheelview1.setlabels(labels1); try { cyclewheelview1.setwheelsize(5); } catch (cyclewheelviewexception e) { e.printstacktrace(); } cyclewheelview1.setselection(2); cyclewheelview1.setwheelitemlayout(r.layout.item_cyclewheel_custom, r.id.tv_label_item_wheel_custom); cyclewheelview1.setonwheelitemselectedlistener(new wheelitemselectedlistener() { @override public void onitemselected(int position, string label) { log.d("test", label); } }); cyclewheelview2 = (cyclewheelview) findviewbyid(r.id.cyclewheelview2); list<string> labels2 = new arraylist<>(); for (int i = 0; i < 60; i++) { labels2.add("" + i); } cyclewheelview2.setlabels(labels2); try { cyclewheelview2.setwheelsize(7); } catch (cyclewheelviewexception e) { e.printstacktrace(); } cyclewheelview2.setcycleenable(true); cyclewheelview2.setselection(30); cyclewheelview2.setalphagradual(0.6f); cyclewheelview2.setdivider(color.parsecolor("#abcdef"), 2); cyclewheelview2.setsolid(color.white,color.white); cyclewheelview2.setlabelcolor(color.blue); cyclewheelview2.setlabelselectcolor(color.red); cyclewheelview2.setonwheelitemselectedlistener(new wheelitemselectedlistener() { @override public void onitemselected(int position, string label) { log.d("test", label); } }); } }
item_cyclewheel.xml:
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:background="@android:color/transparent" > <textview android:id="@+id/tv_label_item_wheel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="20sp" android:singleline="true" android:layout_centerhorizontal="true" android:layout_centervertical="true" /> </relativelayout>
item_cyclewheel_custom.xml:
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:background="@android:color/transparent" > <textview android:id="@+id/tv_label_item_wheel_custom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleline="true" android:layout_alignparentleft="true" android:layout_centervertical="true" /> <imageview android:layout_width="25dp" android:layout_height="25dp" android:layout_centervertical="true" android:layout_alignparentright="true" android:src="@drawable/ic_launcher" /> </relativelayout>
activity_main.xml:
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <com.example.wheelviewdemo.cyclewheelview android:id="@+id/cyclewheelview" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" > </com.example.wheelviewdemo.cyclewheelview> <com.example.wheelviewdemo.cyclewheelview android:id="@+id/cyclewheelview1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" > </com.example.wheelviewdemo.cyclewheelview> <com.example.wheelviewdemo.cyclewheelview android:id="@+id/cyclewheelview2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" > </com.example.wheelviewdemo.cyclewheelview> </linearlayout>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。