100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > android实现横向选择器 安卓开发 简单实现自定义横向滚动选择View : HorizontalselectedView...

android实现横向选择器 安卓开发 简单实现自定义横向滚动选择View : HorizontalselectedView...

时间:2022-07-16 22:40:48

相关推荐

android实现横向选择器 安卓开发 简单实现自定义横向滚动选择View : HorizontalselectedView...

一、需求:

今日产品经理让在产品里面加了个横向选择的功能,控件样子大致要求为:

网上找了好久没找到此控件,只能自己动手写了,很适合新手练习自定义View,并贡献给大家,效果如下:

其实很多滚轮控件也只是这个简单控件 组合一下就可以了 。

有任何问题可以加QQ群询问:661614986。

二、实现思路:

这里我偷懒了,没有把上、左、右三个箭头写到控件里面,写进去也简单,不过突然感觉在外面布局,写个方法出来也是蛮帅的。所以今天我们的主角就是中间的可以横向滑动的部分,乍一看就是个recycleview,不过这里我没有想过要用recycleview来实现,不是不可以,是用recycleview的话,各种判断、计算偏移量太多了,而且需求中要求只是文本,无需加载布局,所以为了节省时间就干脆自定义一个名为HorizontalselectedView的View,宽高无需自己计算,只需在onDraw()方法里面把每个String 画出来, 然后监听滑动事件或者点击左右箭头的时候,重走onDraw()方法就可以了,难点在于onDraw的时候,每个String的坐标如何获得。

2.1、控件特征:

1、中间箭头下面的文字(被选中的文字)颜色和字体和其他的不一样

2、可以横向左右滑动,滑动过程当中,被选中的文字在变化

3、点击左右箭头的时候也可以 实现滚动,从而改变被选中的文字

4、可见区域内,显示的文字数是可以改变的

5、左右滑动的时候有回弹选择效果

根据以上特征,就能得到我们所需要的自定义属性,如下:

在构造方法里面初始化画笔和属性:

public HorizontalselectedView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

this.context = context;

setWillNotDraw(false);//使可以走到onDraw方法

initAttrs(attrs);//初始化属性

initPaint();//初始化画笔

}

/** * 初始化属性 *@param attrs */

private void initAttrs(AttributeSet attrs) {

TintTypedArray tta = TintTypedArray.obtainStyledAttributes(getContext(), attrs,

R.styleable.HorizontalselectedView);

//两种字体颜色和字体大小

seeSize = tta.getInteger(R.styleable.HorizontalselectedView_HorizontalselectedViewSeesize, 5);

selectedTextSize = tta.getFloat(R.styleable.HorizontalselectedView_HorizontalselectedViewSelectedTextSize, 50);

selectedColor = tta.getColor(R.styleable.HorizontalselectedView_HorizontalselectedViewSelectedTextColor, context.getResources().getColor(android.R.color.black));

textSize = tta.getFloat(R.styleable.HorizontalselectedView_HorizontalselectedViewTextSize, 40);

textColor = tta.getColor(R.styleable.HorizontalselectedView_HorizontalselectedViewTextColor, context.getResources().getColor(android.R.color.darker_gray));

}

/** * 初始化画笔 */

private void initPaint() {

textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);//普通文本画笔

textPaint.setTextSize(textSize);

textPaint.setColor(textColor);

selectedPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);//被选中文本画笔

selectedPaint.setColor(selectedColor);

selectedPaint.setTextSize(selectedTextSize);

}

2.2、重写onDraw()方法:

所有的文字里面,被选中的文字是特殊的,他的大小和颜色不一样,所有我们先把他给画出来,关键点在于要测量文本的宽高,代码如下:

selectedPaint.getTextBounds(s, 0, s.length(), rect);

//从矩形区域中读出文本内容的宽高

int centerTextWidth = rect.width();

int centerTextHeight = rect.height();

canvas.drawText(strings.get(n), getWidth() / 2 - centerTextWidth / 2 , getHeight() / 2 + centerTextHeight / 2, selectedPaint);//绘制被选中文字,注意点是y坐标,是以文字底部为中心

下面要做的是就是要遍历集合,把其他的文字给画出来,刚才也提到了,坐标是难点,我们看张图来理一理:

其实最巧妙的是得到图中所标注的一个单元的长度anInt,可见区域的长度除以可见个数就得到了 , 还有就是这个n,给到数据源的时候,我是让数据源集合的长度除以2的,这个可以理解的。然后遍历集合的时候 就可以根据下面得到x的坐标:x=width/2+anInt*(i-n)-textWidth/2,这里面的textWidth我们默认他一样长了,但实际情况中,可能会出现三位数,四位数,三个字,四个字,所以在代码中,我是获得被选中文字左右两面长度的平均值得到的。

