android 自定义选中效果,Android自定义View实现分段选择按钮的实现代码

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

8a48423cca4799c2418955042bbc52dc.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();

}

想要学习的同学,建议还是直接看项目源码。项目源码地址:https://github.com/danledian/SegmentedControl

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值