- 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
- 处理交互 (
dispatchTouchEvent
onInterceptTouchEvent
onTouchEvent
)
接下来就按照上面的步骤进行分析。
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 创建了子视图,确定他们的大小、位置,现在所有视图可以显示在界面了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论