仿锤子滚轮控件
锤子控件样式:
动图效果:
百度云盘-hammer.mp4
手写控件样式:
动图效果:
百度云盘-address.mp4
- 感觉模糊可以下载下来播放
思路:
通过总滚动量来绘制界面, 所有的touch操作只改变滚动量
//当前选中的index与偏移量等变量均由这个变量计算得,若要改变index请改变totalScroll
float totalScroll;//一共滚动了多少,由于是向上滑动,所以这个数一直都是一个负数
绘制:
中间那一栏的高度允许与旁边的不同
中间那一栏的文字在滚动时, 字体大小和颜色会渐变
float percent = Math.abs(itemScroll) / side_text_height;
textPaint.setColor(MathUtils.caculateColor(side_text_color, center_text_color, percent));
textPaint.setTextSize(MathUtils.caculateFloat(side_text_size, center_text_size, percent));
手势操作:
- 按下时结束所有滚动
if (!overScroller.isFinished()) {
overScroller.abortAnimation();
}
- 移动时滚轮跟着动
float currentY = event.getY();
totalScroll += currentY - moveMarkY;//由于向上滑,y变小,但是totalScroll变大, 所以取负
//防止越界
if (totalScroll > 0) {
totalScroll = 0;
}
if (totalScroll < -1 * (mAdapter.getCount() - 1) * side_text_height) {
totalScroll = -1 * (mAdapter.getCount() - 1) * side_text_height;
}
//重绘,绘制时会根据totalScroll计算当前item
invalidate();
- 手指移开时触发fling
if (absVelocity > mMinimumVelocity) {
int maxY;
if (yVelocity < 0) {
//手指向上,滚轮向上,maxY为数据总量的高度-已滚动的量
maxY = (int) Math.abs(-1 * side_text_height * (mAdapter.getCount() - 1) - totalScroll);
scrollDirectionUp = true;
} else {
//手指向上,滚轮向上,maxY为0+已滚动量
maxY = (int) Math.abs(totalScroll);
scrollDirectionUp = false;
}
Log.i(TAG, "onTouchEvent: maxY= " + maxY);
//fling的scroll方法可以是负数,但是fling方法的velocity必须是正数,
//从而导致maxY必然大于startY,也就是说,还需要一个变量记录fling的方向
scrollMarkY = 0;//重置滚动标记并开始滚动
currentScrollType = SCROLL_TYPE_FLING;
overScroller.fling(0, 0, 0, absVelocity, 0, 0, 0, maxY);
}
- 偏移量矫正
//微调任务
private void autoAdjustByScroll() {
//似乎马上执行的话,itemScroll会有概率不准确,改为延时任务
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
scrollMarkY = 0;
int itemScrollInt = (int) itemScroll;
float scrollPercent = itemScrollInt * 1.0f / side_text_height;
int duration = (int) Math.abs(1000 * scrollPercent);
currentScrollType = SCROLL_TYPE_ADJUST;
overScroller.startScroll(0, 0, 0, -itemScrollInt, duration);
postInvalidate();//线程中刷新要调用postInvalidate
}
};
Timer timer = new Timer();
timer.schedule(timerTask, 20);
}
封装:
- 基类 BasePicker
带有标题, 确定和取消按钮, 另外提供两个抽象方法, 用于改变整体样式和滚轮样式
/**
* 可以通过继承来改变布局的样式, 新布局要求:
* <p>一个id为tv_cancel的TextView,用于返回</p>
* <p>一个id为tv_title的TextView,用于显示标题</p>
* <p>一个id为tv_commit的TextView,用于提交</p>
* <p>一个id为fl_content的FrameLayout,用于嵌套滚轮控件</p>
*/
protected int getContentLayout() {
return 0;
}
/**
* 可以通过继承来改变滚轮的样式,新布局要求:
* <p>只有一个WheelView</p>
* <p>宽为0px</p>
* <p>高最好为wrap_content</p>
*/
protected int getWheelViewLayout() {
return 0;
}
- 日期 DatePicker
填充日期数据, 并提供6种模式: 年、月、日、年+月、月+日、年+月+日
public static final int MODE_YEAR_ONLY = 0x0100;
public static final int MODE_MONTH_ONLY = 0x0010;
public static final int MODE_DAY_ONLY = 0x0001;
public static final int MODE_YEAR_MONTH = 0x0110;
public static final int MODE_MONTH_DAY = 0x0011;
public static final int MODE_YEAR_MONTH_DAY = 0x0111;
public void setMode(int mode) {
boolean showYear = (mode & 0x0100) != 0;
boolean showMonth = (mode & 0x0010) != 0;
boolean showDay = (mode & 0x0001) != 0;
int yearVisibility = showYear ? View.VISIBLE : View.GONE;
int monthVisibility = showMonth ? View.VISIBLE : View.GONE;
int dayVisibility = showDay ? View.VISIBLE : View.GONE;
dateWheelView.setVisibility(yearVisibility, monthVisibility, dayVisibility);
}
- 选项 OptionPicker
泛型, 自定义数据
private ArrayList<T> leftData;
private ArrayList<ArrayList<T>> middleData;
private ArrayList<ArrayList<ArrayList<T>>> rightData;
public interface OnOptionSelectedListener {
public void onOptionSelected(int leftPosition, int middlePosition, int rightPosition);
}
- 选项数据模型T
请实现OptionText接口, 或者改写toString()方法
public interface OptionText {
public String getOptionText();
}
public String getText(int position) {
T t = data.get(position);
if (t instanceof OptionText) {
OptionText optionText = (OptionText) t;
return optionText.getOptionText();
} else {
return t.toString();
}
}