效果圖如下:
因爲需要做一個類似鬧鐘的功能,系統自帶的,雖然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);
}