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平台是有的。。。。。。。。。。。。。。。。。。。。。。郁闷。

  • 相关阅读:
    The lexer hack
    How Clang handles the type / variable name ambiguity of C/C++
    python
    基于asp.net + easyui框架,一步步学习easyui-datagrid——界面(一)
    程序员最该看的30本书---------------------------国外编辑推荐
    DirectX 学习经典参考书籍 电子书下载
    基于asp.net+ easyui框架,js提交图片,实现先上传图片再提交表单
    请问JAVA三层架构,持久层,业务层,表现层,都该怎么理解?和MVC三层模型有什么区别
    对java框架的几点认识
    J2EE入门必备
  • 原文地址:https://www.cnblogs.com/seven1979/p/4369635.html
Copyright © 2011-2022 走看看