上班了一段时间了,最近比较懒没什么更新,今天终于忙的差不多了,把最后一个bug解决了,就来谢谢总结,这次主要跟大家聊聊对于第三方框架的个人修改。我这个人技术不好,也懒很多时候不想自己自定义View用,然后越懒技术越渣,好了,自我吐槽先到这里。
1.到底为什么要修改WheelView,这个问题是测试提出来的,说多级联动时如果Item的字数太多的话就看不清字了,这里Android和Ios是一样的,就要求要单行显示,不缩小字体,参照物的话就是TextView的android:ellipsize="end"这条属性。
2.拿到这个问题了该如何去改,首先我们需要知道这个问题是来自哪里,不用想是我们引用的第三方WheelView和PickerView,这个问题的关键所在应该是在WheelView,那么去WheelView找原因,那找原因的第一步是什么,就是找onDraw(),毕竟你再牛逼的自定义控件还是需要画出来的。
@Override
protected void onDraw(Canvas canvas) {
/**/
//如果是label每项都显示的模式,并且item内容不为空、label 也不为空
if (!isCenterLabel && !TextUtils.isEmpty(label) && !TextUtils.isEmpty(getContentText(visibles[counter]))) {
contentText = getContentText(visibles[counter]) + label;
Log.e("TAG", "contentText and label is " + contentText);
} else {
contentText = getContentText(visibles[counter]);
Log.e("TAG", "contentText is " + contentText);
}
//为了显示完全而重新定义字体的大小
reMeasureTextSize(contentText);
//计算开始绘制的位置
measuredCenterContentStart(contentText);
measuredOutContentStart(contentText);
/**/
}
不想贴太多代码的,但是不贴代码大家又很疑惑。这里我们发现了一个方法,进入这个方法会重新自定义字体的大小,为了我们的问题,我们先把这里注释掉,看看是不是会解决问题,事实上是可以的,接下来就发现字体的大小没问题了,但是控件为了显示美观都是在中间显示的,这就造成了Item上显示的是我们文字的中间部分,下一步呢,我们看Item上显示的内容是从哪里来的。我们通过上述代码发现我们的Item内容是contentText是从getContentText获取到的,这里我们再进去看源码,这个地方的源码还是需要看下的
/**
* 获取所显示的数据源
*
* @param item data resource
* @return 对应显示的字符串
*/
private String getContentText(Object item) {
if (item == null) {
return "";
} else if (item instanceof IPickerViewData) {
return ((IPickerViewData) item).getPickerViewText();
} else if (item instanceof Integer) {
//如果为整形则最少保留两位数.
return getFixNum((int) item);
}
return item.toString();
}
这里我看到了在我们常用的过程中我们传入的Item都是String的,那么对最后一个处理就好了。修改之前先梳理下大致的思路。
1.如果传入的字符串画完后刚好或者小于控件的宽我们就不再修改
2.如果传入的字符串画完以后大于了控件的宽我们就把字符串进行格式化
3.格式化如何做,假如我们的控件宽是20,现在有10个字符传过来,10个字符的长度是50,我们需要做的就是尝试依次减少字符直至小于20,等到小于20了我们就再后面加上...,这里是可以优化的,暂时不管,下面继续。
好了根据上面的思路和中心思想我写了下面的代码
/**
* 这个方法 暂时用来进行字体格式化
* @param temp
* @return
*/
private String formatText(String temp) {
Rect rect = new Rect();
StringBuilder sb = new StringBuilder();
String s = sb.append(temp).toString();
if (measuredWidth == 0) {
return temp;
}
isFirstFormat = true;
while (true) {
paintCenterText.getTextBounds(s, 0, s.length(), rect);
if (rect.width() > measuredWidth) {
if (s.endsWith("..")) {
sb = new StringBuilder();
sb.append(s.substring(0, s.length() - 3)).append("..");
s = sb.toString();
} else {
sb = new StringBuilder();
sb.append(s.substring(0, s.length() - 1));
s = sb.toString();
}
} else {
if (temp.equals(s)) {
return temp;
}
if (s.endsWith("..")) {
return s;
} else {
sb = new StringBuilder();
sb.append(s).append("..");
s = sb.toString();
}
}
}
}
通过上述方法我们在获得Item的内容时进行了内容上的格式化。后面的所有操作都是依据getContentText(),来进行继续操作的,我们把字符串格式化了,后续就不用管了,可以把我们注释的reMeasureTextSize()取消注释了。运行下发现可以了,那么我们是不是要新建个自定义View呢,完全不用(关键是太麻烦了),我们自定义个属性ellipsizeEnd确定是否后面显示..就可以了。通过判断ellipsizeEnd的boolean值进行显示,最终我们改变getContentText(),代码如下
/**
* 获取所显示的数据源
*
* @param item data resource
* @return 对应显示的字符串
*/
private String getContentText(Object item) {
if (item == null) {
return "";
} else if (item instanceof IPickerViewData) {
if (!ellipsizeEnd) {
return ((IPickerViewData) item).getPickerViewText();
} else {
return formatContentText(((IPickerViewData) item).getPickerViewText());
}
} else if (item instanceof Integer) {
//如果为整形则最少保留两位数.
return getFixNum((int) item);
}
if (!ellipsizeEnd) {
return item.toString();
} else {
return formatContentText(item.toString());
}
}
好了,到这里我们已经改完了,其实还是蛮简单的,只是简单粗暴的修改了我们Item的显示内容,通过向外暴露方法用于控制开关。
/**
* 设置以..结尾
*
* @param ellipsizeEnd
*/
public void setEllipsizeEnd(boolean ellipsizeEnd) {
this.ellipsizeEnd = ellipsizeEnd;
}
3.优化
我们通过修改了Item上的内容完美的达成了我们的目标,但是,如果说我们Item上的内容有200个字符串,而我们只能用10个字符串,只是假如啊,那么我们的While循环是不是很耗时,首先我想到的就是第一次进来的时候我算好我每个字符的宽度,计算好我们可以显示多少个字符,直接一步到位截取。在写博客的时候又想到,直接在一步到位截取的时候加上..然后我们再对新的字符串进行处理,这样就很省时间了,逻辑也简单了很多
贴下最终的方法代码
/**
* 用于格式化显示的文字 以..结尾
* 在这里格式化了文字,在画的时候就会显示格式化后的文字 实现文字以..结尾显示
*
* @param text Item上的内容
* @return 可以以设置到Item上的内容
*/
private String formatContentText(String text) {
if (measuredWidth == 0) {
return text;
}
Rect rect = new Rect();
StringBuilder sb = new StringBuilder();
StringBuilder temp = new StringBuilder(text);
isFirstFormat = true;
while (true) {
paintCenterText.getTextBounds(temp.toString(), 0, temp.toString().length(), rect);
if (rect.width() > measuredWidth) {
if (temp.toString().endsWith("...")) {
sb = new StringBuilder();
sb.append(temp.toString().substring(0, temp.toString().length() - 4)).append("...");
temp = new StringBuilder(sb.toString());
} else {
sb = new StringBuilder();
int lastIndex = measuredWidth / (rect.width() / temp.toString().length());
if (lastIndex > temp.toString().length()) {
lastIndex = temp.toString().length();
}
sb.append(temp.toString().substring(0, lastIndex - 1)).append("...");
temp = new StringBuilder(sb.toString());
isFirstFormat = false;
}
} else {
return temp.toString();
}
}
}
以上。好了到这里这个过程基本上就结束了,自己测试是没问题的。
核心代码都放好了,大家自取就行了。贴2张图吧
再说个问题就是魅族Pro5上,如果说EditText的InputType是Number相关可能会出现第一个数字会消失一点点,给大家个解决办法
mEditText.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (v.getId() == R.id.tv_budget_sum){
v.setRight(right+20);
}
}
});
直接给它增加点宽度就好了。也没搞清楚是不是魅族系统的问题,只是解决了