if (n > 0 && n < strings.size() - 1) {//获得中间被选中文字左右两边文本宽的平均值

textPaint.getTextBounds(strings.get(n - 1), 0, strings.get(n - 1).length(), rect);

int width1 = rect.width();

textPaint.getTextBounds(strings.get(n + 1), 0, strings.get(n + 1).length(), rect);

int width2 = rect.width();

textWidth = (width1 + width2) / 2;

}

接下来就可以在onDraw()方法里面遍历了:

for (int i = 0; i < strings.size(); i++) {//遍历strings,把每个地方都绘制出来,

if (n > 0 && n < strings.size() - 1) {

textPaint.getTextBounds(strings.get(n - 1), 0, strings.get(n - 1).length(), rect);

int width1 = rect.width();

textPaint.getTextBounds(strings.get(n + 1), 0, strings.get(n + 1).length(), rect);

int width2 = rect.width();

textWidth = (width1 + width2) / 2;

}

if (i == 0) {//得到高,高度是一样的,所以无所谓

textPaint.getTextBounds(strings.get(0), 0, strings.get(0).length(), rect);

textHeight = rect.height();

}

if (i != n)

canvas.drawText(strings.get(i), (i - n) * anInt + getWidth() / 2 - textWidth / 2 + anOffset, getHeight() / 2 + textHeight / 2, textPaint);//画出未被选中的每组文字

}

这样所得到的效果是把strings平铺在了控件上,其实已经有点样子了,下面就是进行滑动监听了。

迷糊了我们休息一下:

2.3、触屏监听事件:

触屏监听,自然而然就是要复写onTouchEvent 方法了 , 这里的触屏事件还是比较简单的,先贴上代码:

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

downX = event.getX();//获得点下去的x坐标

break;

case MotionEvent.ACTION_MOVE://复杂的是移动时的判断

float scrollX = event.getX();

if (scrollX > downX) {

//向右滑动,当滑动距离大于每个单元的长度时,则改变被选中的文字。

if (scrollX - downX >= anInt) {

if (n > 0) {

n = n - 1;

downX = scrollX;

}

}

} else {

//向左滑动,当滑动距离大于每个单元的长度时,则改变被选中的文字。

if (downX - scrollX >= anInt) {

if (n < strings.size() - 1) {

n = n + 1;

downX = scrollX;

}

}

}

invalidate();

break;

case MotionEvent.ACTION_UP:

//抬起手指时,重绘

invalidate();

break;

default:

break;

}

return super.onTouchEvent(event);

}

奇怪的是,在这里面打log,只有down事件触发,move和up事件触发不了,这就要加上一行代码:

setClickable(true);//使可点击

讲解:

down的时候记录下downX坐标值,然后move时根据scrollX 和downX的大小判断向左还是向右,

从而当scrollX -downX的绝对值刚好等于anInt的时候,n相应的加1 或者减一 , 再进行重绘,继而把此时的scrollX 赋值给doownX;

这个时候就实现了 左右滑动的时候,整个文本向右或者向左 一个单元一个单元的移动,这显然不是我们要的效果 , 我们要的是 , 滑动的过程当中 , 文本就跟着手势在动,所以这里面肯定需要个偏移量offSet;

offset=scrollX - downX;

当移动一个单元长度的时候再把offset归零就可以了;所以最终每个文本的x坐标为:

canvas.drawText(strings.get(i), (i - n) * anInt + getWidth() / 2 - textWidth / 2 + anOffset, getHeight() / 2 + textHeight / 2, textPaint);

那up的时候我们就要把offSet归零,然后经行重绘,就产生了回弹效果

这样就基本实现需求了,需要注意的是有几个地方需要加上n的大小判断的,不能让他小于0了,或者大于集合长度了,防止越界,源码已上传至github,点击可查看。

2.4、对外提供的一些方法:

/** * 设置个数据源 * @param strings 数据源String集合 */

public void setData(List strings)

/** * 改变中间可见文字的数目 * @param seeSizes 可见数 */

public void setSeeSize(int seeSizes)

/** * 向左移动一个单元 */

public void setAnLeftOffset()

/** * 向右移动一个单元 */

public void setAnRightOffset()

/** * 获得被选中的文本 * * @return 被选中的文本 */

public String getSelectedString()

三、经验总结:

本篇文章为适合初级程序员学习的典型自定义View,可能很多人看到效果都觉得一头雾水,不知用了什么高大上的东西实现的,其实一步步理下来,就是先展示,再让他动起来,最多就是难在计算坐标而已,木户的地方我们一定要善于画图去总结,发现里面的规律,最后祝各位工作顺利,幸福美满!

只贴出了部分代码,源码已上传至github,点击可查看,欢饮star,fork,有问题或者建议欢饮提出,大家共同进步,不喜勿喷,谢谢!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。