Advertisement
morganbelford

StagLayout

Jan 8th, 2013
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 8.58 KB | None | 0 0
  1. package com.morganbelford.stackoverflowtest.pinterest;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.HashSet;
  5.  
  6. import android.annotation.SuppressLint;
  7. import android.content.Context;
  8. import android.graphics.Canvas;
  9. import android.graphics.Paint;
  10. import android.graphics.RectF;
  11. import android.util.AttributeSet;
  12. import android.util.Log;
  13. import android.view.View;
  14. import android.view.ViewGroup;
  15. import android.widget.FrameLayout;
  16.  
  17. import com.loopj.android.image.SmartImageView;
  18.  
  19. public class StagLayout extends ViewGroup {
  20.    
  21.     private float _pxMargin;
  22.     private int _cCols;
  23.     private int _cMaxCachedViews;
  24.  
  25.     // store our image info
  26.     private ArrayList<ImageInfo> _infos;
  27.     private float _maxBottom;
  28.    
  29.     private float _viewportTop;
  30.     private float _viewportBottom;
  31.    
  32.     // manage subviews
  33.     // hashset of active
  34.     private HashSet<ImageInfo> _activeInfos;
  35.     private ArrayList<SmartImageView> _cachedViews;
  36.     //
  37.    
  38.     private Paint _paint;
  39.    
  40.     public StagLayout(Context context) {
  41.         super(context);
  42.     }
  43.  
  44.     public StagLayout(Context context, AttributeSet attrs) {
  45.         super(context, attrs);
  46.     }
  47.  
  48.     public StagLayout(Context context, AttributeSet attrs, int defStyle) {
  49.         super(context, attrs, defStyle);
  50.     }
  51.    
  52.     public void setUrls(String[] urls, float pxMargin, int cCols)
  53.     {
  54.         _pxMargin = pxMargin;
  55.         _cCols = cCols;
  56.         _cMaxCachedViews = 2 * cCols;
  57.         // build list of 200 ImageInfos, with default sizes
  58.         _infos = new ArrayList<ImageInfo>(200);  // should be urls.length
  59.         for (int i = 0; i< 200; i++)
  60.         {
  61.             final String sUrl = urls[i % urls.length];
  62.             _infos.add(new ImageInfo(sUrl, new OnClickListener() {
  63.                
  64.                 @Override
  65.                 public void onClick(View v) {
  66.                     Log.d("PinterestLayout", String.format("Image clicked: url == %s", sUrl));
  67.                 }
  68.             }));
  69.         }
  70.        
  71.         _activeInfos = new HashSet<ImageInfo>(_infos.size());
  72.         _cachedViews = new ArrayList<SmartImageView>(_cMaxCachedViews);
  73.  
  74.         requestLayout();
  75.  
  76.     }
  77.  
  78.     @SuppressLint("DefaultLocale")
  79.     @Override
  80.     protected void onFinishInflate() {
  81.         super.onFinishInflate();
  82.  
  83.         // record scale for layout later
  84.         _infos = new ArrayList<ImageInfo>(); // just to be non-null
  85.        
  86.         _cCols = 1; // for the math, before we get our urls set
  87.        
  88.         _paint = new Paint();
  89.         _paint.setColor(0x22000000);
  90.     }
  91.    
  92.    
  93.     private void computeImageInfo(float width)
  94.     {
  95.         float dxMargin = _pxMargin;
  96.         float dyMargin = _pxMargin;
  97.        
  98.         float left = 0;
  99.         float tops[] = new float[_cCols];  // start at 0
  100.         float widthCol = (int)((width - (_cCols + 1) * dxMargin) / _cCols);
  101.        
  102.         _maxBottom = 0;
  103.        
  104.         // layout the images -- set their layoutrect based on our current location and their bounds
  105.         for (int i = 0; i < _infos.size(); i++)
  106.         {
  107.             int iCol = i % _cCols;
  108.             // new row
  109.             if (iCol == 0)
  110.             {
  111.                left = dxMargin;
  112.                for (int j = 0; j < _cCols; j++)
  113.                    tops[j] += dyMargin;
  114.             }
  115.             ImageInfo info = _infos.get(i);
  116.             RectF bounds = info.bounds();
  117.             float scale = widthCol / bounds.width(); // up or down, for now, it does not matter
  118.             float layoutHeight = bounds.height() * scale;
  119.             float top = tops[iCol];
  120.             float bottom = top + layoutHeight;
  121.             info.setLayoutBounds(left, top, left + widthCol, bottom);
  122.            
  123.             if (bottom > _maxBottom)
  124.                 _maxBottom = bottom;
  125.             left += widthCol + dxMargin;
  126.             tops[iCol] += layoutHeight;
  127.         }
  128.        
  129.         // TODO build indexes of tops and bottoms
  130.        
  131.        
  132.         // should now set our own height using layoutParams
  133.         _maxBottom += dyMargin;
  134.     }
  135.    
  136.     public void setVisibleArea(int top, int bottom) {
  137.  
  138.         _viewportTop = top;
  139.         _viewportBottom = bottom;
  140.        
  141.         //fixup views
  142.         if (getWidth() == 0) // if we have never been measured, dont do this - it will happen in first layout shortly
  143.             return;
  144.         requestLayout();
  145.     }
  146.    
  147.     private void setupSubviews()
  148.     {
  149.  
  150.         // need to compute new set of active
  151.         // TODO for now enumerate -- later do binary search and spread.
  152.         HashSet<ImageInfo> neededInfos = new HashSet<ImageInfo>(_infos.size());
  153.         HashSet<ImageInfo> newInfos = new HashSet<ImageInfo>(_infos.size());
  154.         for (ImageInfo info : _infos)
  155.         {
  156.             if (info.overlaps(_viewportTop, _viewportBottom))
  157.             {
  158.                 neededInfos.add(info);
  159.                 if (info.view() == null)
  160.                     newInfos.add(info);
  161.             }
  162.         }
  163.        
  164.         // ok, so now we have the active ones. lets get any we need to deactivate        
  165.         HashSet<ImageInfo> unneededInfos = new HashSet<ImageInfo>(_activeInfos); // copy this
  166.         unneededInfos.removeAll(neededInfos);
  167.         // we want to grab all the views from these guys, and possibly reuse them
  168.         ArrayList<SmartImageView> unneededViews = new ArrayList<SmartImageView>(unneededInfos.size());
  169.         for (ImageInfo info : unneededInfos)
  170.         {
  171.             SmartImageView vw = info.view();
  172.             unneededViews.add(vw);
  173.             info.setView(null); // view still attached at this point to partent
  174.         }
  175.         // ok, so now we try to reuse the views, and create new ones if needed
  176.         for (ImageInfo info : newInfos)
  177.         {
  178.             SmartImageView vw = null;
  179.             if (unneededViews.size() > 0)
  180.             {
  181.                 vw = unneededViews.remove(0);
  182.             }
  183.             else if (_cachedViews.size() > 0)
  184.             {
  185.                 vw = _cachedViews.remove(0);
  186.                 addViewInLayout(vw, -1, new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
  187.             }
  188.             else
  189.             {
  190.                 vw = new SmartImageView(getContext());
  191.                 //vw.setBackgroundResource(R.drawable.photo_bg);
  192.                 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  193.                 addViewInLayout(vw, -1, lp);
  194.             }
  195.             info.setView(vw);  // info should also set its data
  196.         }
  197.        
  198.         // At this point, add any unneeded views to our cache, up to limit
  199.         for (SmartImageView vw : unneededViews)
  200.         {
  201.             // tell view to cancel
  202.             removeViewInLayout(vw);  // always remove from parent
  203.             if (_cachedViews.size() < _cMaxCachedViews)
  204.                 _cachedViews.add(vw);
  205.         }
  206.        
  207.         _activeInfos = neededInfos;
  208.        
  209.     }
  210.    
  211.     @Override
  212.     protected void onDraw(Canvas canvas) {
  213.         super.onDraw(canvas);
  214.        
  215.         // Just for kicks draw a 1px "drop shadow" around each image. We could (should) do this with a background on each SmartImageView, but
  216.         //  I wanted to experiment a little
  217.         if (_activeInfos == null)
  218.             return;
  219.        
  220.         for (ImageInfo info : _activeInfos)
  221.         {
  222.             RectF r = info.layoutBounds();
  223.             canvas.drawRect(r.left , r.top , r.right + 1, r.bottom + 1, _paint);
  224.         }
  225.     }
  226.    
  227.    
  228.     @Override
  229.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  230.  
  231.         int width = MeasureSpec.getSize(widthMeasureSpec);
  232.        
  233.         // measure each real guy, record "natural" size
  234.         for (ImageInfo info : _activeInfos)
  235.         {
  236.             View v = info.view();
  237.             v.measure(MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.UNSPECIFIED));
  238.         }
  239.        
  240.         computeImageInfo(width);  // this computes the layout/rect of all imageinfos, and sets _maxBottom
  241.         setMeasuredDimension(width, (int)_maxBottom);
  242.     }
  243.  
  244.     @Override
  245.     protected void onLayout(boolean changed, int l, int t, int r, int b) {
  246.         setupSubviews();
  247.  
  248.         for (ImageInfo info : _activeInfos)
  249.         {
  250.             RectF rBounds = info.layoutBounds();  // computed in measure
  251.             info.view().layout((int)rBounds.left, (int)rBounds.top, (int)rBounds.right, (int)rBounds.bottom);    
  252.         }
  253.  
  254.     }
  255.  
  256. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement