android分段加载_Android自定义View实现分段选择按钮的实现代码

首先演示下效果,分段选择按钮,支持点击和滑动切换。

8e75de40b792cf65a05929a8b1307011.gif

视图绘制过程中,要执行onMeasure、onLayout、onDraw等方法,这也是自定义控件最常用到的几个方法。

onMeasure:测量视图的大小,可以根据MeasureSpec的Mode确定父视图和子视图的大小。

onLayout:确定视图的位置

onDraw:绘制视图

这里就不做过多的介绍,主要介绍本控件涉及的到的部分。

1.1 获取item大小、起始位置

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if(isItemZero() || getMeasuredWidth() == 0)

return;

mHeight = getMeasuredHeight();

int width = getMeasuredWidth();

mItemWidth = (width - 2 * itemHorizontalMargin)/getCount();

mStart = itemHorizontalMargin + mItemWidth * selectedItem;

mEnd = width - itemHorizontalMargin - mItemWidth;

}

1.2 绘制

绘制背景,所有的Item,以及选中项

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if(isItemZero())

return;

drawBackgroundRect(canvas);

drawUnselectedItemsText(canvas);

drawSelectedItem(canvas);

drawSelectedItemsText(canvas);

}

* 绘制背景区域

背景区域就是个带圆角的长方形

/**

* 画背景区域

* @param canvas

*/

private void drawBackgroundRect(Canvas canvas) {

float r = cornersMode == Round?cornersRadius: mHeight >> 1;

mPaint.setXfermode(null);

mPaint.setColor(backgroundColor);

mRectF.set(0, 0, getWidth(), getHeight());

canvas.drawRoundRect(mRectF, r, r, mPaint);

}

* 绘制所有未选中Item的文字

轮流绘制所有Item的文字

/**

* 画所有未选中Item的文字

* @param canvas

*/

private void drawUnselectedItemsText(Canvas canvas) {

mTextPaint.setColor(textColor);

mTextPaint.setXfermode(null);

for (int i = 0; i< getCount(); i++){

int start = itemHorizontalMargin + i * mItemWidth;

float x = start + (mItemWidth >> 1) - mTextPaint.measureText(getName(i))/2;

float y = (getHeight() >> 1) - (mTextPaint.ascent() + mTextPaint.descent())/2;

canvas.drawText(getName(i), x, y, mTextPaint);

}

}

* 绘制选中项

/**

* 画选中项

* @param canvas

*/

private void drawSelectedItem(Canvas canvas) {

float r = cornersMode == Round?cornersRadius: (mHeight >> 1) - itemVerticalMargin;

mPaint.setColor(selectedItemBackgroundColor);

mRectF.set(mStart, itemVerticalMargin, mStart + mItemWidth, getHeight() - itemVerticalMargin);

canvas.drawRoundRect(mRectF, r, r, mPaint);

}

* 绘制选中Item的文字

当选中项移动时,刚移动到下一个Item时,颜色应该是选中的颜色。这里在原来文字之上再画选中Item的文字颜色,就有了被选中的效果。

/**

* 画选中Item的文字

* @param canvas

*/

private void drawSelectedItemsText(Canvas canvas) {

canvas.saveLayer(mStart, 0, mStart + mItemWidth, getHeight(), null, Canvas.ALL_SAVE_FLAG);

mTextPaint.setColor(selectedItemTextColor);

mTextPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));

int begin = mStart/mItemWidth;

int end = (begin + 2) < getCount()?begin+2:getCount();

for (int i = begin; i< end; i++){

int start = itemHorizontalMargin + i * mItemWidth;

float x = start + (mItemWidth >> 1) - mTextPaint.measureText(getName(i))/2;

float y = (getHeight() >> 1) - (mTextPaint.ascent() + mTextPaint.descent())/2;

canvas.drawText(getName(i), x, y, mTextPaint);

}

canvas.restore();

}

1.3 添加手势事件

