效果图如下:
因为需要做一个类似闹钟的功能,系统自带的,虽然7.0包的那个圆盘看着是挺好看的,挺高大上的,但真心不太好用,然后网上找的时候,找到了一个比较精美的日期选择控件,但是奈何她写的模式固定死了,仅仅是可以选择年月日时分,或者是月日,偏偏我需要的时分模式没有,而且是写好了弹窗的形式,与我需要的嵌入到布局中的需求也不太符合,所以干脆是操刀自己仿来写一个了。(她写的日期传送门:https://github.com/mrfluency/Time-Selector)
目前写了一个可拖动可定制的item,然后通过这个item就可以把日期、时间选择的都集成出来了,集成的传送门:https://my.oschina.net/u/1462828/blog/861817,直接上代码:
package com.imxiaoyu.common.widget;
import android.content.Context;
import android.graphics.Bitmap;
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.imxiaoyu.common.impl.OnStringListener;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义时间选择
* Created by 庞光渝 on 2017/3/9.博客:https://my.oschina.net/u/1462828/blog
*/
public class PickerItemView extends View {
/**
* 变量
*/
private List<ItemEntity> itemEntities;
private float startY = 0;//记录手指按下时候的Y坐标
private float itemHeight = 0;//每一个文本的高度(包括上下空白的地方)
private float minFontSize;//最小的文字字体大小
private float maxFontSize;//最大的文字字体大小
private boolean isLoop = false;//是否循环
private String lastValue;//
//用于根据高度设置最小最大文字的大小,范围2-80;
private int minFontSizePercent = 35;
private int maxFontSizePercent = 65;
private int normalFontColor = 0xff999999;//正常的文字颜色
private int pressFontColor = 0xff444444;//选中的文字颜色
/**
* 外部接口
*/
private OnStringListener onPitchOnChangeListener;//拖动的时候,返回当前选中的
public PickerItemView(Context context) {
super(context);
}
public PickerItemView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PickerItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onDraw(Canvas canvas) {
itemHeight = getHeight() / 5;
minFontSize = itemHeight * minFontSizePercent / 100;
maxFontSize = itemHeight * maxFontSizePercent / 100;
if (itemEntities != null) {
drawText(canvas);
}
}
/**
* 重写触摸监听
*
* @param event
* @return
*/
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float y = event.getY() - startY;
startY = event.getY();
deviationY(y);
onPitchOnBack();
invalidate();
break;
case MotionEvent.ACTION_UP:
//手指抬起的时候,计算并偏移Y轴,相当于对齐一下位置,让被选中的那个保持在画布中间
if (itemEntities != null && itemEntities.size() > 0) {
float intervalY = (itemEntities.get(0).getY()) % itemHeight;
intervalY = (intervalY + itemHeight * 2) % itemHeight;
if (intervalY >= itemHeight / 2) {
deviationY((itemHeight - intervalY));
} else {
deviationY(-intervalY);
}
onPitchOnBack();
invalidate();
}
break;
}
return true;
}
/**
* 绘制文本
*
* @param canvas
*/
private void drawText(Canvas canvas) {
for (int i = 0; i < itemEntities.size(); i++) {
ItemEntity itemEntity = itemEntities.get(i);
if ((itemEntity.getY() > (-itemHeight)) && itemEntity.getY() < itemHeight * 6) {
//在范围内,绘制出来
float itemCoreY = itemEntity.getY() + itemHeight / 2;
float itemCoreX = getWidth() / 2;
float size = getFontSize(itemEntity.getY());
int color = normalFontColor;
if (size == maxFontSize) {
color = pressFontColor;
}
Bitmap bitmap = str2Bitmap(itemEntity.getValue(), (int) size, color);
float x = itemCoreX - bitmap.getWidth() / 2;
float y = itemCoreY - bitmap.getHeight() / 2;
canvas.drawBitmap(bitmap, x, y, null);
}
}
}
/**
* 将文本绘制成一个bitmap,并居中
*
* @param testString
* @param size
* @param txtColor
* @return
*/
public Bitmap str2Bitmap(String testString, int size, int txtColor) {
float x = size * testString.length() + 1;
float y = size + 1;
Bitmap bmp = Bitmap.createBitmap((int) x, (int) y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Rect targetRect = new Rect(0, 0, (int) x, (int) y);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(size);
paint.setColor(txtColor);
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(testString, targetRect.centerX(), baseline, paint);
return bmp;
}
/**
* Y 轴偏移,即移动
*
* @param y
*/
private void deviationY(float y) {
if (itemEntities == null || itemEntities.size() <= 0) {
return;
}
if (isLoop) {
//可以循环
if (y > 0) {
//往下拉
if ((itemEntities.get(0).getY() + y) >= -itemHeight) {
ItemEntity itemEntity = itemEntities.get(itemEntities.size() - 1);
itemEntities.remove(itemEntities.size() - 1);
itemEntity.setY(itemEntities.get(0).getY() - itemHeight);
itemEntities.add(0, itemEntity);
}
} else {
//往下拉
if ((itemEntities.get(itemEntities.size() - 1).getY() + y) <= itemHeight * 6) {
ItemEntity itemEntity = itemEntities.get(0);
itemEntities.remove(0);
itemEntity.setY(itemEntities.get(itemEntities.size() - 1).getY() + itemHeight);
itemEntities.add(itemEntities.size(), itemEntity);
}
}
} else {
//不可以循环,上下到顶了就不给滑了
if ((itemEntities.get(0).getY() + y) > itemHeight * 2) {
return;
}
if ((itemEntities.get(itemEntities.size() - 1).getY() + y) < itemHeight * 2) {
return;
}
}
for (int i = 0; i < itemEntities.size(); i++) {
itemEntities.get(i).setY(itemEntities.get(i).getY() + y);
}
}
/**
* 计算Y轴不同位置的需要映射的字体大小
*
* @param y
* @return
*/
private float getFontSize(float y) {
y = y + itemHeight / 2;
if (y <= itemHeight / 2 || y > 4.5 * itemHeight) {
return minFontSize;
}
if (y > itemHeight / 2 && y < itemHeight * 2) {
//慢慢大
return (float) ((maxFontSize - minFontSize) * (y - (itemHeight / 2)) / (1.5 * itemHeight) + minFontSize);
}
if (y >= itemHeight * 2 && y <= itemHeight * 3) {
return maxFontSize;
}
if (y > itemHeight * 3 && y <= 4.5 * itemHeight) {
//慢慢小
return (float) (maxFontSize - (maxFontSize - minFontSize) * (y - itemHeight * 3) / (1.5 * itemHeight));
}
return minFontSize;
}
/**
* 改变值的时候回调通知
*/
private void onPitchOnBack() {
if (onPitchOnChangeListener != null) {
String str = getPitchOn();
if (!str.equals(lastValue)) {
lastValue = str;
onPitchOnChangeListener.onClick(str);
}
}
}
/**
* 设置数组
*
* @param strings
*/
public void setList(final List<String> strings) {
if (strings == null || strings.size() <= 0) {
return;
}
if (strings.size() < 9) {
isLoop = false;
} else {
isLoop = true;
}
post(new Runnable() {
@Override
public void run() {
itemHeight = getHeight() / 5;
itemEntities = new ArrayList<>();
for (int i = 0; i < strings.size(); i++) {
ItemEntity itemEntity = new ItemEntity();
itemEntity.setValue(strings.get(i));
itemEntity.setY(itemHeight * i);
itemEntities.add(itemEntity);
}
invalidate();
}
});
}
/**
* 设置是否循环,如果小于9条记录,直接默认不给循环,必须在setList()之后调用,因为setList会有一个自动设置是否循环的动作
*
* @param bln
*/
private void setIsLoop(boolean bln) {
isLoop = bln;
if (itemEntities.size() < 9) {
isLoop = false;
}
}
/**
* 设置文字颜色
*
* @param normal 未选中颜色
* @param press 选中颜色
*/
public void setFontColor(int normal, int press) {
normalFontColor = normal;
pressFontColor = press;
}
/**
* 根据高度设置最大最小字体的大小,范围是20-80
*
* @param minPercent
* @param maxPercent
*/
public void setFontSize(int minPercent, int maxPercent) {
if (minPercent < 20) {
minPercent = 20;
}
if (maxPercent < 20) {
maxPercent = 20;
}
if (minPercent > 80) {
minPercent = 80;
}
if (maxPercent > 80) {
maxPercent = 80;
}
minFontSizePercent = minPercent;
maxFontSizePercent = maxPercent;
invalidate();
}
/**
* 设置改变监听
*
* @param onStringListener
*/
public void setOnPitchOnChangeListener(OnStringListener onStringListener) {
this.onPitchOnChangeListener = onStringListener;
}
/**
* 返回选中的
*
* @return
*/
public String getPitchOn() {
if (itemEntities != null) {
for (int i = 0; i < itemEntities.size(); i++) {
if (itemEntities.get(i).getY() >= 2 * itemHeight && itemEntities.get(i).getY() < 3 * itemHeight) {
return itemEntities.get(i).getValue();
}
}
}
return "";
}
class ItemEntity {
private String value;//需要显示的文本
private float y;//当前的y坐标
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
}
}
回调接口:
package com.imxiaoyu.common.impl;
/**
* 回调一个字符串
* Created by 她叫我小渝 on 2016/11/1.
*/
public interface OnStringListener {
void onClick(String str);
}