Android 带你从源码的角度解析Scroller的滚动实现原理

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介:

今天给大家讲解的是Scroller类的滚动实现原理,可能很多朋友不太了解该类是用来干嘛的,但是研究Launcher的朋友应该对他很熟悉,Scroller类是滚动的一个封装类,可以实现View的平滑滚动效果,什么是实现View的平滑滚动效果呢,举个简单的例子,一个View从在我们指定的时间内从一个位置滚动到另外一个位置,我们利用Scroller类可以实现匀速滚动,可以先加速后减速,可以先减速后加速等等效果,而不是瞬间的移动的效果,所以Scroller可以帮我们实现很多滑动的效果。

在介绍Scroller类之前,我们先去了解View的scrollBy() 和scrollTo()方法的区别,在区分这两个方法的之前,我们要先理解View 里面的两个成员变量mScrollX, mScrollY,X轴方向的偏移量和Y轴方向的偏移量,这个是一个相对距离,相对的不是屏幕的原点,而是View的左边缘,举个通俗易懂的例子,一列火车从吉安到深圳,途中经过赣州,那么原点就是赣州,偏移量就是 负的吉安到赣州的距离,大家从getScrollX()方法中的注释中就能看出答案来

  1. /** 
  2.     * Return the scrolled left position of this view. This is the left edge of 
  3.     * the displayed part of your view. You do not need to draw any pixels 
  4.     * farther left, since those are outside of the frame of your view on 
  5.     * screen. 
  6.     * 
  7.     * @return The left edge of the displayed part of your view, in pixels. 
  8.     */  
  9.    public final int getScrollX() {  
  10.        return mScrollX;  
  11.    }  
现在我们知道了向右滑动 mScrollX就为负数,向左滑动mScrollX为正数,接下来我们先来看看 scrollTo()方法的源码
  1. /** 
  2.    * Set the scrolled position of your view. This will cause a call to 
  3.    * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.    * invalidated. 
  5.    * @param x the x position to scroll to 
  6.    * @param y the y position to scroll to 
  7.    */  
  8.   public void scrollTo(int x, int y) {  
  9.       if (mScrollX != x || mScrollY != y) {  
  10.           int oldX = mScrollX;  
  11.           int oldY = mScrollY;  
  12.           mScrollX = x;  
  13.           mScrollY = y;  
  14.           onScrollChanged(mScrollX, mScrollY, oldX, oldY);  
  15.           if (!awakenScrollBars()) {  
  16.               invalidate();  
  17.           }  
  18.       }  
  19.   }  
从该方法中我们可以看出,先判断传进来的(x, y)值是否和View的X, Y偏移量相等,如果不相等,就调用onScrollChanged()方法来通知界面发生改变,然后重绘界面,所以这样子就实现了移动效果啦, 现在我们知道了scrollTo()方法是滚动到(x, y)这个偏移量的点,他是相对于View的开始位置来滚动的。在看看scrollBy()这个方法的代码
  1. /** 
  2.     * Move the scrolled position of your view. This will cause a call to 
  3.     * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.     * invalidated. 
  5.     * @param x the amount of pixels to scroll by horizontally 
  6.     * @param y the amount of pixels to scroll by vertically 
  7.     */  
  8.    public void scrollBy(int x, int y) {  
  9.        scrollTo(mScrollX + x, mScrollY + y);  
  10.    }  

原来他里面调用了scrollTo()方法,那就好办了,他就是相对于View上一个位置根据(x, y)来进行滚动,可能大家脑海中对这两个方法还有点模糊,没关系,还是举个通俗的例子帮大家理解下,假如一个View,调用两次scrollTo(-10, 0),第一次向右滚动10,第二次就不滚动了,因为mScrollX和x相等了,当我们调用两次scrollBy(-10, 0),第一次向右滚动10,第二次再向右滚动10,他是相对View的上一个位置来滚动的。

对于scrollTo()和scrollBy()方法还有一点需要注意,这点也很重要,假如你给一个LinearLayout调用scrollTo()方法,并不是LinearLayout滚动,而是LinearLayout里面的内容进行滚动,比如你想对一个按钮进行滚动,直接用Button调用scrollTo()一定达不到你的需求,大家可以试一试,如果真要对某个按钮进行scrollTo()滚动的话,我们可以在Button外面包裹一层Layout,然后对Layout调用scrollTo()方法。