手势分为三种,ACTION_DOWN、ACTION_MOVE、ACTION_UP,对应动作就是按下,滑动,按起。

当按下时确定按下位置,是在当前Item,则不做处理,当按下位置为其它Item位置,就滑动到其它Item位置。

当手势滑动时,计算相对滑动值,通过改变mStart,改变选中项的位置。

当手势按起时,根据按下位置、速度和方向,判断是否可用移动到下一个Item。

@Override

public boolean onTouchEvent(MotionEvent event) {

if(!isEnabled() || !isInTouchMode() || getCount() == 0)

return false;

if (mVelocityTracker == null) {

mVelocityTracker = VelocityTracker.obtain();

}

mVelocityTracker.addMovement(event);

int action = event.getActionMasked();

if(action == MotionEvent.ACTION_DOWN){

x = event.getX();

onClickDownPosition = -1;

final float y = event.getY();

if(isItemInside(x, y)){

return scrollSelectEnabled;

}else if(isItemOutside(x, y)){

if(!mScroller.isFinished()){

mScroller.abortAnimation();

}

onClickDownPosition = (int) ((x - itemHorizontalMargin)/ mItemWidth);

startScroll(positionStart(x));

return true;

}

return false;

}else if(action == MotionEvent.ACTION_MOVE){

if(!mScroller.isFinished() || !scrollSelectEnabled){

return true;

}

float dx = event.getX() - x;

if(Math.abs(dx) > MIN_MOVE_X){

mStart = (int) (mStart + dx);

mStart = Math.min(Math.max(mStart, itemHorizontalMargin), mEnd);

postInvalidate();

x = event.getX();

}

return true;

}else if(action == MotionEvent.ACTION_UP){

int newSelectedItem;

float offset = (mStart - itemHorizontalMargin)%mItemWidth;

float itemStartPosition = (mStart - itemHorizontalMargin) * 1.0f/ mItemWidth;

if(!mScroller.isFinished() && onClickDownPosition != -1){

newSelectedItem = onClickDownPosition;

}else{

if(offset == 0f){

newSelectedItem = (int)itemStartPosition;

}else {

VelocityTracker velocityTracker = mVelocityTracker;

velocityTracker.computeCurrentVelocity(VELOCITY_UNITS, mMaximumFlingVelocity);

int initialVelocity = (int) velocityTracker.getXVelocity();

float itemRate = offset/mItemWidth;

if (isXVelocityCanMoveNextItem(initialVelocity, itemRate)){

newSelectedItem = initialVelocity > 0?(int)itemStartPosition+1:(int)itemStartPosition;

}else {

newSelectedItem = Math.round(itemStartPosition);

}

newSelectedItem = Math.max(Math.min(newSelectedItem, getCount() - 1), 0);

startScroll(getXByPosition(newSelectedItem));

}

}

onStateChange(newSelectedItem);

mVelocityTracker = null;

onClickDownPosition = -1;

return true;

}

return super.onTouchEvent(event);

}

1.4 保存状态

当手机屏幕方向转换或者内存不足等情况下, 视图会重新加载,这样就会导致状态丢失。使用onSaveInstanceState和onRestoreInstanceState方法保存并恢复状态。

@Override

public Parcelable onSaveInstanceState() {

Parcelable parcelable = super.onSaveInstanceState();

SelectedItemState pullToLoadState = new SelectedItemState(parcelable);

pullToLoadState.setSelectedItem(selectedItem);

return pullToLoadState;

}

@Override

public void onRestoreInstanceState(Parcelable state) {

if(!(state instanceof SelectedItemState))

return;

SelectedItemState pullToLoadState = ((SelectedItemState)state);

super.onRestoreInstanceState(pullToLoadState.getSuperState());

selectedItem = pullToLoadState.getSelectedItem();

invalidate();

}

到此这篇关于Android自定义View实现分段选择按钮的文章就介绍到这了,更多相关Android自定义View分段选择按钮内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值