这是一个简单的,能与seekbar共享滑动的自定义view
我的博客:详解
简单分析:
- 初始化一些参数,设置数据,准备在onDraw方法中连续绘制TextView
- 计算出TextView绘制的坐标点,根据左滑还是右滑设置TextView之间间隙,大小,颜色
- 将seekbar注入进来,根据对调监听设置当前位置,进行重绘,绘制最新的数组列表
实现难点:
- 计算出绘制数组宽度与高度
- TextView绘制坐标与方式
- seekbar回调,改变哪一些值可以达到预期的滑动效果
- 往左滑计算方式,往右滑计算方式
- 设置不同状态下TextViewde参数
自定义View分析:
HorizontalNumberView
- 首先,初始化构造器与必要参数
public class HorizontalNumberView extends View implements SeekBar.OnSeekBarChangeListener {
private static final int SPACE_NUM = 2;
private static String[] NUMBERS = new String[] {
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
};
private static int mNormalTextColor;
private static int mSelectTextColor;
private int mNormalTextSize = 32;
private int mSelectTextSize = 60;
private int mNumberMax;
private SeekBar mSeekBar;
private int mItemHeight;
private int mItemWidth;
private int mOffset;
private int mCurrentProgress = 0;
private int mBeforeProgress = 0;
private int mCurrentIndex = 1;
private double mCurrentValue;
private Paint mPaint;
private Paint mSelectPaint;
private Rect mBounds;
private OnSeekBarChangeListener mOnSeekBarChangeListener;
public HorizontalNumberView(Context context) {
this(context, null);
}
public HorizontalNumberView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public HorizontalNumberView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mNumberMax = NUMBERS.length + SPACE_NUM;
mNormalTextColor = getResources().getColor(R.color.number_view_normal_text_color);
mSelectTextColor = getResources().getColor(R.color.number_view_select_text_color);
//init text paint
mPaint = new Paint();
mPaint.setColor(mNormalTextColor);
mPaint.setAntiAlias(true);
mPaint.setTextSize(mNormalTextSize);
//init color paint
mSelectPaint = new Paint();
mSelectPaint.setColor(mSelectTextColor);
mSelectPaint.setAntiAlias(true);
mSelectPaint.setTextSize(mSelectTextSize);
//init text bound rect
mBounds = new Rect();
}
- 获取PullRelativeLayoutState的状态值,根据它来判断是否处理事件:自己处理,子View处理,不处理等
- OnScrollStateChangeListener把ScrollView滚动高度状态回调出去,给外面使用
HeaderFrameLayout
public class HeaderFrameLayout extends FrameLayout {
private Scroller mScroller;
private int mHeight;
private boolean isOpen;
public HeaderFrameLayout(Context context) {
super(context);
init();
}
public HeaderFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public HeaderFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mScroller = new Scroller(getContext());
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mHeight = getMeasuredHeight();
}
public void setOpen(boolean flag) {
isOpen = flag;
}
public boolean isOpen() {
return isOpen;
}
public void open() {
if (!isOpen) {
return;
}
smoothScrollTo(0, mHeight, 0, -mHeight, 800);
isOpen = false;
}
private void smoothScrollTo(int startX, int startY,
int dx, int dy, int duration) {
mScroller.startScroll(startX, startY, dx, dy, duration);
invalidate();
}
}
- 计算TextView宽高,注入seekbar设置监听,设置必要参数
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//init text width and height
mItemHeight = getMeasuredHeight();
mItemWidth = getMeasuredWidth() / mNumberMax;
//check set seek bar
if (mSeekBar == null) {
throw new RuntimeException("you must call set seekBar method!");
}
mSeekBar.setOnSeekBarChangeListener(this);
}
public void setSeekBar(SeekBar seekBar) {
mSeekBar = seekBar;
mSeekBar.setMax((NUMBERS.length - 1) * 10);//set seekBar max value
}
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mCurrentProgress = progress;
mCurrentIndex = (mCurrentProgress / NUMBERS.length) + 1;
mCurrentValue = mCurrentIndex + ((mCurrentProgress % 10) / 10.0f);
invalidate();
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
mOnSeekBarChangeListener.onStateChangeBack(mCurrentProgress, mCurrentIndex,
getCurrentValue());
}
}
- onDraw方法中根据左滑还是右滑设置TextView距离,即实际的位置
@Override protected void onDraw(Canvas canvas) {
//start draw text
for (int i = 0; i < NUMBERS.length; i++) {
String text = NUMBERS[i];
int textIndex = i + 1;
if (mCurrentProgress > 0 && mCurrentProgress > mBeforeProgress) {//left to right slip
//item % value
float value = (mCurrentProgress % 10) / 10.0f;
if (textIndex - mCurrentIndex == 0) {//current text
mOffset = (int) (mOffset + mItemWidth - value * mItemWidth);
}
if (textIndex - mCurrentIndex == 1) {//current+1
mOffset += mItemWidth;
}
if (textIndex - mCurrentIndex == 2) {//current+2
mOffset = (int) (mOffset + value * mItemWidth);
}
} else if (mCurrentProgress > 0 && mCurrentProgress < mBeforeProgress) {//right to left slip
//item % value
float value = 1 - ((mCurrentProgress % 10) / 10.0f);
if (textIndex - mCurrentIndex == 0 && mCurrentIndex != 1) {//current text
mOffset += mItemWidth;
}
if (textIndex - mCurrentIndex == -1) {//current - 1
mOffset = (int) (mOffset + value * mItemWidth);
}
if (textIndex - mCurrentIndex == 1 && mCurrentIndex != 1) {//current + 1
mOffset = (int) (mOffset + mItemWidth - value * mItemWidth);
}
}
//text width
int textX = (int) (mItemWidth * 0.5f + i * mItemWidth + mOffset);
mPaint.getTextBounds(text, 0, text.length(), mBounds);
//text height
float textHeight = mBounds.height();
int textY = (int) (mItemHeight * 0.5f + textHeight * 0.5f);
float fontHeight = mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top;
float posValue = fontHeight / 2 - mPaint.getFontMetrics().descent - 10;
//text draw
if (textIndex == mCurrentIndex) {
canvas.drawText(text, textX, textY + posValue, mSelectPaint);
} else {
canvas.drawText(text, textX, textY + posValue, mPaint);
}
}
//reset params
mOffset = 0;
mBeforeProgress = mCurrentProgress;
}
onDraw方法为核心内容,必要的地方都有注释,实现方式可能有很多种,分析就到这里。
再来看一次效果图:
Demo下载:我的Github HorizontalNumberView
10/31/2016 12:49:08 AM