了解了scrollTo()和scrollBy()方法之后我们就了解下Scroller类了,先看其构造方法

  1. /** 
  2.  * Create a Scroller with the default duration and interpolator. 
  3.  */  
  4. public Scroller(Context context) {  
  5.     this(context, null);  
  6. }  
  7.   
  8. /** 
  9.  * Create a Scroller with the specified interpolator. If the interpolator is 
  10.  * null, the default (viscous) interpolator will be used. 
  11.  */  
  12. public Scroller(Context context, Interpolator interpolator) {  
  13.     mFinished = true;  
  14.     mInterpolator = interpolator;  
  15.     float ppi = context.getResources().getDisplayMetrics().density * 160.0f;  
  16.     mDeceleration = SensorManager.GRAVITY_EARTH   // g (m/s^2)  
  17.                   * 39.37f                        // inch/meter  
  18.                   * ppi                           // pixels per inch  
  19.                   * ViewConfiguration.getScrollFriction();  
  20. }  
只有两个构造方法,第一个只有一个Context参数,第二个构造方法中指定了Interpolator,什么Interpolator呢?中文意思插补器,了解Android动画的朋友都应该熟悉
Interpolator,他指定了动画的变化率,比如说匀速变化,先加速后减速,正弦变化等等,不同的Interpolator可以做出不同的效果出来,第一个使用默认的Interpolator(viscous) 


接下来我们就要在Scroller类里面找滚动的方法,我们从名字上面可以看出startScroll()应该是个滚动的方法,我们来看看其源码吧

  1. public void startScroll(int startX, int startY, int dx, int dy, int duration) {  
  2.     mMode = SCROLL_MODE;  
  3.     mFinished = false;  
  4.     mDuration = duration;  
  5.     mStartTime = AnimationUtils.currentAnimationTimeMillis();  
  6.     mStartX = startX;  
  7.     mStartY = startY;  
  8.     mFinalX = startX + dx;  
  9.     mFinalY = startY + dy;  
  10.     mDeltaX = dx;  
  11.     mDeltaY = dy;  
  12.     mDurationReciprocal = 1.0f / (float) mDuration;  
  13.     // This controls the viscous fluid effect (how much of it)  
  14.     mViscousFluidScale = 8.0f;  
  15.     // must be set to 1.0 (used in viscousFluid())  
  16.     mViscousFluidNormalize = 1.0f;  
  17.     mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);  
  18. }  
在这个方法中我们只看到了对一些滚动的基本设置动作,比如设置滚动模式,开始时间,持续时间等等,并没有任何对View的滚动操作,也许你正纳闷,不是滚动的方法干嘛还叫做startScroll(),稍安勿躁,既然叫开始滚动,那就是对滚动的滚动之前的基本设置咯。
  1. /** 
  2.  * Call this when you want to know the new location.  If it returns true, 
  3.  * the animation is not yet finished.  loc will be altered to provide the 
  4.  * new location. 
  5.  */   
  6. public boolean computeScrollOffset() {  
  7.     if (mFinished) {  
  8.         return false;  
  9.     }  
  10.   
  11.     int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);  
  12.   
  13.     if (timePassed < mDuration) {  
  14.         switch (mMode) {  
  15.         case SCROLL_MODE:  
  16.             float x = (float)timePassed * mDurationReciprocal;  
  17.   
  18.             if (mInterpolator == null)  
  19.                 x = viscousFluid(x);   
  20.             else  
  21.                 x = mInterpolator.getInterpolation(x);  
  22.   
  23.             mCurrX = mStartX + Math.round(x * mDeltaX);  
  24.             mCurrY = mStartY + Math.round(x * mDeltaY);  
  25.             break;  
  26.         case FLING_MODE:  
  27.             float timePassedSeconds = timePassed / 1000.0f;  
  28.             float distance = (mVelocity * timePassedSeconds)  
  29.                     - (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);  
  30.               
  31.             mCurrX = mStartX + Math.round(distance * mCoeffX);  
  32.             // Pin to mMinX <= mCurrX <= mMaxX  
  33.             mCurrX = Math.min(mCurrX, mMaxX);  
  34.             mCurrX = Math.max(mCurrX, mMinX);  
  35.               
  36.             mCurrY = mStartY + Math.round(distance * mCoeffY);  
  37.             // Pin to mMinY <= mCurrY <= mMaxY  
  38.             mCurrY = Math.min(mCurrY, mMaxY);  
  39.             mCurrY = Math.max(mCurrY, mMinY);  
  40.               
  41.             break;  
  42.         }  
  43.     }  
  44.     else {  
  45.         mCurrX = mFinalX;  
  46.         mCurrY = mFinalY;  
  47.         mFinished = true;  
  48.     }  
  49.     return true;  
  50. }  
