欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

仿IOS 带字母索引的滑轮控件

程序员文章站 2022-03-09 21:33:39
效果大概就是这样,右边是字母索引效果   做开发的时候,经常碰到产品经理设计出来的界面是参考ios控件设计出来的 ,比如上图效果 ios有个控件是uipickerview 就是可以上下滑...

效果大概就是这样,右边是字母索引效果

仿IOS 带字母索引的滑轮控件

 

做开发的时候,经常碰到产品经理设计出来的界面是参考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 list list; // 滚动选择器数据
	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 list mdatalist;
	/**
	 * 选中的位置,这个位置是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。