- CompoundButton 源码分析
- LinearLayout 源码分析
- SearchView 源码解析
- LruCache 源码解析
- ViewDragHelper 源码解析
- BottomSheets 源码解析
- Media Player 源码分析
- NavigationView 源码解析
- Service 源码解析
- Binder 源码分析
- Android 应用 Preference 相关及源码浅析 SharePreferences 篇
- ScrollView 源码解析
- Handler 源码解析
- NestedScrollView 源码解析
- SQLiteOpenHelper/SQLiteDatabase/Cursor 源码解析
- Bundle 源码解析
- LocalBroadcastManager 源码解析
- Toast 源码解析
- TextInputLayout
- LayoutInflater 和 LayoutInflaterCompat 源码解析
- TextView 源码解析
- NestedScrolling 事件机制源码解析
- ViewGroup 源码解析
- StaticLayout 源码分析
- AtomicFile 源码解析
- AtomicFile 源码解析
- Spannable 源码分析
- Notification 之 Android 5.0 实现原理
- CoordinatorLayout 源码分析
- Scroller 源码解析
- SwipeRefreshLayout 源码分析
- FloatingActionButton 源码解析
- AsyncTask 源码分析
- TabLayout 源码解析
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
SwipeRefreshLayout extends ViewGroup
其实就是一个自定义的 ViewGroup ,结合我们自己平时自定义 ViewGroup 的步骤:
- 初始化变量
- onMeasure
- onLayout
- 处理交互 (
dispatchTouchEventonInterceptTouchEventonTouchEvent)
接下来就按照上面的步骤进行分析。
1. 初始化变量
SwipeRefreshLayout 内部有 2 个 View,一个 圆圈(mCircleView) ,一个内部可滚动的 View(mTarget) 。除了 View,还包含一个 OnRefreshListener 接口,当刷新动画被触发时回调。
/**
* Constructor that is called when inflating SwipeRefreshLayout from XML.
*
* @param context
* @param attrs
*/
public SwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// 系统默认的最小滚动距离
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
// 系统默认的动画时长
mMediumAnimationDuration = getResources().getInteger(
android.R.integer.config_mediumAnimTime);
setWillNotDraw(false);
mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
// 获取 xml 中定义的属性
final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
setEnabled(a.getBoolean(0, true));
a.recycle();
// 刷新的圆圈的大小,单位转换成 sp
final DisplayMetrics metrics = getResources().getDisplayMetrics();
mCircleWidth = (int) (CIRCLE_DIAMETER * metrics.density);
mCircleHeight = (int) (CIRCLE_DIAMETER * metrics.density);
// 创建刷新动画的圆圈
createProgressView();
ViewCompat.setChildrenDrawingOrderEnabled(this, true);
// the absolute offset has to take into account that the circle starts at an offset
mSpinnerFinalOffset = DEFAULT_CIRCLE_TARGET * metrics.density;
// 刷新动画的临界距离值
mTotalDragDistance = mSpinnerFinalOffset;
// 通过 NestedScrolling 机制来处理嵌套滚动
mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}创建刷新动画的圆圈
private void createProgressView() {
mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT, CIRCLE_DIAMETER/2);
mProgress = new MaterialProgressDrawable(getContext(), this);
mProgress.setBackgroundColor(CIRCLE_BG_LIGHT);
mCircleView.setImageDrawable(mProgress);
mCircleView.setVisibility(View.GONE);
addView(mCircleView);
}初始化的时候创建一个出来一个 View (下拉刷新的圆圈)。可以看出使用背景圆圈是 v4 包里提供的 CircleImageView 控件,中间的是 MaterialProgressDrawable 进度条。 另一个 View 是在 xml 中包含的可滚动视图。
2. onMeasure
onMeasure 确定子视图的大小。
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mTarget == null) {
// 确定内部要滚动的 View,如 RecycleView
ensureTarget();
}
if (mTarget == null) {
return;
}
// 测量子 View (mTarget)
mTarget.measure(MeasureSpec.makeMeasureSpec(
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));
// 测量刷新的圆圈 mCircleView
mCircleView.measure(MeasureSpec.makeMeasureSpec(mCircleWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mCircleHeight, MeasureSpec.EXACTLY));
if (!mUsingCustomStart && !mOriginalOffsetCalculated) {
mOriginalOffsetCalculated = true;
mCurrentTargetOffsetTop = mOriginalOffsetTop = -mCircleView.getMeasuredHeight();
}
// 计算 mCircleView 在 ViewGroup 中的索引
mCircleViewIndex = -1;
// Get the index of the circleview.
for (int index = 0; index < getChildCount(); index++) {
if (getChildAt(index) == mCircleView) {
mCircleViewIndex = index;
break;
}
}
}这个步骤确定了 mCircleView 和 SwipeRefreshLayout 的子视图的大小。
3. onLayout
onLayout 主要负责确定各个子视图的位置。
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// 获取 SwipeRefreshLayout 的宽高
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
if (getChildCount() == 0) {
return;
}
if (mTarget == null) {
ensureTarget();
}
if (mTarget == null) {
return;
}
// 考虑到给控件设置 padding,去除 padding 的距离
final View child = mTarget;
final int childLeft = getPaddingLeft();
final int childTop = getPaddingTop();
final int childWidth = width - getPaddingLeft() - getPaddingRight();
final int childHeight = height - getPaddingTop() - getPaddingBottom();
// 设置 mTarget 的位置
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
int circleWidth = mCircleView.getMeasuredWidth();
int circleHeight = mCircleView.getMeasuredHeight();
// 根据 mCurrentTargetOffsetTop 变量的值来设置 mCircleView 的位置
mCircleView.layout((width / 2 - circleWidth / 2), mCurrentTargetOffsetTop,
(width / 2 + circleWidth / 2), mCurrentTargetOffsetTop + circleHeight);
}在 onLayout 中放置了 mCircleView 的位置,注意 顶部位置是 mCurrentTargetOffsetTop ,mCurrentTargetOffsetTop 初始距离是 -mCircleView.getMeasuredHeight() ,所以是在 SwipeRefreshLayout 外。
经过以上几个步骤,SwipeRefreshLayout 创建了子视图,确定他们的大小、位置,现在所有视图可以显示在界面了。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论