我们在startScroll()方法的时候获取了当前的动画毫秒赋值给了mStartTime,在computeScrollOffset()中再一次调用AnimationUtils.currentAnimationTimeMillis()来获取动画
毫秒减去mStartTime就是持续时间了,然后进去if判断,如果动画持续时间小于我们设置的滚动持续时间mDuration,进去switch的SCROLL_MODE,然后根据Interpolator来计算出在该时间段里面移动的距离,赋值给mCurrX, mCurrY, 所以该方法的作用是,计算在0到mDuration时间段内滚动的偏移量,并且判断滚动是否结束,true代表还没结束,false则表示滚动介绍了,Scroller类的其他的方法我就不提了,大都是一些get(), set()方法。

看了这么多,到底要怎么才能触发滚动,你心里肯定有很多疑惑,在说滚动之前我要先提另外一个方法computeScroll(),该方法是滑动的控制方法,在绘制View时,会在draw()过程调用该方法。我们先看看computeScroll()的源码

  1. /** 
  2.     * Called by a parent to request that a child update its values for mScrollX 
  3.     * and mScrollY if necessary. This will typically be done if the child is 
  4.     * animating a scroll using a {@link android.widget.Scroller Scroller} 
  5.     * object. 
  6.     */  
  7.    public void computeScroll() {  
  8.    }  
没错,他是一个空的方法,需要子类去重写该方法来实现逻辑,到底该方法在哪里被触发呢。我们继续看看View的绘制方法draw()
  1. public void draw(Canvas canvas) {  
  2.        final int privateFlags = mPrivateFlags;  
  3.        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&  
  4.                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);  
  5.        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;  
  6.   
  7.        /* 
  8.         * Draw traversal performs several drawing steps which must be executed 
  9.         * in the appropriate order: 
  10.         * 
  11.         *      1. Draw the background 
  12.         *      2. If necessary, save the canvas' layers to prepare for fading 
  13.         *      3. Draw view's content 
  14.         *      4. Draw children 
  15.         *      5. If necessary, draw the fading edges and restore layers 
  16.         *      6. Draw decorations (scrollbars for instance) 
  17.         */  
  18.   
  19.        // Step 1, draw the background, if needed  
  20.        int saveCount;  
  21.   
  22.        if (!dirtyOpaque) {  
  23.            final Drawable background = mBackground;  
  24.            if (background != null) {  
  25.                final int scrollX = mScrollX;  
  26.                final int scrollY = mScrollY;  
  27.   
  28.                if (mBackgroundSizeChanged) {  
  29.                    background.setBounds(00,  mRight - mLeft, mBottom - mTop);  
  30.                    mBackgroundSizeChanged = false;  
  31.                }  
  32.   
  33.                if ((scrollX | scrollY) == 0) {  
  34.                    background.draw(canvas);  
  35.                } else {  
  36.                    canvas.translate(scrollX, scrollY);  
  37.                    background.draw(canvas);  
  38.                    canvas.translate(-scrollX, -scrollY);  
  39.                }  
  40.            }  
  41.        }  
  42.   
  43.        // skip step 2 & 5 if possible (common case)  
  44.        final int viewFlags = mViewFlags;  
  45.        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;  
  46.        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;  
  47.        if (!verticalEdges && !horizontalEdges) {  
  48.            // Step 3, draw the content  
  49.            if (!dirtyOpaque) onDraw(canvas);  
  50.   
  51.            // Step 4, draw the children  
  52.            dispatchDraw(canvas);  
  53.   
  54.            // Step 6, draw decorations (scrollbars)  
  55.            onDrawScrollBars(canvas);  
  56.   
  57.            // we're done...  
  58.            return;  
  59.        }  
  60.   
  61.        ......  
  62.        ......  
  63.        ......  

我们只截取了draw()的部分代码,这上面11-16行为我们写出了绘制一个View的几个步骤,我们看看第四步绘制孩子的时候会触发dispatchDraw()这个方法,来看看源码是什么内容

  1. /** 
  2.     * Called by draw to draw the child views. This may be overridden 
  3.     * by derived classes to gain control just before its children are drawn 
  4.     * (but after its own view has been drawn). 
  5.     * @param canvas the canvas on which to draw the view 
  6.     */  
  7.    protected void dispatchDraw(Canvas canvas) {  
  8.   
  9.    }  
