本篇文章给大家分享的内容是如何实现自定义圆环状时间滚轮,有着一定的参考价值,有需要的朋友可以参考一下
先看一下最终效果:
实现功能简介: 一年以内的任意天和week是可以对应起来的,其他的日与月不限制年
实现原理:利用一个自定义的滚轮来写日周月相互对应的逻辑部分
自定义view的源码如下:
package com.lwyy.wheel.view;
import android.content.context;
import android.content.res.typedarray;
import android.graphics.canvas;
import android.graphics.paint;
import android.graphics.rect;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;
import com.lwyy.wheel.r;
import java.util.arraylist;
import java.util.list;
/**
* created by ll on 2017/9/25.
*/
public class circlewheelview extends view {
private static final string tag = circlewheelview.class.getsimplename();
private int mitemalign;
public static final int align_center = 0, align_left = 1, align_right = 2;
private paint mpaint;
private onitemselectedlistener monitemselectedlistener;
private int mtextmaxwidth, mtextmaxheight;
private rect mrectdrawn;
private int mdrawncenterx, mdrawncentery; //滚轮选择器绘制中心坐标
private int mwheelcenterx, mwheelcentery; //滚轮选择器中心坐标
private int mitemtextcolor, mselecteditemtextcolor; //数据项文本颜色以及被选中的数据项文本颜色
private int mitemheight;
private float mitemtextsize;
private int mlastpointy; //用户手指上一次触摸事件发生时事件y坐标
private float movey;
private list mdata = new arraylist();
private list mdatacc = new arraylist();
private int mturntocenterx;
private int mcurrentitemposition;
private int mdefaulthalfnum = 7, mdefaultvisiblenum = 13; //设置当前view默认可见的item个数
private int mvisiblehalfnum, mvisiblecount; //视图区域内的展示的item个数
private int mdatasize;
private boolean iscyclic; //数据是否循环展示 针对list的数目小于mdefaultvisiblenum
private boolean isloopdisplay; //所有的数据是否是球形展示,即永远的头尾衔接,如果iscyclic是true,则该值也是true
private float[] rates = null;
private float[] textsizerates = null;
public circlewheelview(context context) {
this(context, null);
}
public circlewheelview(context context, attributeset attrs) {
super(context, attrs);
typedarray a = context.obtainstyledattributes(attrs, r.styleable.circlewheelview);
mitemtextsize = a.getdimensionpixelsize(r.styleable.circlewheelview_wheel_item_text_size,
getresources().getdimensionpixelsize(r.dimen.wheelitemtextsize_default));
mitemalign = a.getint(r.styleable.circlewheelview_wheel_item_align, align_center);
mitemtextcolor = a.getcolor(r.styleable.circlewheelview_wheel_item_text_color, 0xff888888);
mselecteditemtextcolor = a.getcolor(r.styleable.circlewheelview_wheel_selected_item_text_color, 0xff888899);
mturntocenterx = a.getint(r.styleable.circlewheelview_wheel_turn_to_centerx, 0); //转向中间,偏移的距离
iscyclic = a.getboolean(r.styleable.circlewheelview_wheel_cyclic, false);
isloopdisplay = a.getboolean(r.styleable.circlewheelview_wheel_loop_display, false);
a.recycle();
mpaint = new paint(paint.anti_alias_flag | paint.dither_flag | paint.linear_text_flag);
mpaint.setstyle(paint.style.fill);
mrectdrawn = new rect();
}
@override
protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
int modewidth = measurespec.getmode(widthmeasurespec);
int modeheight = measurespec.getmode(heightmeasurespec);
int sizewidth = measurespec.getsize(widthmeasurespec);
int sizeheight = measurespec.getsize(heightmeasurespec);
// 计算原始内容尺寸
int resultwidth = mtextmaxwidth;
int resultheight = mtextmaxheight * mvisiblecount;
// 考虑内边距对尺寸的影响
resultwidth += getpaddingleft() + getpaddingright();
resultheight += getpaddingtop();
// 考虑父容器对尺寸的影响
resultwidth = measuresize(modewidth, sizewidth, resultwidth);
resultheight = measuresize(modeheight, sizeheight, resultheight);
setmeasureddimension(resultwidth, resultheight);
}
@override
protected void onsizechanged(int w, int h, int oldw, int oldh) {
super.onsizechanged(w, h, oldw, oldh);
// 获取内容区域中心坐标
mrectdrawn.set(getpaddingleft(), getpaddingtop(), getwidth() - getpaddingright(),
getheight() - getpaddingbottom());
mwheelcenterx = mrectdrawn.centerx();
mwheelcentery = mrectdrawn.centery();
computedrawncenter();
}
@override
protected void ondraw(canvas canvas) {
if (mdatacc.size() > 0) {
mpaint.setstrokewidth(1);
mpaint.setsubpixeltext(true); //设置该项为true,将有助于文本在lcd屏幕上的显示效果
canvas.save();
int indexbottom = 0;
float distancex = 0, mdrawnitemcentery = 0;
int endindex = iscyclic ? 13 : math.min(mdefaultvisiblenum, mdatacc.size());
if (endindex > mdata.size())
endindex = mdata.size();
for (int i = 0; i < endindex; i++) {
if (i == 0) mpaint.setcolor(mselecteditemtextcolor);
else mpaint.setcolor(mitemtextcolor);
if (iscyclic) {
if (i < mvisiblehalfnum)
indexbottom = i;
else
indexbottom = i - mvisiblehalfnum + 1;
if (i < mvisiblehalfnum)
mdrawnitemcentery = (int) (mdrawncentery - (mitemheight * rates[indexbottom]));
else
mdrawnitemcentery = (int) (mdrawncentery + (mitemheight * rates[indexbottom]));
if (i == endindex - 1)
mdrawnitemcentery = (int) (mdrawncentery + (mitemheight * rates[indexbottom]) - 10);
} else if (mdata.size() < 13) { if (i < mvisiblehalfnum + 1)
indexbottom = i; else
indexbottom = i - mvisiblehalfnum; if (i < mvisiblehalfnum + 1)
mdrawnitemcentery = (int) (mdrawncentery - (mitemheight * rates[indexbottom])); else
mdrawnitemcentery = (int) (mdrawncentery + (mitemheight * rates[indexbottom])); if (i == endindex - 1)
mdrawnitemcentery = (int) (mdrawncentery + (mitemheight * rates[indexbottom]) - 10);// if (i < mvisiblehalfnum)// indexbottom = i;// else// indexbottom = i - mvisiblehalfnum + 1;//// if (i < mvisiblehalfnum)// mdrawnitemcentery = (int) (mdrawncentery - (mitemheight * rates[indexbottom]));// else// mdrawnitemcentery = (int) (mdrawncentery + (mitemheight * rates[indexbottom]));// if (i == endindex - 1)// mdrawnitemcentery = (int) (mdrawncentery + (mitemheight * rates[indexbottom]) - 10);
} else {
if (i < mvisiblehalfnum)
indexbottom = i;
else
indexbottom = i - mvisiblehalfnum + 1;
// logutil.e(tag, "indexbottom:" + indexbottom + ",mvisiblehalfnum:" + mvisiblehalfnum + ",i:" + i);
if (indexbottom >= mdefaulthalfnum)
continue;
if (i < mvisiblehalfnum)
mdrawnitemcentery = (int) (mdrawncentery - (mitemheight * rates[indexbottom])); else
mdrawnitemcentery = (int) (mdrawncentery + (mitemheight * rates[indexbottom])); if (i == endindex - 1)
mdrawnitemcentery = (int) (mdrawncentery + (mitemheight * rates[indexbottom]) - 10);
}
mpaint.settextsize(mitemtextsize * textsizerates[indexbottom]);
painttext(canvas, distancex, rates, mdrawnitemcentery, indexbottom, i);
}
}
}
private void painttext(canvas canvas, float distancex, float[] rates, float drawncentery, int indexbottom, int i) {
float mdistancesubx = mdrawncenterx - mturntocenterx * rates[indexbottom] * rates[indexbottom];
float mdistanceaddx = mdrawncenterx + mturntocenterx * rates[indexbottom] * rates[indexbottom];
switch (mitemalign) {
case align_center:
distancex = mdrawncenterx;
break;
case align_left:
distancex = mdata.get(i).tostring().length() <= 1 ?
mdistancesubx + 4 * (mvisiblehalfnum - indexbottom) : mdistancesubx;
break;
case align_right:
distancex = mdata.get(i).tostring().length() <= 1 ?
mdistanceaddx - 4 * (mvisiblehalfnum - indexbottom) : mdistanceaddx;
break;
}
string text = "";
if (!isloopdisplay && !iscyclic && mdatasize > mdefaulthalfnum) {
if (mcurrentitemposition < mdefaulthalfnum - 1 && (i >= mdefaulthalfnum || i <= mcurrentitemposition)) {
text = string.valueof(mdata.get(i));
} else if (mcurrentitemposition > mdefaulthalfnum - 1 && i < mdefaulthalfnum + (mdatasize - mcurrentitemposition) - 1)
text = string.valueof(mdata.get(i));
else if (mcurrentitemposition == mdefaulthalfnum - 1)
text = string.valueof(mdata.get(i));
} else if (iscyclic && !isloopdisplay) {
if (mcurrentitemposition < mdefaulthalfnum - 1 && ((i >= mdefaulthalfnum && i < mdatacc.size() - mcurrentitemposition - 1 + mdefaulthalfnum) || i <= mcurrentitemposition)) {
text = string.valueof(mdata.get(i));
} else if (mcurrentitemposition > mdefaulthalfnum - 1 && i < mdefaulthalfnum + (mdatacc.size() - mcurrentitemposition) - 1)
text = string.valueof(mdata.get(i)); else if (mcurrentitemposition == mdefaulthalfnum - 1 && i < mdatacc.size())
text = string.valueof(mdata.get(i));
} else {
text = string.valueof(mdata.get(i));
// logutil.i(tag, "text:" + mdata.get(i) + ",i:" + i);
}
canvas.drawtext(text, distancex, drawncentery, mpaint);
}
@override
public boolean ontouchevent(motionevent event) {
switch (event.getaction()) {
case motionevent.action_down:
mlastpointy = (int) event.gety();
break;
case motionevent.action_move:
movey = event.gety() - mlastpointy;
if (math.abs(movey) < 1) break;
if (mdatacc.size() < mdefaulthalfnum)
break;
if (!isloopdisplay) {
if (mcurrentitemposition == 0 && movey > 0)
break;
if (iscyclic && mdatacc.size() < mdefaultvisiblenum && mcurrentitemposition == mdatacc.size() - 1 && movey < 0)
break;
else if (mcurrentitemposition == mdatasize - 1 && movey < 0)
break;
}
changemoveitemposition(event, mitemheight * 3 / 2);
break;
case motionevent.action_up:
if (mdatacc.size() < mdefaulthalfnum) {
changemoveitemposition(event, mitemheight / 2);
}
if (null != monitemselectedlistener && mcurrentitemposition < mdatacc.size() && mdatacc.size() > 0 && mcurrentitemposition >= 0)
monitemselectedlistener.onitemselected(this, mdatacc.get(mcurrentitemposition), mcurrentitemposition); break;
}
return true;
}
private void changemoveitemposition(motionevent event, int moveheight) {
if (math.abs(movey) > moveheight) {
mlastpointy = (int) event.gety();
if (movey > 0)
mcurrentitemposition--;
else
mcurrentitemposition++;
if (mcurrentitemposition > mdatacc.size() - 1)
mcurrentitemposition -= mdatacc.size();
if (mcurrentitemposition < 0)
mcurrentitemposition += mdatacc.size();
if (mcurrentitemposition >= 0 && mcurrentitemposition < mdatacc.size())
setselecteditemposition(mdatacc, mcurrentitemposition);
}
}
private void computetextsize() {
mtextmaxwidth = mtextmaxheight = 0;
for (object obj : mdata) {
string text = string.valueof(obj);
int width = (int) mpaint.measuretext(text);
mtextmaxwidth = math.max(mtextmaxwidth, width);
}
paint.fontmetrics metrics = mpaint.getfontmetrics();
mtextmaxheight = (int) (metrics.bottom - metrics.top);
}
private int measuresize(int mode, int sizeexpect, int sizeactual) {
int realsize; if (mode == measurespec.exactly) {
realsize = sizeexpect;
} else {
realsize = sizeactual; if (mode == measurespec.at_most)
realsize = math.min(realsize, sizeexpect);
} return realsize;
}
//固定的等比高度
private void computedrawncenter() {
int num = 7;
mitemheight = mrectdrawn.height() / (num * 2 - 2);
mpaint.settextalign(paint.align.center);
mdrawncenterx = mwheelcenterx;
mdrawncentery = mwheelcentery + mitemheight / num;
}
public void updatevisibleitemcount(int num) {
if (iscyclic) {
mvisiblecount = mdefaultvisiblenum = 13;
mvisiblehalfnum = mdefaulthalfnum = 7;
} else {
if (num >= mdefaulthalfnum)
mvisiblehalfnum = mdefaulthalfnum;
else {
mvisiblehalfnum = num % 2 == 1 ? num / 2 + 1 : num / 2;
}
}
// logutil.e(tag, "mvisiblehalfnum:" + mvisiblehalfnum);
mvisiblecount = math.min(mdefaultvisiblenum, num);
mdatasize = iscyclic ? mdefaultvisiblenum : num;
}
public void setdata(list mdata, int pos) {
setdata(mdata, pos, true);
}
public void setdata(list mdata, int pos, boolean isloopdisplay) {
setdata(mdata, pos, isloopdisplay, false, 13);
}
public void setdata(list mdata, int pos, boolean isloopdisplay, boolean iscycle) {
setdata(mdata, pos, isloopdisplay, iscycle, 13);
}
/**
* @param mdata 传入的展示列表数据
* @param pos 当前view的中间item展示的内容
* @param isloopdisplay 所有的数据是否是球形展示,即永远的头尾衔接,即item由mdatasize - 1下滑变为0,默认true
* @param iscycle 数据是否循环展示 针对list的数目小于mdefaultvisiblenum. 默认false
* @param visibleshownum 当前界面展示的item个数
*/
public void setdata(list mdata, int pos, boolean isloopdisplay, boolean iscycle, int visibleshownum) {
if (mdata.size() <= 0 || pos < 0 || pos >= mdata.size())
return;
if (visibleshownum % 2 == 1)
visibleshownum++;
if (mdata.size() > 12 && iscycle)
iscycle = false;
else if (mdata.size() < 13 && !iscycle && !isloopdisplay) {
iscycle = true;
}
mdefaultvisiblenum = mdata.size() < 13 && !iscycle ? mdata.size() : 13;
mdefaulthalfnum = mdefaultvisiblenum < 13 ? mdefaultvisiblenum / 2 : 7;
this.isloopdisplay = isloopdisplay;
this.iscyclic = iscycle;
if (this.mdatacc.size() > 0)
this.mdatacc.clear();
this.mdatacc.addall(mdata);
this.mcurrentitemposition = pos;
updatevisibleitemcount(mdata.size());
setselecteditemposition(mdata, pos);
updaterates();
computetextsize();
requestlayout();
invalidate();
}
public void updatedatapos(int pos) {
if (pos < 0)
return;
if (pos >= mdatacc.size())
pos = mdatacc.size() / 2 - 1;
this.mcurrentitemposition = pos;
setselecteditemposition(mdatacc, pos);
invalidate();
}
private void updaterates() {
int num = 7;
if (null == rates)
rates = new float[num];
if (null == textsizerates)
textsizerates = new float[num];
float rate = 0.0f;
for (int i = 0; i < num; i++) {
if (i > 0) {
rate = (3 * (11 * i - (i - 1) * (i - 1))) / 22f;
if (i == num - 1)
rate = (3 * (11 * i - (i - 1) * (i - 1)) + 4) / 22f;
}
rates[i] = rate;
textsizerates[i] = ((18 - 7 * i / 3) / 13f);
}
}
public void setitemalign(int align) {
this.mitemalign = align;
invalidate();
}
public void setturntocenter(int turntocenterx) {
this.mturntocenterx = turntocenterx;
requestlayout();
invalidate();
}
public void setselecteditemposition(list mdatas, int centerpos) {
if (mdata.size() > 0)
mdata.clear();
mdatasize = iscyclic ? mdefaultvisiblenum : mdatas.size();
int halfindex = iscyclic ? 7 : math.min(mvisiblehalfnum, mdefaulthalfnum);
// logutil.i(tag, "endindex:" + endindex + ",toppos:" + toppos + ",centerpos:" + centerpos + ",bottompos:" + bottompos + ",halfindex:" + halfindex);
// logutil.i(tag, "endindex:" + endindex + ",mvisiblehalfnum:" + mvisiblehalfnum + ",mdefaulthalfnum:" + mdefaulthalfnum + ",mdefaultvisiblenum:" + mdefaultvisiblenum);
int bottom = 0;
mdata.add(0, mdatas.get(centerpos));
if ((iscyclic && mdatas.size() < 13) || mdatas.size() >= 13) {
for (int i = 1; i < halfindex; i++) {
bottom = centerpos - i;
bottom = chooseindex(bottom, mdatas.size());
mdata.add(mdatas.get(bottom));
}
for (int i = mdatas.size() - 1; i >= halfindex; i--) {
bottom = centerpos + mdatas.size() - i;
bottom = chooseindex(bottom, mdatas.size());
mdata.add(mdatas.get(bottom));
}
if (iscyclic)
for (int i = mdatas.size(); i < 13; i++) {
bottom = i - halfindex + 1 + centerpos;
bottom = chooseindex(bottom, mdatas.size());
// logutil.e(tag, "bottom:" + bottom + ",:" + mdatas.get(bottom));
mdata.add(mdatas.get(bottom));
}
} else if (mdatas.size() < 13) {
//下面个数比上面多
for (int i = 1; i <= halfindex; i++) {
bottom = centerpos - i;
bottom = chooseindex(bottom, mdatas.size());
mdata.add(mdatas.get(bottom));
}
for (int i = mdatas.size() - 1; i > halfindex; i--) {
bottom = centerpos + mdatas.size() - i;
bottom = chooseindex(bottom, mdatas.size());
mdata.add(mdatas.get(bottom));
}
}
invalidate();
}
public void settextsize(float textsize) {
this.mitemtextsize = textsize;
invalidate();
}
public void settextselectcolor(int colorres) {
this.mselecteditemtextcolor = colorres;
requestlayout();
invalidate();
}
public int chooseindex(int index, int datasize) {
if (index > datasize - 1)
index -= datasize;
if (index < 0)
index += datasize;
return index;
}
public void setonitemselectedlistener(onitemselectedlistener onitemselectedlistener) {
this.monitemselectedlistener = onitemselectedlistener;
}
public int getlistsize() {
if (null != mdatacc)
return mdatacc.size();
return 0;
}
/**
* 滚轮选择器item项被选中时监听接口
*/
public interface onitemselectedlistener {
void onitemselected(circlewheelview view, object data, int position);
}
}
以上就是如何实现自定义圆环状时间滚轮的详细内容。