超级扩展的textview(让我们开启自定义之旅吧)
自定义已经家喻户晓了,自定义的实现有好几种,继承现有的控件,几种控件组合,继承view,其中继承view算是最难的啦,建议开始大家先继承现有的控件,其实自定义你完全理解了可以说so easy,但是对于刚起步的同学,感觉难是因为你们没去尝试,给自己定个小目标,每周写个自定义,自定义需要自己慢慢积累的,着急是来不了的,后期你会发现自定义,也就是位置的计算,这就考验大家的高中数学知识了,和大学里面的学的矩阵之类的。没好好学习是不是现在感觉后悔啦。。。。。。开玩笑一切可以弥补的啦.
现在看下效果图,类似朋友圈内容的查看全部。
展开前的
展开后的
喜欢或者需要的朋友可以继续往下面看———–
在项目res/values/下新建attrs.xml(当然其它名称也是可以的),在其中声明相关属性如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SuperExpandableTextViewAttr">
<!-- 默认情况下需要显示的最大行数 -->
<attr name="maxExpandLines" format="integer"></attr>
<!-- 一定距离下,动画的时候 -->
<attr name="duration" format="integer"></attr>
</declare-styleable>
</resources>
其中,attar的格式即单位有:dimension(尺寸)、boolean(布尔)、color(颜色)、enum(枚举)、flag(位或)、float(浮点)、fraction(百分比)、integer(整型)、reference(资源引用)、string(字符串)。
1.首先需要我们自定义下属性,但是属性在xml 运用的时候一定加入xmlns:app=”http://schemas.android.com/apk/res-auto要不然识别不了我们的属性,
2然后需要在我们自定义view 里面实现这个构造
private void init(Context context, AttributeSet attrs) {
setOrientation(VERTICAL);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SuperExpandableTextViewAttr);
maxExpandLines = array.getInteger(R.styleable.SuperExpandableTextViewAttr_maxExpandLines, 3);
duration = array.getInteger(R.styleable.SuperExpandableTextViewAttr_duration, 500);
array.recycle();
}
注意:TypedArray对象是一个共享资源,使用后必须调用recycle()回收。目的是为了缓存,不需要每次调用TypedArray的时候都重新分配内存,方便了其它地方的复用。
3.此次我们自定义在LinearLayout 的基础上定义的
public class SuperEpandableTextView extends LinearLayout
4.我们需要重写onFinishInflate 此方法, 在这这个方法里面进行找控件的操作,为什么在这里面进行呢,这个方法的作用是//当加载完成xml后,就会执行那个方法。
@Override
protected void onFinishInflate() {
super.onFinishInflate();
id_source_textview = (TextView) findViewById(R.id.id_source_textview);
id_expand_textview = (TextView) findViewById(R.id.id_expand_textview);
id_expand_textview.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
SuperAnimation animation;
isCollapsed=!isCollapsed;
if (isCollapsed) {
id_expand_textview.setText("查看更多");
if (listener!=null) {
listener.onExpandStateChanged(true);
}
animation=new SuperAnimation(getHeight(), collapsedHeight);
}
else {
id_expand_textview.setText("收起");
if (listener!=null) {
listener.onExpandStateChanged(false);
}
animation=new SuperAnimation(getHeight(), realTextViewHeigt+lastHeight);
}
//只是将view移动到了目标位置,但是view绑定的点击事件还在原来位置,导致点击时会先闪一下
animation.setFillAfter(true);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
isAnimate=true;
}
@Override
public void onAnimationEnd(Animation animation) {
clearAnimation();
isAnimate=false;
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
clearAnimation();
startAnimation(animation);
}
});
5.我们还需要重写测量的方法onMeasure ,在此处进行对控件进行操作,显示隐藏等
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//如果隐藏控件或者textview的值没有发生改变,那么不进行测量
if (getVisibility()==GONE || !isChange) {
return;
}
isChange=false;
//初始化默认状态,即正常显示文本
id_expand_textview.setVisibility(GONE);
id_source_textview.setMaxLines(Integer.MAX_VALUE);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//如果本身没有达到收起展开的限定要求,则不进行处理
if (id_source_textview.getLineCount()<=maxExpandLines) {
return;
}
//初始化高度赋值,为后续动画事件准备数据
realTextViewHeigt=getRealTextViewHeight(id_source_textview);
//如果处于收缩状态,则设置最多显示行数
if (isCollapsed) {
id_source_textview.setLines(maxExpandLines);
}
id_expand_textview.setVisibility(VISIBLE);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (isCollapsed) {
id_source_textview.post(new Runnable() {
@Override
public void run() {
lastHeight=getHeight()-id_source_textview.getHeight();
collapsedHeight=getMeasuredHeight();
}
});
}
}
解释下上面代码两个地方,post(new Runnable() {}为什么要post出去,这样才能布局绘制完成,测量的值才是正确,getMeasuredHeight()是实际View的大小,与屏幕无关,而getHeight的大小此时则是屏幕的大小
6大家看见了里面我用到了Animation这是为了增强用户的体验,Animation的实现很简单这里用到了估值器, 解释下, 比如view 移动的距离是10,需要的时间5秒 估值器会计算每一秒需要移动的距离。
/**
* 此处用到了估值器
*/
public int getValue(int startValue, int endValue, float Time) {
int value = (int) ((endValue - startValue) * Time + startValue);
return value;
}
private class SuperAnimation extends Animation {
int startValue = 0;
int endValue = 0;
public SuperAnimation(int startValue, int endValue) {
setDuration(duration);
this.startValue = startValue;
this.endValue = endValue;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
int height = getValue(startValue, endValue, interpolatedTime);
id_source_textview.setMaxHeight(height - lastHeight);
SuperEpandableTextView.this.getLayoutParams().height = height;
SuperEpandableTextView.this.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
}
核心代码解释完了,需要完整代码我可以上传github。
参考文献:http://www.shorr.cn/2016/11/25/Android自定义View(一):基础篇/