好吧,又是定义的一个空方法,给子类来重写的方法,所以我们找到View的子类ViewGroup来看看该方法的具体实现逻辑
  1. @Override  
  2. protected void dispatchDraw(Canvas canvas) {  
  3.     final int count = mChildrenCount;  
  4.     final View[] children = mChildren;  
  5.     int flags = mGroupFlags;  
  6.   
  7.     if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {  
  8.         final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;  
  9.   
  10.         final boolean buildCache = !isHardwareAccelerated();  
  11.         for (int i = 0; i < count; i++) {  
  12.             final View child = children[i];  
  13.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {  
  14.                 final LayoutParams params = child.getLayoutParams();  
  15.                 attachLayoutAnimationParameters(child, params, i, count);  
  16.                 bindLayoutAnimation(child);  
  17.                 if (cache) {  
  18.                     child.setDrawingCacheEnabled(true);  
  19.                     if (buildCache) {                          
  20.                         child.buildDrawingCache(true);  
  21.                     }  
  22.                 }  
  23.             }  
  24.         }  
  25.   
  26.         final LayoutAnimationController controller = mLayoutAnimationController;  
  27.         if (controller.willOverlap()) {  
  28.             mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;  
  29.         }  
  30.   
  31.         controller.start();  
  32.   
  33.         mGroupFlags &= ~FLAG_RUN_ANIMATION;  
  34.         mGroupFlags &= ~FLAG_ANIMATION_DONE;  
  35.   
  36.         if (cache) {  
  37.             mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;  
  38.         }  
  39.   
  40.         if (mAnimationListener != null) {  
  41.             mAnimationListener.onAnimationStart(controller.getAnimation());  
  42.         }  
  43.     }  
  44.   
  45.     int saveCount = 0;  
  46.     final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;  
  47.     if (clipToPadding) {  
  48.         saveCount = canvas.save();  
  49.         canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,  
  50.                 mScrollX + mRight - mLeft - mPaddingRight,  
  51.                 mScrollY + mBottom - mTop - mPaddingBottom);  
  52.   
  53.     }  
  54.   
  55.     // We will draw our child's animation, let's reset the flag  
  56.     mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;  
  57.     mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;  
  58.   
  59.     boolean more = false;  
  60.     final long drawingTime = getDrawingTime();  
  61.   
  62.     if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {  
  63.         for (int i = 0; i < count; i++) {  
  64.             final View child = children[i];  
  65.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {  
  66.                 more |= drawChild(canvas, child, drawingTime);  
  67.             }  
  68.         }  
  69.     } else {  
  70.         for (int i = 0; i < count; i++) {  
  71.             final View child = children[getChildDrawingOrder(count, i)];  
  72.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {  
  73.                 more |= drawChild(canvas, child, drawingTime);  
  74.             }  
  75.         }  
  76.     }  
  77.   
  78.     // Draw any disappearing views that have animations  
  79.     if (mDisappearingChildren != null) {  
  80.         final ArrayList<View> disappearingChildren = mDisappearingChildren;  
  81.         final int disappearingCount = disappearingChildren.size() - 1;  
  82.         // Go backwards -- we may delete as animations finish  
  83.         for (int i = disappearingCount; i >= 0; i--) {  
  84.             final View child = disappearingChildren.get(i);  
  85.             more |= drawChild(canvas, child, drawingTime);  
  86.         }  
  87.     }  
  88.   
  89.     if (debugDraw()) {  
  90.         onDebugDraw(canvas);  
  91.     }  
  92.   
  93.     if (clipToPadding) {  
  94.         canvas.restoreToCount(saveCount);  
  95.     }  
  96.   
  97.     // mGroupFlags might have been updated by drawChild()  
  98.     flags = mGroupFlags;  
  99.   
  100.     if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {  
  101.         invalidate(true);  
  102.     }  
  103.   
  104.     if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&  
  105.             mLayoutAnimationController.isDone() && !more) {  
  106.         // We want to erase the drawing cache and notify the listener after the  
  107.         // next frame is drawn because one extra invalidate() is caused by  
  108.         // drawChild() after the animation is over  
  109.         mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;  
  110.         final Runnable end = new Runnable() {  
  111.            public void run() {  
  112.                notifyAnimationListener();  
  113.            }  
  114.         };  
  115.         post(end);  
  116.     }  
  117. }  
