最近公司要求要出一个暂开内容,然后还能收缩内容的控件
其实一开始我想用ExpendListView,但是后面寻思着用了自定义View,毕竟现在自己也在学这一块
0x02 效果

0x03 代码
一、SubExpendLayout代码
SubExpendLayout 这个是上面那个view,上面那个view实际上是叠加了一个textView,然后进行的,因为需要要求第一个textView收缩之后要点点,所以加上了singleLine =true

根据分析

对应的expend()和close()代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public void expend() { tvContentClose.setVisibility(INVISIBLE); ivExpend.setVisibility(INVISIBLE);
tvContentExpend.setVisibility(VISIBLE); ivClose.setVisibility(VISIBLE); }
public void close() { tvContentClose.setVisibility(VISIBLE); ivExpend.setVisibility(VISIBLE);
tvContentExpend.setVisibility(INVISIBLE); ivClose.setVisibility(INVISIBLE); }
|
对应的测量代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = 0; int height = 0;
switch (widthMode) { case MeasureSpec.UNSPECIFIED: case MeasureSpec.AT_MOST: { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); int measuredWidth = view.getMeasuredWidth(); width = Math.max(widthSize, measuredWidth); } } break; default: { width = widthSize; } break; }
switch (heightMode) { case MeasureSpec.UNSPECIFIED: case MeasureSpec.AT_MOST: { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i);
if (view.getVisibility() != GONE) { int measuredHeight = view.getMeasuredHeight(); if (i > 0) { height += measuredHeight; } } } } break; default: { height = heightSize; } break; }
Log.e(TAG, "onMeasure: " + height); setMeasuredDimension(width, height);
}
|
对应的onLayout的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {
int tempHeight = 0; for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); if (view.getVisibility() == GONE) { continue; } int measuredHeight = view.getMeasuredHeight(); view.layout(0, tempHeight, view.getMeasuredWidth(), measuredHeight + tempHeight); if (i > 0) { tempHeight += measuredHeight; } else { closeHeight = measuredHeight; } }
if (expendHeight == 0 || reLayout) { expendHeight = getMeasuredHeight();
midHeight = (closeHeight + expendHeight) / 2;
reLayout = false; } }
|
最后可以来一个触摸事件的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_UP: if (isExpend) { closeLayout();
} else { expendLayout();
} break; default: break; }
return true; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public void expendLayout() { ValueAnimator animator = ValueAnimator.ofInt(closeHeight, expendHeight); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int changeHeight = (int) animation.getAnimatedValue(); LayoutParams layoutParams = getLayoutParams(); layoutParams.height = changeHeight; setLayoutParams(layoutParams);
if (changeHeight >= midHeight) { expend(); } } }); animator.setDuration(500); animator.start();
isExpend = true; }
public void closeLayout() { ValueAnimator animator = ValueAnimator.ofInt(expendHeight, closeHeight); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int changeHeight = (int) animation.getAnimatedValue(); LayoutParams layoutParams = getLayoutParams(); layoutParams.height = changeHeight; setLayoutParams(layoutParams);
if (changeHeight <= midHeight) { close(); } } }); animator.setDuration(500); animator.start();
isExpend = false; }
|
具体的动画实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public void expendLayout() { ValueAnimator animator = ValueAnimator.ofInt(closeHeight, expendHeight); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int changeHeight = (int) animation.getAnimatedValue(); LayoutParams layoutParams = getLayoutParams(); layoutParams.height = changeHeight; setLayoutParams(layoutParams);
if (changeHeight >= midHeight) { expend(); } } }); animator.setDuration(500); animator.start();
isExpend = true; }
public void closeLayout() { ValueAnimator animator = ValueAnimator.ofInt(expendHeight, closeHeight); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int changeHeight = (int) animation.getAnimatedValue(); LayoutParams layoutParams = getLayoutParams(); layoutParams.height = changeHeight; setLayoutParams(layoutParams);
if (changeHeight <= midHeight) { close(); } } }); animator.setDuration(500); animator.start();
isExpend = false; }
|
二、RecordExpendLayout代码
分别设置了2个类,SubmissionExpendAdapter是个实现类

RecordExpendLayout的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public RecordExpendLayout(Context context, AttributeSet attrs) { super(context, attrs);
init(); }
private void init() {
setBackgroundColor(getResources().getColor(R.color.background));
View titleView = LayoutInflater.from(getContext()).inflate(R.layout.view_record_expend_title, this, false); cardItemViews.add(titleView); addView(titleView);
ivCtrolStatus = (ImageView) findViewById(R.id.iv_ctrol_status);
setStatus(Status.EXPEND); }
|
进行对应的测量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); measureChildWithMargins(view, widthMeasureSpec, 0, heightMeasureSpec, 0); }
int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = 0; int height = 0;
switch (widthMode) { case MeasureSpec.UNSPECIFIED: case MeasureSpec.AT_MOST: { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); int measuredWidth = view.getMeasuredWidth(); width = Math.max(widthSize, measuredWidth); } } break; default: { width = widthSize; } break; }
switch (heightMode) { case MeasureSpec.UNSPECIFIED: case MeasureSpec.AT_MOST: { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
if (view.getVisibility() != GONE) { int measuredHeight = view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; height += measuredHeight; } } } break; default: { height = heightSize; } break; }
Log.e(TAG, "onMeasure: " + height);
if (expendHeight == 0 || reLayout) { expendHeight = getMeasuredHeight(); } setMeasuredDimension(width, height);
}
|
进行onLayout
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {
int tempHeight = 0; for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); if (view.getVisibility() == GONE) { continue; } int measuredHeight = view.getMeasuredHeight(); MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
view.layout(lp.leftMargin, tempHeight + lp.topMargin, view.getMeasuredWidth() + lp.rightMargin, measuredHeight + tempHeight + lp.topMargin - lp.bottomMargin);
tempHeight += measuredHeight + lp.topMargin + lp.bottomMargin; if (i == 0) { closeHeight = measuredHeight; } }
if (expendHeight == 0 || reLayout) { expendHeight = getMeasuredHeight();
midHeight = (closeHeight + expendHeight) / 2;
reLayout = false;
} Log.e(TAG, "onLayout: expendHeight: " + expendHeight + " " + getMeasuredHeight()); }
|
剩下的就是ExpendAdapter 接口的设置了
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface ExpendAdapter {
int getItemCount();
void getView(int position, View view, RecordExpendLayout parent); }
|
RecordExpendLayout和adapter相关的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); }
private ExpendAdapter mAdapter;
public void setAdapter(ExpendAdapter adapter) { mAdapter = adapter;
notifyAllData(); }
private void handleData() { int itemCount = mAdapter.getItemCount(); for (int i = 0; i < itemCount; i++) { View view = LayoutInflater.from(getContext()).inflate(R.layout.view_record_expend_item, this, false); cardItemViews.add(view); addView(view);
mAdapter.getView(i, view, this);
} }
public void notifyAllData() { if (mAdapter != null) { handleData(); }
Log.e(TAG, "notifyAllData: "); reLayout = true; requestLayout(); }
|
以上就是上周觉得比较有意思而又比较重要的代码