zoukankan      html  css  js  c++  java
  • 安卓高手之路之图形系统(6)ListView继续

    安卓高手之路之图形系统(6)ListView继续 - 修补C++ - ITeye技术网站

    综述:

    本篇首先介绍了ListView的实现细节。然后介绍了Gallery,ListView,ViewPager的效率对比分析。以及效率低下的原因。最后给出了一些解决方案。

    1.在上一篇讨论了requestLayout的效率问题。对如何避免这个问题也进行了深入探讨。本篇就内存问题进行讨论。一般情况下,安卓的ListView实现方式上,就存在要移动childView位置的需求。

    如果对childView进行了回收并且回收的childView必须仍然在原来的位置上,那么childView的位置可能要出现在两个位置上。这非常有挑战性,因为安卓的很多东西都是基于一个View一个位置的这样的思想。现在突然出现两个位置。那么也就是说,回收的View不能再呆在原来的位置了。必须被remove掉。remove掉之后呢,其他的View必须挤过去。总之所有的children都得改变位置。

              也就是说必须改变布局。但是改变布局是否就一定要reqestLayout,也是个问题。刚才说了不需要。只要调用一下onLayout就行了。onLayout调用的时候,必须知道childView的位置吧。、那还得改变child的位置,那又调用什么呢?

                那么,仅仅改变childView位置的函数是什么呢?这个函数就是offset系列函数。在Gallery那边是offsetChildrenLeftAndRight另外还有个setX和setY。这些都是改变位置的函数。

             ListView的实现时非常高效的。既保证了回收childiew,有能不进行layout。非常高效。

    第一。View的回收机制。

    第二。View不进行requestLayout

        

    2.Gallery的实现方式

             Gallery在实现的时候没有采用回收机制。经过测试的Adapter.getView方法参数,View都是null。也就是说不对View进行复用。其实上Gallery中的View是越来越多。而且每一个View都不会进行回收。这跟一个

    ScrollView+ViewPager的实现方式是一样的。唯一的区别是Gallery采用了Adapter机制,并且使用了ListView的滚动原理。但是Gallery没有对View进行回收,全部保存了起来。在内存不够用的时候,尽量不要使用这个。下面拿Gallery和ListView的回收机制进行了对比,

    先看trackMotionScroll方法:

        Gallery:

    Java代码  收藏代码
    1. /** 
    2.  * Tracks a motion scroll. In reality, this is used to do just about any 
    3.  * movement to items (touch scroll, arrow-key scroll, set an item as selected). 
    4.  *  
    5.  * @param deltaX Change in X from the previous event. 
    6.  */  
    7. void trackMotionScroll(int deltaX) {  
    8.   
    9.     if (getChildCount() == 0) {  
    10.         return;  
    11.     }  
    12.       
    13.     boolean toLeft = deltaX < 0;   
    14.       
    15.     int limitedDeltaX = getLimitedMotionScrollAmount(toLeft, deltaX);  
    16.     if (limitedDeltaX != deltaX) {  
    17.         // The above call returned a limited amount, so stop any scrolls/flings  
    18.         mFlingRunnable.endFling(false);  
    19.         onFinishedMovement();  
    20.     }  
    21.       
    22.     offsetChildrenLeftAndRight(limitedDeltaX);  
    23.       
    24.     detachOffScreenChildren(toLeft);  
    25.       
    26.     if (toLeft) {  
    27.         // If moved left, there will be empty space on the right  
    28.         fillToGalleryRight();  
    29.     } else {  
    30.         // Similarly, empty space on the left  
    31.         fillToGalleryLeft();  
    32.     }  
    33.       
    34.     // Clear unused views  
    35.     mRecycler.clear();  
    36.       
    37.     setSelectionToCenterChild();  
    38.   
    39.     onScrollChanged(0000); // dummy values, View's implementation does not use these.  
    40.   
    41.     invalidate();  
    42. }  
        /**
         * Tracks a motion scroll. In reality, this is used to do just about any
         * movement to items (touch scroll, arrow-key scroll, set an item as selected).
         *
         * @param deltaX Change in X from the previous event.
         */
        void trackMotionScroll(int deltaX) {
    
            if (getChildCount() == 0) {
                return;
            }
    
            boolean toLeft = deltaX < 0; 
    
            int limitedDeltaX = getLimitedMotionScrollAmount(toLeft, deltaX);
            if (limitedDeltaX != deltaX) {
                // The above call returned a limited amount, so stop any scrolls/flings
                mFlingRunnable.endFling(false);
                onFinishedMovement();
            }
    
            offsetChildrenLeftAndRight(limitedDeltaX);
    
            detachOffScreenChildren(toLeft);
    
            if (toLeft) {
                // If moved left, there will be empty space on the right
                fillToGalleryRight();
            } else {
                // Similarly, empty space on the left
                fillToGalleryLeft();
            }
    
            // Clear unused views
            mRecycler.clear();
    
            setSelectionToCenterChild();
    
            onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
    
            invalidate();
        }

     ListView

      

    Java代码  收藏代码
    1. /** 
    2.   * Track a motion scroll 
    3.   * 
    4.   * @param deltaY Amount to offset mMotionView. This is the accumulated delta since the motion 
    5.   *        began. Positive numbers mean the user's finger is moving down the screen. 
    6.   * @param incrementalDeltaY Change in deltaY from the previous event. 
    7.   * @return true if we're already at the beginning/end of the list and have nothing to do. 
    8.   */  
    9.  boolean trackMotionScroll(int deltaY, int incrementalDeltaY) {  
    10.      final int childCount = getChildCount();  
    11.      if (childCount == 0) {  
    12.          return true;  
    13.      }  
    14.   
    15.      final int firstTop = getChildAt(0).getTop();  
    16.      final int lastBottom = getChildAt(childCount - 1).getBottom();  
    17.   
    18.      final Rect listPadding = mListPadding;  
    19.   
    20.      // "effective padding" In this case is the amount of padding that affects  
    21.      // how much space should not be filled by items. If we don't clip to padding  
    22.      // there is no effective padding.  
    23.      int effectivePaddingTop = 0;  
    24.      int effectivePaddingBottom = 0;  
    25.      if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {  
    26.          effectivePaddingTop = listPadding.top;  
    27.          effectivePaddingBottom = listPadding.bottom;  
    28.      }  
    29.   
    30.       // FIXME account for grid vertical spacing too?  
    31.      final int spaceAbove = effectivePaddingTop - firstTop;  
    32.      final int end = getHeight() - effectivePaddingBottom;  
    33.      final int spaceBelow = lastBottom - end;  
    34.   
    35.      final int height = getHeight() - mPaddingBottom - mPaddingTop;  
    36.      if (deltaY < 0) {  
    37.          deltaY = Math.max(-(height - 1), deltaY);  
    38.      } else {  
    39.          deltaY = Math.min(height - 1, deltaY);  
    40.      }  
    41.   
    42.      if (incrementalDeltaY < 0) {  
    43.          incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);  
    44.      } else {  
    45.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    = Math.min(height - 1, incrementalDeltaY);  
    46.      }  
    47.   
    48.      final int firstPosition = mFirstPosition;  
    49.   
    50.      // Update our guesses for where the first and last views are  
    51.      if (firstPosition == 0) {  
    52.          mFirstPositionDistanceGuess = firstTop - listPadding.top;  
    53.      } else {  
    54.          mFirstPositionDistanceGuess += incrementalDeltaY;  
    55.      }  
    56.      if (firstPosition + childCount == mItemCount) {  
    57.          mLastPositionDistanceGuess = lastBottom + listPadding.bottom;  
    58.      } else {  
    59.          mLastPositionDistanceGuess += incrementalDeltaY;  
    60.      }  
    61.   
    62.      final boolean cannotScrollDown = (firstPosition == 0 &&  
    63.              firstTop >= listPadding.top && incrementalDeltaY >= 0);  
    64.      final boolean cannotScrollUp = (firstPosition + childCount == mItemCount &&  
    65.              lastBottom <= getHeight() - listPadding.bottom && incrementalDeltaY <= 0);  
    66.   
    67.      if (cannotScrollDown || cannotScrollUp) {  
    68.          return incrementalDeltaY != 0;  
    69.      }  
    70.   
    71.      final boolean down = incrementalDeltaY < 0;  
    72.   
    73.      final boolean inTouchMode = isInTouchMode();  
    74.      if (inTouchMode) {  
    75.          hideSelector();  
    76.      }  
    77.   
    78.      final int headerViewsCount = getHeaderViewsCount();  
    79.      final int footerViewsStart = mItemCount - getFooterViewsCount();  
    80.   
    81.      int start = 0;  
    82.      int count = 0;  
    83.   
    84.      if (down) {  
    85.          int top = -incrementalDeltaY;  
    86.          if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {  
    87.              top += listPadding.top;  
    88.          }  
    89.          for (int i = 0; i < childCount; i++) {  
    90.              final View child = getChildAt(i);  
    91.              if (child.getBottom() >= top) {  
    92.                  break;  
    93.              } else {  
    94.                  count++;  
    95.                  int position = firstPosition + i;  
    96.                  if (position >= headerViewsCount && position < footerViewsStart) {  
    97.                      mRecycler.addScrapView(child, position);  
    98.   
    99.                      if (ViewDebug.TRACE_RECYCLER) {  
    100.                          ViewDebug.trace(child,  
    101.                                  ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,  
    102.                                  firstPosition + i, -1);  
    103.                      }  
    104.                  }  
    105.              }  
    106.          }  
    107.      } else {  
    108.          int bottom = getHeight() - incrementalDeltaY;  
    109.          if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {  
    110.              bottom -= listPadding.bottom;  
    111.          }  
    112.          for (int i = childCount - 1; i >= 0; i--) {  
    113.              final View child = getChildAt(i);  
    114.              if (child.getTop() <= bottom) {  
    115.                  break;  
    116.              } else {  
    117.                  start = i;  
    118.                  count++;  
    119.                  int position = firstPosition + i;  
    120.                  if (position >= headerViewsCount && position < footerViewsStart) {  
    121.                      mRecycler.addScrapView(child, position);  
    122.   
    123.                      if (ViewDebug.TRACE_RECYCLER) {  
    124.                          ViewDebug.trace(child,  
    125.                                  ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,  
    126.                                  firstPosition + i, -1);  
    127.                      }  
    128.                  }  
    129.              }  
    130.          }  
    131.      }  
    132.   
    133.      mMotionViewNewTop = mMotionViewOriginalTop + deltaY;  
    134.   
    135.      mBlockLayoutRequests = true;  
    136.   
    137.      if (count > 0) {  
    138.          detachViewsFromParent(start, count);  
    139.      }  
    140.      offsetChildrenTopAndBottom(incrementalDeltaY);  
    141.   
    142.      if (down) {  
    143.          mFirstPosition += count;  
    144.      }  
    145.   
    146.      invalidate();  
    147.   
    148.      final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);  
    149.      if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {  
    150.          fillGap(down);  
    151.      }  
    152.   
    153.      if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {  
    154.          final int childIndex = mSelectedPosition - mFirstPosition;  
    155.          if (childIndex >= 0 && childIndex < getChildCount()) {  
    156.              positionSelector(mSelectedPosition, getChildAt(childIndex));  
    157.          }  
    158.      } else if (mSelectorPosition != INVALID_POSITION) {  
    159.          final int childIndex = mSelectorPosition - mFirstPosition;  
    160.          if (childIndex >= 0 && childIndex < getChildCount()) {  
    161.              positionSelector(INVALID_POSITION, getChildAt(childIndex));  
    162.          }  
    163.      } else {  
    164.          mSelectorRect.setEmpty();  
    165.      }  
    166.   
    167.      mBlockLayoutRequests = false;  
    168.   
    169.      invokeOnItemScrollListener();  
    170.      awakenScrollBars();  
    171.   
    172.      return false;  
    173.  }  
       /**
         * Track a motion scroll
         *
         * @param deltaY Amount to offset mMotionView. This is the accumulated delta since the motion
         *        began. Positive numbers mean the user's finger is moving down the screen.
         * @param incrementalDeltaY Change in deltaY from the previous event.
         * @return true if we're already at the beginning/end of the list and have nothing to do.
         */
        boolean trackMotionScroll(int deltaY, int incrementalDeltaY) {
            final int childCount = getChildCount();
            if (childCount == 0) {
                return true;
            }
    
            final int firstTop = getChildAt(0).getTop();
            final int lastBottom = getChildAt(childCount - 1).getBottom();
    
            final Rect listPadding = mListPadding;
    
            // "effective padding" In this case is the amount of padding that affects
            // how much space should not be filled by items. If we don't clip to padding
            // there is no effective padding.
            int effectivePaddingTop = 0;
            int effectivePaddingBottom = 0;
            if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
                effectivePaddingTop = listPadding.top;
                effectivePaddingBottom = listPadding.bottom;
            }
    
             // FIXME account for grid vertical spacing too?
            final int spaceAbove = effectivePaddingTop - firstTop;
            final int end = getHeight() - effectivePaddingBottom;
            final int spaceBelow = lastBottom - end;
    
            final int height = getHeight() - mPaddingBottom - mPaddingTop;
            if (deltaY < 0) {
                deltaY = Math.max(-(height - 1), deltaY);
            } else {
                deltaY = Math.min(height - 1, deltaY);
            }
    
            if (incrementalDeltaY < 0) {
                incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);
            } else {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          = Math.min(height - 1, incrementalDeltaY);
            }
    
            final int firstPosition = mFirstPosition;
    
            // Update our guesses for where the first and last views are
            if (firstPosition == 0) {
                mFirstPositionDistanceGuess = firstTop - listPadding.top;
            } else {
                mFirstPositionDistanceGuess += incrementalDeltaY;
            }
            if (firstPosition + childCount == mItemCount) {
                mLastPositionDistanceGuess = lastBottom + listPadding.bottom;
            } else {
                mLastPositionDistanceGuess += incrementalDeltaY;
            }
    
            final boolean cannotScrollDown = (firstPosition == 0 &&
                    firstTop >= listPadding.top && incrementalDeltaY >= 0);
            final boolean cannotScrollUp = (firstPosition + childCount == mItemCount &&
                    lastBottom <= getHeight() - listPadding.bottom && incrementalDeltaY <= 0);
    
            if (cannotScrollDown || cannotScrollUp) {
                return incrementalDeltaY != 0;
            }
    
            final boolean down = incrementalDeltaY < 0;
    
            final boolean inTouchMode = isInTouchMode();
            if (inTouchMode) {
                hideSelector();
            }
    
            final int headerViewsCount = getHeaderViewsCount();
            final int footerViewsStart = mItemCount - getFooterViewsCount();
    
            int start = 0;
            int count = 0;
    
            if (down) {
                int top = -incrementalDeltaY;
                if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
                    top += listPadding.top;
                }
                for (int i = 0; i < childCount; i++) {
                    final View child = getChildAt(i);
                    if (child.getBottom() >= top) {
                        break;
                    } else {
                        count++;
                        int position = firstPosition + i;
                        if (position >= headerViewsCount && position < footerViewsStart) {
                            mRecycler.addScrapView(child, position);
    
                            if (ViewDebug.TRACE_RECYCLER) {
                                ViewDebug.trace(child,
                                        ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
                                        firstPosition + i, -1);
                            }
                        }
                    }
                }
            } else {
                int bottom = getHeight() - incrementalDeltaY;
                if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
                    bottom -= listPadding.bottom;
                }
                for (int i = childCount - 1; i >= 0; i--) {
                    final View child = getChildAt(i);
                    if (child.getTop() <= bottom) {
                        break;
                    } else {
                        start = i;
                        count++;
                        int position = firstPosition + i;
                        if (position >= headerViewsCount && position < footerViewsStart) {
                            mRecycler.addScrapView(child, position);
    
                            if (ViewDebug.TRACE_RECYCLER) {
                                ViewDebug.trace(child,
                                        ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
                                        firstPosition + i, -1);
                            }
                        }
                    }
                }
            }
    
            mMotionViewNewTop = mMotionViewOriginalTop + deltaY;
    
            mBlockLayoutRequests = true;
    
            if (count > 0) {
                detachViewsFromParent(start, count);
            }
            offsetChildrenTopAndBottom(incrementalDeltaY);
    
            if (down) {
                mFirstPosition += count;
            }
    
            invalidate();
    
            final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
            if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
                fillGap(down);
            }
    
            if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {
                final int childIndex = mSelectedPosition - mFirstPosition;
                if (childIndex >= 0 && childIndex < getChildCount()) {
                    positionSelector(mSelectedPosition, getChildAt(childIndex));
                }
            } else if (mSelectorPosition != INVALID_POSITION) {
                final int childIndex = mSelectorPosition - mFirstPosition;
                if (childIndex >= 0 && childIndex < getChildCount()) {
                    positionSelector(INVALID_POSITION, getChildAt(childIndex));
                }
            } else {
                mSelectorRect.setEmpty();
            }
    
            mBlockLayoutRequests = false;
    
            invokeOnItemScrollListener();
            awakenScrollBars();
    
            return false;
        }
    

     可以看到Gallery处理View的关键代码:

    Java代码  收藏代码
    1. offsetChildrenLeftAndRight(limitedDeltaX);  
    2.   detachOffScreenChildren(toLeft);  
    3.  。。。。  
    4.   mRecycler.clear();  
          offsetChildrenLeftAndRight(limitedDeltaX);
            detachOffScreenChildren(toLeft);
           。。。。
            mRecycler.clear();

    而ListView中为:

      

    Java代码  收藏代码
    1. if (count > 0) {  
    2.     detachViewsFromParent(start, count);  
    3. }  
    4. offsetChildrenTopAndBottom(incrementalDeltaY);  
            if (count > 0) {
                detachViewsFromParent(start, count);
            }
            offsetChildrenTopAndBottom(incrementalDeltaY);

    detachOffScreenChildren和detachViewsFromParent跟ListView功能一样。但是Gallery多出一个 mRecycler.clear()。等待重复利用的mRecycler被回收了,这就造成了再Gallery中无法复用之前的View的情况,由此可见Gallery在内存的使用上存在很大的设计缺陷。

    再看Gallery与ListView的makeAndAddView方法

     Gallery

    Java代码  收藏代码
    1. private View makeAndAddView(int position, int offset, int x, boolean fromLeft) {  
    2.   
    3.     View child;  
    4.     if (!mDataChanged) {  
    5.         child = mRecycler.get(position);  
    6.         if (child != null) {  
    7.             // Can reuse an existing view  
    8.             int childLeft = child.getLeft();  
    9.               
    10.             // Remember left and right edges of where views have been placed  
    11.             mRightMost = Math.max(mRightMost, childLeft   
    12.                     + child.getMeasuredWidth());  
    13.             mLeftMost = Math.min(mLeftMost, childLeft);  
    14.   
    15.             // Position the view  
    16.             setUpChild(child, offset, x, fromLeft);  
    17.   
    18.             return child;  
    19.         }  
    20.     }  
    21.   
    22.     // Nothing found in the recycler -- ask the adapter for a view  
    23.     child = mAdapter.getView(position, nullthis);  
    24.   
    25.     // Position the view  
    26.     setUpChild(child, offset, x, fromLeft);  
    27.   
    28.     return child;  
    29. }  
        private View makeAndAddView(int position, int offset, int x, boolean fromLeft) {
    
            View child;
            if (!mDataChanged) {
                child = mRecycler.get(position);
                if (child != null) {
                    // Can reuse an existing view
                    int childLeft = child.getLeft();
    
                    // Remember left and right edges of where views have been placed
                    mRightMost = Math.max(mRightMost, childLeft
                            + child.getMeasuredWidth());
                    mLeftMost = Math.min(mLeftMost, childLeft);
    
                    // Position the view
                    setUpChild(child, offset, x, fromLeft);
    
                    return child;
                }
            }
    
            // Nothing found in the recycler -- ask the adapter for a view
            child = mAdapter.getView(position, null, this);
    
            // Position the view
            setUpChild(child, offset, x, fromLeft);
    
            return child;
        }

     ListView:

       

    Java代码  收藏代码
    1. private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,  
    2.         boolean selected) {  
    3.     View child;  
    4.   
    5.   
    6.     if (!mDataChanged) {  
    7.         // Try to use an existing view for this position  
    8.         child = mRecycler.getActiveView(position);  
    9.         if (child != null) {  
    10.             if (ViewDebug.TRACE_RECYCLER) {  
    11.                 ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,  
    12.                         position, getChildCount());  
    13.             }  
    14.   
    15.             // Found it -- we're using an existing child  
    16.             // This just needs to be positioned  
    17.             setupChild(child, position, y, flow, childrenLeft, selected, true);  
    18.   
    19.             return child;  
    20.         }  
    21.     }  
    22.   
    23.     // Make a new view for this position, or convert an unused view if possible  
    24.     child = obtainView(position, mIsScrap);  
    25.   
    26.     // This needs to be positioned and measured  
    27.     setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);  
    28.   
    29.     return child;  
    30. }  
    31.                               
        private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
                boolean selected) {
            View child;
    
            if (!mDataChanged) {
                // Try to use an existing view for this position
                child = mRecycler.getActiveView(position);
                if (child != null) {
                    if (ViewDebug.TRACE_RECYCLER) {
                        ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,
                                position, getChildCount());
                    }
    
                    // Found it -- we're using an existing child
                    // This just needs to be positioned
                    setupChild(child, position, y, flow, childrenLeft, selected, true);
    
                    return child;
                }
            }
    
            // Make a new view for this position, or convert an unused view if possible
            child = obtainView(position, mIsScrap);
    
            // This needs to be positioned and measured
            setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
    
            return child;
        }
    

    由此可见Gallery每次都将不可见的View进行了清理。对比ListView来说,在滚动过程中多了new一个View的开销。那么就存在一个避免此类问题的方法,自己对View建一个recyler从而弥补这个缺点。

    另外安卓的ViewPager+ScrollView 采用了传统的整个容器进行滚动算法,而不是调整childView的位置,但是也存在卡顿问题。

    ViewPager的用法见下文:

    http://blog.csdn.net/wangjinyu501/article/details/816

    ViewPager的卡顿见下文:

    http://marspring.mobi/viewpager-majorization/

    里面google提供了一个接口解决卡顿问题。至少目前看来,只是五十步笑百步。不能根本解决问题。默认当前正中位置前后缓存一个View。改后可以缓存多个View。当缓存的多个View被用完的时候,仍然是卡顿。

    另外,

    有人说用异步加载,异步加载解决的是数据加载问题,跟这个new View造成的卡顿问题是两码事儿。

    如何解决?

    自己做类似ListView的回收机制,对View进行复用,从而根本上解决这个问题。

    另外,安卓没有采用享元模式。。。。很遗憾。。。也许google大牛们会想到,希望如此。希望google能体谅我们,再提供一个类似View的轻量级显示控件来直接支持享元模式。这在Brew平台是有的。。。。。。。。。。。。。。。。。。。。。。郁闷。

  • 相关阅读:
    2017ccpc全国邀请赛(湖南湘潭) E. Partial Sum
    Codeforces Round #412 C. Success Rate (rated, Div. 2, base on VK Cup 2017 Round 3)
    2017 中国大学生程序设计竞赛 女生专场 Building Shops (hdu6024)
    51nod 1084 矩阵取数问题 V2
    Power收集
    红色的幻想乡
    Koishi Loves Segments
    Wood Processing
    整数对
    Room and Moor
  • 原文地址:https://www.cnblogs.com/seven1979/p/4369635.html
Copyright © 2011-2022 走看看