这个方法代码有点多,但是我们还是挑重点看吧,从65-79行可以看出 在dispatchDraw()里面会对ViewGroup里面的子View调用drawChild()来进行绘制,接下来我们来看看drawChild()方法的代码
  1.  protected boolean drawChild(Canvas canvas, View child, long drawingTime) {  
  2.     ......  
  3.     ......  
  4.   
  5.     if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&  
  6.                 (child.mPrivateFlags & DRAW_ANIMATION) == 0) {  
  7.             return more;  
  8.         }  
  9.   
  10.         child.computeScroll();  
  11.   
  12.         final int sx = child.mScrollX;  
  13.         final int sy = child.mScrollY;  
  14.   
  15.         boolean scalingRequired = false;  
  16.         Bitmap cache = null;  
  17.   
  18.     ......  
  19.     ......  
  20.   
  21. }  
只截取了部分代码,看到child.computeScroll()你大概明白什么了吧,转了老半天终于找到了computeScroll()方法被触发,就是ViewGroup在分发绘制自己的孩子的时候,会对其子View调用computeScroll()方法


整理下思路,来看看View滚动的实现原理,我们先调用Scroller的startScroll()方法来进行一些滚动的初始化设置,然后迫使View进行绘制,我们调用View的invalidate()或postInvalidate()就可以重新绘制View,绘制View的时候会触发computeScroll()方法,我们重写computeScroll(),在computeScroll()里面先调用Scroller的computeScrollOffset()方法来判断滚动有没有结束,如果滚动没有结束我们就调用scrollTo()方法来进行滚动,该scrollTo()方法虽然会重新绘制View,但是我们还是要手动调用下invalidate()或者postInvalidate()来触发界面重绘,重新绘制View又触发computeScroll(),所以就进入一个循环阶段,这样子就实现了在某个时间段里面滚动某段距离的一个平滑的滚动效果
也许有人会问,干嘛还要调用来调用去最后在调用scrollTo()方法,还不如直接调用scrollTo()方法来实现滚动,其实直接调用是可以,只不过scrollTo()是瞬间滚动的,给人的用户体验不太好,所以Android提供了Scroller类实现平滑滚动的效果。为了方面大家理解,我画了一个简单的调用示意图


好了,讲到这里就已经讲完了Scroller类的滚动实现原理啦,不知道大家理解了没有,Scroller能实现很多滚动的效果,由于考虑到这篇文章的篇幅有点长,所以这篇文章就不带领大家来使用Scroller类了
目录
相关文章
|
2月前
|
存储 缓存 算法
HashMap深度解析:从原理到实战
HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
113 14
|
6天前
|
机器学习/深度学习 算法 数据挖掘
解析静态代理IP改善游戏体验的原理
静态代理IP通过提高网络稳定性和降低延迟,优化游戏体验。具体表现在加快游戏网络速度、实时玩家数据分析、优化游戏设计、简化更新流程、维护网络稳定性、提高连接可靠性、支持地区特性及提升访问速度等方面,确保更流畅、高效的游戏体验。
53 22
解析静态代理IP改善游戏体验的原理
|
4天前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
48 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
1月前
|
机器学习/深度学习 自然语言处理 搜索推荐
自注意力机制全解析:从原理到计算细节,一文尽览!
自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。
|
2月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2月前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
2月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2月前
|
存储 物联网 大数据
探索阿里云 Flink 物化表:原理、优势与应用场景全解析
阿里云Flink的物化表是流批一体化平台中的关键特性,支持低延迟实时更新、灵活查询性能、无缝流批处理和高容错性。它广泛应用于电商、物联网和金融等领域,助力企业高效处理实时数据,提升业务决策能力。实践案例表明,物化表显著提高了交易欺诈损失率的控制和信贷审批效率,推动企业在数字化转型中取得竞争优势。
120 16
|
1月前
|
自然语言处理 数据处理 索引
mindspeed-llm源码解析(一)preprocess_data
mindspeed-llm是昇腾模型套件代码仓,原来叫"modelLink"。这篇文章带大家阅读一下数据处理脚本preprocess_data.py(基于1.0.0分支),数据处理是模型训练的第一步,经常会用到。
52 0
|
2月前
|
网络协议 安全 网络安全
探索网络模型与协议:从OSI到HTTPs的原理解析
OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
174 3

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 6
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 7
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 8
    DeepSeek技术报告解析:为什么DeepSeek-R1 可以用低成本训练出高效的模型
  • 9
    深度解析淘宝商品详情API接口:解锁电商数据新维度,驱动业务增长
  • 10
    基于OS Copilot 的深度解析测评
  • 推荐镜像

    更多