Android三级联动wheel代码分析(一)
联系不到源代码作者 源码地址 ,闲下来分析一下wheel的原理帮助自己更明白点吧。。当然github上面也有很多很好的wheel项目。
如图的效果想要首先肯定要自定义View。
另外这个里面的城市信息是放在assets下面的名称area.json里。我们先从整个这个控件的代码看起把。
唔,写到哪算哪吧。首先选择器肯定需要自定义view
public class ScrollerNumberPicker extends View{
public ScrollerNumberPicker(Context context, AttributeSet attrs,int defStyle){
super(context, attrs, defStyle);
init(context, attrs);
initData();
}
public ScrollerNumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
initData();
}
public ScrollerNumberPicker(Context context) {
super(context);
initData();
}
private void init(Context context, AttributeSet attrs) {
TypedArray attribute = context.obtainStyledAttributes(attrs,R.styleable.NumberPicker);
/** 单元格高度 */
unitHeight = (int) attribute.getDimension(R.styleable.NumberPicker_unitHight, 32);
/** 默认字体 */
normalFont = attribute.getDimension(R.styleable.NumberPicker_normalTextSize, 14.0f);
/** 选中的时候字体 */
selectedFont = attribute.getDimension(R.styleable.NumberPicker_selecredTextSize, 22.0f);
/** 显示多少个内容 */
itemNumber = attribute.getInt(R.styleable.NumberPicker_itemNumber, 7);
/** 默认字体颜色 */
normalColor = attribute.getColor(R.styleable.NumberPicker_normalTextColor, 0xff000000);
/** 选中时候的字体颜色 */
selectedColor = attribute.getColor(R.styleable.NumberPicker_selecredTextColor, 0xffff0000);
/** 线的默认颜色 */
lineColor = attribute.getColor(R.styleable.NumberPicker_lineColor,0xff000000);
/** 蒙板高度 */
maskHight = attribute.getDimension(R.styleable.NumberPicker_maskHight, 48.0f);
/** 是否允许选空 */
noEmpty = attribute.getBoolean(R.styleable.NumberPicker_noEmpty,false);
/** 是否可用 */
isEnable = attribute.getBoolean(R.styleable.NumberPicker_isEnable,true);
//调用recycle()方法,否则这次的设定会对下次的使用造成影响
attribute.recycle();
/** 控件高度 */
controlHeight = itemNumber * unitHeight;
}
}
关于TypedArray的知识简单说下
在自定义view的代码中引入自定义属性,修改构造函数
context通过调用obtainStyledAttributes方法来获取一个TypeArray,然后由该TypeArray来对属性进行设置
obtainStyledAttributes方法有三个,我们最常用的是有一个参数的obtainStyledAttributes(int[] attrs),其参数直接styleable中获得
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyView);
调用结束后务必调用recycle()方法,否则这次的设定会对下次的使用造成影响
/**
* 初始化数据
*/
private void initData() {
/** 正在修改数据,避免ConcurrentModificationException异常 */
isClearing = true;
/** 选择的内容 */
itemList.clear();
/** 设置数据 */ datalist
for (int i = 0; i < dataList.size(); i++) {
ItemObject itmItemObject = new ItemObject();
itmItemObject.id = i;
itmItemObject.itemText = dataList.get(i);
itmItemObject.x = 0;
itmItemObject.y = i * unitHeight;
itemList.add(itmItemObject);
}
isClearing = false;
}
关于ConcurrentModificationException异常
对Vector、ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常
关键点就在于:调用list.remove()方法导致modCount和expectedModCount的值不一致。
注意,像使用for-each进行迭代实际上也会出现这种问题。
expectedModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount。
每次调用add()方法或者remove()方法就会对modCount进行加1操作
/**
* 单条内容
*
* @author zoudong
*/
private class ItemObject {
/** id */
public int id = 0;
/** 内容 */
public String itemText = "";
/** x坐标 */
public int x = 0;
/** y坐标 */
public int y = 0;
/** 移动距离 */
public int move = 0;
/** 字体画笔 */
private Paint textPaint;
/** 字体范围矩形 */
private Rect textRect;
public ItemObject() {
super();
}
/**
* 绘制自身
*
* @param canvas
*/
public void drawSelf(Canvas canvas) {
if (textPaint == null) {
textPaint = new Paint();
textPaint.setAntiAlias(true);
}
if (textRect == null)
textRect = new Rect();
// 判断是否被选择,见下面代码分析
if (isSelected()) {
textPaint.setColor(selectedColor);
// 获取距离标准位置的距离
float moveToSelect = moveToSelected();
moveToSelect = moveToSelect > 0 ? moveToSelect : moveToSelect
* (-1);
// 计算当前字体大小
float textSize = (float) normalFont
+ ((float) (selectedFont - normalFont) * (1.0f - (float) moveToSelect
/ (float) unitHeight));
textPaint.setTextSize(textSize);
} else {
textPaint.setColor(normalColor);
textPaint.setTextSize(normalFont);
}
// 返回包围整个字符串的最小的一个Rect区域
textPaint.getTextBounds(itemText, 0, itemText.length(), textRect);
// 判断是否可视
if (!isInView())
return;
// 绘制内容
canvas.drawText(itemText, x + controlWidth / 2 - textRect.width()
/ 2, y + move + unitHeight / 2 + textRect.height() / 2,
textPaint);
}
/**
* 是否在可视界面内
*
* @param rect
* @return
*/
public boolean isInView() {
if (y + move > controlHeight
|| (y + move + unitHeight / 2 + textRect.height() / 2) < 0)
return false;
return true;
}
/**
* 移动距离
*
* @param _move
*/
public void move(int _move) {
this.move = _move;
}
/**
* 设置新的坐标
*
* @param move
*/
public void newY(int _move) {
this.move = 0;
this.y = y + _move;
}
/**
* 判断是否在选择区域内
*画出来矩形以左上为(0,0)点。下为Y轴,然后进行判断
* @return
*/
public boolean isSelected() {
//手指移动中间部分判断
if ((y + move) >= controlHeight / 2 - unitHeight / 2 + 2
&& (y + move) <= controlHeight / 2 + unitHeight / 2 - 2)
return true;
//手指移动最上部分判断
if ((y + move + unitHeight) >= controlHeight / 2 - unitHeight / 2
+ 2
&& (y + move + unitHeight) <= controlHeight / 2
+ unitHeight / 2 - 2)
return true;
//手指移动最下部分判断
if ((y + move) <= controlHeight / 2 - unitHeight / 2 + 2
&& (y + move + unitHeight) >= controlHeight / 2
+ unitHeight / 2 - 2)
return true;
return false;
}
/**
* 获取移动到标准位置需要的距离
*/
public float moveToSelected() {
return (controlHeight / 2 - unitHeight / 2) - (y + move);
}
}
第一部分代码就分析到这边,明天接着分析Android三级联动wheel代码分析(二)~第一部分自定义view的构造方面代码结束。后续已经写好,可以点我个人信息查看。