仿IOS 带字母索引的滑轮控件
程序员文章站
2022-03-09 21:33:39
效果大概就是这样,右边是字母索引效果
做开发的时候,经常碰到产品经理设计出来的界面是参考ios控件设计出来的 ,比如上图效果 ios有个控件是uipickerview 就是可以上下滑...
效果大概就是这样,右边是字母索引效果
做开发的时候,经常碰到产品经理设计出来的界面是参考ios控件设计出来的 ,比如上图效果 ios有个控件是uipickerview 就是可以上下滑动 并有些3d效果,非常炫。
但是android并没有提供这样的原生控件支持,所以需要通过其他方式实现类似效果。上图就是我开发中用到的一个效果。
话不多说 ,直接上代码:
activity
package com.example.picscrollview; import java.util.arraylist; import java.util.collections; import java.util.list; import com.example.picscrollview.pickerscrollview.onselectlistener; import com.example.test.r; import android.app.activity; import android.os.bundle; import android.view.view; import android.view.view.onclicklistener; import android.widget.toast; public class mainactivity extends activity implements onclicklistener { boolean state_pressed = true; private string cities[] ={"北京版", "天津版", "上海版", "重庆版", "河北版", "山西版", "辽宁版", "吉林版", "黑龙江版", "江苏版", "浙江版", "安徽版", "福建版", "江西版", "山东版", "河南版", "湖北版", "湖南版", "广东版", "甘肃版", "四川版", "贵州版", "海南版", "云南版", "青海版", "陕西版", "广西版", "*版", "宁夏版", "*版", "内蒙古版", "澳门版", "香港版", "*版","全国版"}; private string[] id = { "1", "2", "3", "4", "5", "6" ,"7","8","9","10","11","12","13","14","15","16", "17", "18", "19", "20", "21", "22" ,"23","24","25","26","27","28","29","30","31","32","33","34","35"}; private pickerscrollview pickerscrlllview; // 滚动选择器 private listlist; // 滚动选择器数据 private characterparser mcharacterparser; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); initdata(); } public void initview() { lettersidebar lettersidebar = (lettersidebar) findviewbyid(r.id.cs_letter_sb); lettersidebar .setontouchingletterchangedlistener(new lettersidebar.ontouchingletterchangedlistener() { @override public void ontouchingletterchanged(string letter) { int jumppos = getpositionforsection(letter.charat(0)); pickerscrlllview.setselected(jumppos); } }); pickerscrlllview = (pickerscrollview) findviewbyid(r.id.pickerscrlllview); pickerscrlllview.setonselectlistener(pickerlistener); findviewbyid(r.id.location_confirm_btn).setonclicklistener(this); } private void initdata() { mcharacterparser = characterparser.getinstance(); list = filleddata(); pinyincomparator pinyincomparator = new pinyincomparator(); collections.sort(list, pinyincomparator); pickerscrlllview.setdata(list); pickerscrlllview.setselected(0); } @override protected void onresume() { super.onresume(); } public void onpause() { super.onpause(); } private list filleddata() { list sortlist = new arraylist (); for (int i = 0, n = cities.length; i < n; i++) { pickers picker = new pickers(); picker.setshowconetnt(cities[i]); picker.setshowid(id[i]); string pinyin = mcharacterparser.getselling(cities[i]); picker.setpinyin(pinyin); string sortstring = pinyin.substring(0, 1).touppercase(); if (sortstring.matches("[a-z]")) { picker.setsortletter(sortstring.touppercase()); } else { picker.setsortletter("#"); } sortlist.add(picker); } return sortlist; } public int getpositionforsection(int section) { for (int i = 0; i < list.size(); i++) { string sortstr = list.get(i).getsortletter(); char firstchar = sortstr.touppercase().charat(0); if (firstchar == section) { return i; } } return -1; } onselectlistener pickerlistener = new onselectlistener() { @override public void onselect(pickers pickers) { toast.maketext(mainactivity.this, pickers.getshowconetnt(), toast.length_long).show(); } }; @override public void onbackpressed() { super.onbackpressed(); } @override public void onclick(view v) { switch (v.getid()) { default: break; } } }
自定义滑动view
package com.example.picscrollview; import java.util.arraylist; import java.util.list; import java.util.timer; import java.util.timertask; import com.example.test.r; import android.annotation.suppresslint; import android.content.context; import android.graphics.canvas; import android.graphics.paint; import android.graphics.paint.align; import android.graphics.paint.fontmetricsint; import android.graphics.paint.style; import android.os.handler; import android.os.message; import android.util.attributeset; import android.view.motionevent; import android.view.view; /** * 自定义滚动选择器 * * @author zlw * */ @suppresslint({ "handlerleak", "clickableviewaccessibility" }) public class pickerscrollview extends view { public static final string tag = "pickerview"; /** * text之间间距和mintextsize之比 */ public static final float margin_alpha = 2.8f; /** * 自动回滚到中间的速度 */ public static final float speed = 2; private listmdatalist; /** * 选中的位置,这个位置是mdatalist的中心位置,一直不变 */ private int mcurrentselected; private paint mpaint; private float mmaxtextsize = 14; private float mmintextsize = 6; private float mmaxtextalpha = 255; private float mmintextalpha = 120; private int mcolortext = 0x333333; private int mviewheight; private int mviewwidth; private float mlastdowny; /** * 滑动的距离 */ private float mmovelen = 0; private boolean isinit = false; private onselectlistener mselectlistener; private timer timer; private mytimertask mtask; handler updatehandler = new handler() { @override public void handlemessage(message msg) { if (math.abs(mmovelen) < speed) { mmovelen = 0; if (mtask != null) { mtask.cancel(); mtask = null; performselect(); } } else // 这里mmovelen / math.abs(mmovelen)是为了保有mmovelen的正负号,以实现上滚或下滚 mmovelen = mmovelen - mmovelen / math.abs(mmovelen) * speed; invalidate(); } }; public pickerscrollview(context context) { super(context); init(); } public pickerscrollview(context context, attributeset attrs) { super(context, attrs); init(); } public void setonselectlistener(onselectlistener listener) { mselectlistener = listener; } private void performselect() { if (mselectlistener != null) mselectlistener.onselect(mdatalist.get(mcurrentselected)); } public void setdata(list datas) { mdatalist = datas; mcurrentselected = datas.size() / 2; invalidate(); } /** * 选择选中的item的index * * @param selected */ public void setselected(int selected) { mcurrentselected = selected; int distance = mdatalist.size() / 2 - mcurrentselected; if (distance < 0) for (int i = 0; i < -distance; i++) { moveheadtotail(); mcurrentselected--; } else if (distance > 0) for (int i = 0; i < distance; i++) { movetailtohead(); mcurrentselected++; } invalidate(); } /** * 选择选中的内容 * * @param mselectitem */ public void setselected(string mselectitem) { for (int i = 0; i < mdatalist.size(); i++) if (mdatalist.get(i).equals(mselectitem)) { setselected(i); break; } } private void moveheadtotail() { pickers head = mdatalist.get(0); mdatalist.remove(0); mdatalist.add(head); } private void movetailtohead() { pickers tail = mdatalist.get(mdatalist.size() - 1); mdatalist.remove(mdatalist.size() - 1); mdatalist.add(0, tail); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); mviewheight = getmeasuredheight(); mviewwidth = getmeasuredwidth(); // 按照view的高度计算字体大小 mmaxtextsize = mviewheight / 8.0f; mmintextsize = mmaxtextsize / 2f; isinit = true; invalidate(); } private void init() { timer = new timer(); mdatalist = new arraylist (); mpaint = new paint(paint.anti_alias_flag); mpaint.setstyle(style.fill); mpaint.settextalign(align.center); mpaint.setcolor(getresources().getcolor(r.color.white)); } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); // 根据index绘制view if (isinit) drawdata(canvas); } private void drawdata(canvas canvas) { // 先绘制选中的text再往上往下绘制其余的text float scale = parabola(mviewheight / 4.0f, mmovelen); float size = (mmaxtextsize - mmintextsize) * scale + mmintextsize; mpaint.settextsize(size); mpaint.setalpha((int) ((mmaxtextalpha - mmintextalpha) * scale + mmintextalpha)); // text居中绘制,注意baseline的计算才能达到居中,y值是text中心坐标 float x = (float) (mviewwidth / 2.0); float y = (float) (mviewheight / 2.0 + mmovelen); fontmetricsint fmi = mpaint.getfontmetricsint(); float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); int indexs = mcurrentselected; string textdata = mdatalist.get(indexs).getshowconetnt(); canvas.drawtext(textdata, x, baseline, mpaint); // 绘制上方data for (int i = 1; (mcurrentselected - i) >= 0; i++) { drawothertext(canvas, i, -1); } // 绘制下方data for (int i = 1; (mcurrentselected + i) < mdatalist.size(); i++) { drawothertext(canvas, i, 1); } } /** * @param canvas * @param position * 距离mcurrentselected的差值 * @param type * 1表示向下绘制,-1表示向上绘制 */ private void drawothertext(canvas canvas, int position, int type) { float d = (float) (margin_alpha * mmintextsize * position + type * mmovelen); float scale = parabola(mviewheight / 4.0f, d); float size = (mmaxtextsize - mmintextsize) * scale + mmintextsize; mpaint.settextsize(size); mpaint.setalpha((int) ((mmaxtextalpha - mmintextalpha) * scale + mmintextalpha)); float y = (float) (mviewheight / 2.0 + type * d); fontmetricsint fmi = mpaint.getfontmetricsint(); float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); int indexs = mcurrentselected + type * position; string textdata = mdatalist.get(indexs).getshowconetnt(); canvas.drawtext(textdata, (float) (mviewwidth / 2.0), baseline, mpaint); } /** * 抛物线 * * @param zero * 零点坐标 * @param x * 偏移量 * @return scale */ private float parabola(float zero, float x) { float f = (float) (1 - math.pow(x / zero, 2)); return f < 0 ? 0 : f; } @override public boolean ontouchevent(motionevent event) { switch (event.getactionmasked()) { case motionevent.action_down: dodown(event); break; case motionevent.action_move: domove(event); break; case motionevent.action_up: doup(event); break; } return true; } private void dodown(motionevent event) { if (mtask != null) { mtask.cancel(); mtask = null; } mlastdowny = event.gety(); } private void domove(motionevent event) { mmovelen += (event.gety() - mlastdowny); if (mmovelen > margin_alpha * mmintextsize / 2) { // 往下滑超过离开距离 movetailtohead(); mmovelen = mmovelen - margin_alpha * mmintextsize; } else if (mmovelen < -margin_alpha * mmintextsize / 2) { // 往上滑超过离开距离 moveheadtotail(); mmovelen = mmovelen + margin_alpha * mmintextsize; } mlastdowny = event.gety(); invalidate(); } private void doup(motionevent event) { // 抬起手后mcurrentselected的位置由当前位置move到中间选中位置 if (math.abs(mmovelen) < 0.0001) { mmovelen = 0; return; } if (mtask != null) { mtask.cancel(); mtask = null; } mtask = new mytimertask(updatehandler); timer.schedule(mtask, 0, 10); } class mytimertask extends timertask { handler handler; public mytimertask(handler handler) { this.handler = handler; } @override public void run() { handler.sendmessage(handler.obtainmessage()); } } public interface onselectlistener { void onselect(pickers pickers); } }
核心的就是上面这个类。另外右边是个字母索引条。想必大家都做过,就不一一粘贴代码了。我将这个整理出来一个demo。
上一篇: 真不开玩笑,贫穷限制了人的想象力