效果图如下:
因为需要做一个类似闹钟的功能,系统自带的,虽然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 ListitemEntities; 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 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);}