Google+和Google Now Listview滑动特效

最近在体验比较好的Android UE效果,从网上搜集代码,这段就是出自国外程序员之手的Google做的Google+Listview滚动效果

 
package com.cuubonandroid.sugaredlistanimations.sample;

import java.util.List;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.animation.DecelerateInterpolator;

/**
 * 
 * @author <a href="http://www.hugofernandes.pt">Hugo Fernandes</a>
 * 
 */
public abstract class GNowListAdapter extends GenericBaseAdapter {

  public GNowListAdapter(Context context, SpeedScrollListener scrollListener, List<? extends Object> items) {
    super(context, scrollListener, items);
  }

  @Override
  protected void defineInterpolator() {
    interpolator = new DecelerateInterpolator();
  }

  @Override
  protected View getAnimatedView(int position, View convertView, ViewGroup parent) {
    v = getRowView(position, convertView, parent);

    if (v != null && !positionsMapper.get(position) && position > previousPostition) {

      speed = scrollListener.getSpeed();

      animDuration = (((int) speed) == 0) ? ANIM_DEFAULT_SPEED : (long) (1 / speed * 15000);

      if (animDuration > ANIM_DEFAULT_SPEED)
        animDuration = ANIM_DEFAULT_SPEED;

      previousPostition = position;

      v.setTranslationY(height);
      v.setPivotX(width / 2);
      v.setPivotY(height / 2);
      v.setAlpha(0.0F);

      if (position % 2 == 0) {
        v.setTranslationX(-(width / 1.2F));
        v.setRotation(50);
      } else {
        v.setTranslationX((width / 1.2F));
        v.setRotation(-50);
      }

      ViewPropertyAnimator localViewPropertyAnimator =
          v.animate().rotation(0.0F).translationX(0).translationY(0).setDuration(animDuration).alpha(1.0F)
              .setInterpolator(interpolator);

      localViewPropertyAnimator.setStartDelay(500).start();

      positionsMapper.put(position, true);
    }

    return v;
  }

  /**
   * Get a View that displays the data at the specified position in the data
   * set. You can either create a View manually or inflate it from an XML layout
   * file. When the View is inflated, the parent View (GridView, ListView...)
   * will apply default layout parameters unless you use
   * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
   * to specify a root view and to prevent attachment to the root.
   * 
   * @param position The position of the item within the adapter's data set of
   *          the item whose view we want.
   * @param convertView The old view to reuse, if possible. Note: You should
   *          check that this view is non-null and of an appropriate type before
   *          using. If it is not possible to convert this view to display the
   *          correct data, this method can create a new view.
   * @param parent The parent that this view will eventually be attached to
   * @return A View corresponding to the data at the specified position.
   */
  protected abstract View getRowView(int position, View convertView, ViewGroup parent);
}



package com.cuubonandroid.sugaredlistanimations.sample;

import java.util.List;

import android.content.Context;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.widget.BaseAdapter;

/**
 * 
 * @author <a href="http://www.hugofernandes.pt">Hugo Fernandes</a>
 * 
 */
public abstract class GenericBaseAdapter extends BaseAdapter {

  protected static final long ANIM_DEFAULT_SPEED = 1000L;

  protected Interpolator interpolator;

  protected SparseBooleanArray positionsMapper;
  protected List<? extends Object> items;
  protected int size, height, width, previousPostition;
  protected SpeedScrollListener scrollListener;
  protected double speed;
  protected long animDuration;
  protected View v;
  protected Context context;

  protected GenericBaseAdapter(Context context, SpeedScrollListener scrollListener, List<? extends Object> items) {
    this.context = context;
    this.scrollListener = scrollListener;
    this.items = items;
    if (items != null)
      size = items.size();

    previousPostition = -1;
    positionsMapper = new SparseBooleanArray(size);
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    width = windowManager.getDefaultDisplay().getWidth();
    height = windowManager.getDefaultDisplay().getHeight();

    defineInterpolator();
  }

  @Override
  public int getCount() {
    return size;
  }

  @Override
  public Object getItem(int position) {
    return items != null && position < size ? items.get(position) : null;
  }

  @Override
  public long getItemId(int position) {
    return position;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    return getAnimatedView(position, convertView, parent);
  }

  protected abstract View getAnimatedView(int position, View convertView, ViewGroup parent);

  protected abstract void defineInterpolator();
}
package com.cuubonandroid.sugaredlistanimations.sample;

import java.util.List;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.animation.DecelerateInterpolator;

/**
 * 
 * @author <a href="http://www.hugofernandes.pt">Hugo Fernandes</a>
 *
 */
public abstract class GPlusListAdapter extends GenericBaseAdapter {

  public GPlusListAdapter(Context context, SpeedScrollListener scrollListener, List<? extends Object> items) {
    super(context, scrollListener, items);
  }

  @Override
  protected void defineInterpolator() {
    interpolator = new DecelerateInterpolator();
  }

  @Override
  public View getAnimatedView(int position, View convertView, ViewGroup parent) {
    v = getRowView(position, convertView, parent);

    if (v != null && !positionsMapper.get(position) && position > previousPostition) {
      speed = scrollListener.getSpeed();

      animDuration = (((int) speed) == 0) ? ANIM_DEFAULT_SPEED : (long) (1 / speed * 15000);

      if (animDuration > ANIM_DEFAULT_SPEED)
        animDuration = ANIM_DEFAULT_SPEED;

      previousPostition = position;

      v.setTranslationX(0.0F);
      v.setTranslationY(height);
      v.setRotationX(45.0F);
      v.setScaleX(0.7F);
      v.setScaleY(0.55F);

      ViewPropertyAnimator localViewPropertyAnimator =
          v.animate().rotationX(0.0F).rotationY(0.0F).translationX(0).translationY(0).setDuration(animDuration).scaleX(
              1.0F).scaleY(1.0F).setInterpolator(interpolator);

      localViewPropertyAnimator.setStartDelay(0).start();
      positionsMapper.put(position, true);
    }
    return v;
  }

  /**
   * Get a View that displays the data at the specified position in the data
   * set. You can either create a View manually or inflate it from an XML layout
   * file. When the View is inflated, the parent View (GridView, ListView...)
   * will apply default layout parameters unless you use
   * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
   * to specify a root view and to prevent attachment to the root.
   * 
   * @param position The position of the item within the adapter's data set of
   *          the item whose view we want.
   * @param convertView The old view to reuse, if possible. Note: You should
   *          check that this view is non-null and of an appropriate type before
   *          using. If it is not possible to convert this view to display the
   *          correct data, this method can create a new view.
   * @param parent The parent that this view will eventually be attached to
   * @return A View corresponding to the data at the specified position.
   */
  protected abstract View getRowView(int position, View convertView, ViewGroup parent);

}

package com.cuubonandroid.sugaredlistanimations.sample;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;

/**
 * This helper class download images from the Internet and binds those with the
 * provided ImageView.
 * 
 * <p>
 * It requires the INTERNET permission, which should be added to your
 * application's manifest file.
 * </p>
 * 
 * A local cache of downloaded images is maintained internally to improve
 * performance.
 */
public class ImageDownloader {
  private static final String LOG_TAG = "ImageDownloader";

  public enum Mode {
    NO_ASYNC_TASK, NO_DOWNLOADED_DRAWABLE, CORRECT
  }

  private Mode mode = Mode.CORRECT;

  /**
   * Download the specified image from the Internet and binds it to the provided
   * ImageView. The binding is immediate if the image is found in the cache and
   * will be done asynchronously otherwise. A null bitmap will be associated to
   * the ImageView if an error occurs.
   * 
   * @param url The URL of the image to download.
   * @param imageView The ImageView to bind the downloaded image to.
   */
  public void download(String url, ImageView imageView) {
    resetPurgeTimer();
    Bitmap bitmap = getBitmapFromCache(url);
    imageView.setTag(url);

    if (bitmap == null) {
      forceDownload(url, imageView);
    } else {
      cancelPotentialDownload(url, imageView);
      imageView.setImageBitmap(bitmap);
    }
  }

  /*
   * Same as download but the image is always downloaded and the cache is not
   * used. Kept private at the moment as its interest is not clear. private void
   * forceDownload(String url, ImageView view) { forceDownload(url, view, null);
   * }
   */

  /**
   * Same as download but the image is always downloaded and the cache is not
   * used. Kept private at the moment as its interest is not clear.
   */
  private void forceDownload(String url, ImageView imageView) {
    // State sanity: url is guaranteed to never be null in DownloadedDrawable
    // and cache keys.
    if (url == null) {
      imageView.setImageDrawable(null);
      return;
    }

    if (cancelPotentialDownload(url, imageView)) {
      switch (mode) {
        case NO_ASYNC_TASK:
          Bitmap bitmap = downloadBitmap(url);
          addBitmapToCache(url, bitmap);
          imageView.setImageBitmap(bitmap);
          break;

        case NO_DOWNLOADED_DRAWABLE:
          imageView.setMinimumHeight(156);
          BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
          task.execute(url);
          break;

        case CORRECT:
          task = new BitmapDownloaderTask(imageView);
          DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
          imageView.setImageDrawable(downloadedDrawable);
          imageView.setMinimumHeight(156);
          task.execute(url);
          break;
      }
    }
  }

  /**
   * Returns true if the current download has been canceled or if there was no
   * download in progress on this image view. Returns false if the download in
   * progress deals with the same url. The download is not stopped in that case.
   */
  private static boolean cancelPotentialDownload(String url, ImageView imageView) {
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

    if (bitmapDownloaderTask != null) {
      String bitmapUrl = bitmapDownloaderTask.url;
      if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
        bitmapDownloaderTask.cancel(true);
      } else {
        // The same URL is already being downloaded.
        return false;
      }
    }
    return true;
  }

  /**
   * @param imageView Any imageView
   * @return Retrieve the currently active download task (if any) associated
   *         with this imageView. null if there is no such task.
   */
  private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
    if (imageView != null) {
      Drawable drawable = imageView.getDrawable();
      if (drawable instanceof DownloadedDrawable) {
        DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
        return downloadedDrawable.getBitmapDownloaderTask();
      }
    }
    return null;
  }

  Bitmap downloadBitmap(String url) {
    final int IO_BUFFER_SIZE = 4 * 1024;

    // AndroidHttpClient is not allowed to be used from the main thread
    final HttpClient client =
        (mode == Mode.NO_ASYNC_TASK) ? new DefaultHttpClient() : AndroidHttpClient.newInstance("Android");
    final HttpGet getRequest = new HttpGet(url);

    try {
      HttpResponse response = client.execute(getRequest);
      final int statusCode = response.getStatusLine().getStatusCode();
      if (statusCode != HttpStatus.SC_OK) {
        Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
        return null;
      }

      final HttpEntity entity = response.getEntity();
      if (entity != null) {
        InputStream inputStream = null;
        try {
          inputStream = entity.getContent();
          // return BitmapFactory.decodeStream(inputStream);
          // Bug on slow connections, fixed in future release.
          return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
        } finally {
          if (inputStream != null) {
            inputStream.close();
          }
          entity.consumeContent();
        }
      }
    } catch (IOException e) {
      getRequest.abort();
      Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
    } catch (IllegalStateException e) {
      getRequest.abort();
      Log.w(LOG_TAG, "Incorrect URL: " + url);
    } catch (Exception e) {
      getRequest.abort();
      Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
    } finally {
      if ((client instanceof AndroidHttpClient)) {
        ((AndroidHttpClient) client).close();
      }
    }
    return null;
  }

  /*
   * An InputStream that skips the exact number of bytes provided, unless it
   * reaches EOF.
   */
  static class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
      super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
      long totalBytesSkipped = 0L;
      while (totalBytesSkipped < n) {
        long bytesSkipped = in.skip(n - totalBytesSkipped);
        if (bytesSkipped == 0L) {
          int b = read();
          if (b < 0) {
            break; // we reached EOF
          } else {
            bytesSkipped = 1; // we read one byte
          }
        }
        totalBytesSkipped += bytesSkipped;
      }
      return totalBytesSkipped;
    }
  }

  /**
   * The actual AsyncTask that will asynchronously download the image.
   */
  class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    private String url;
    private final WeakReference<ImageView> imageViewReference;

    public BitmapDownloaderTask(ImageView imageView) {
      imageViewReference = new WeakReference<ImageView>(imageView);
    }

    /**
     * Actual download method.
     */
    @Override
    protected Bitmap doInBackground(String... params) {
      url = params[0];
      return downloadBitmap(url);
    }

    /**
     * Once the image is downloaded, associates it to the imageView
     */
    @Override
    protected void onPostExecute(Bitmap bitmap) {
      if (isCancelled()) {
        bitmap = null;
      }

      addBitmapToCache(url, bitmap);

      if (imageViewReference != null) {
        ImageView imageView = imageViewReference.get();
        BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
        // Change bitmap only if this process is still associated with it
        // Or if we don't use any bitmap to task association
        // (NO_DOWNLOADED_DRAWABLE mode)
        if ((this == bitmapDownloaderTask) || (mode != Mode.CORRECT)) {
          imageView.setImageBitmap(bitmap);
        }
      }
    }
  }

  /**
   * A fake Drawable that will be attached to the imageView while the download
   * is in progress.
   * 
   * <p>
   * Contains a reference to the actual download task, so that a download task
   * can be stopped if a new binding is required, and makes sure that only the
   * last started download process can bind its result, independently of the
   * download finish order.
   * </p>
   */
  static class DownloadedDrawable extends ColorDrawable {
    private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
      super(Color.BLACK);
      bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
    }

    public BitmapDownloaderTask getBitmapDownloaderTask() {
      return bitmapDownloaderTaskReference.get();
    }
  }

  public void setMode(Mode mode) {
    this.mode = mode;
    clearCache();
  }

  /*
   * Cache-related fields and methods.
   * 
   * We use a hard and a soft cache. A soft reference cache is too aggressively
   * cleared by the Garbage Collector.
   */

  private static final int HARD_CACHE_CAPACITY = 10;
  private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds

  // Hard cache, with a fixed maximum capacity and a life duration
  private final HashMap<String, Bitmap> sHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2,
      0.75f, true) {
    @Override
    protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
      if (size() > HARD_CACHE_CAPACITY) {
        // Entries push-out of hard reference cache are transferred to soft
        // reference cache
        sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
        return true;
      } else
        return false;
    }
  };

  // Soft cache for bitmaps kicked out of hard cache
  private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
      new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);

  private final Handler purgeHandler = new Handler();

  private final Runnable purger = new Runnable() {
    public void run() {
      clearCache();
    }
  };

  /**
   * Adds this bitmap to the cache.
   * 
   * @param bitmap The newly downloaded bitmap.
   */
  private void addBitmapToCache(String url, Bitmap bitmap) {
    if (bitmap != null) {
      synchronized (sHardBitmapCache) {
        sHardBitmapCache.put(url, bitmap);
      }
    }
  }

  /**
   * @param url The URL of the image that will be retrieved from the cache.
   * @return The cached bitmap or null if it was not found.
   */
  private Bitmap getBitmapFromCache(String url) {
    // First try the hard reference cache
    synchronized (sHardBitmapCache) {
      final Bitmap bitmap = sHardBitmapCache.get(url);
      if (bitmap != null) {
        // Bitmap found in hard cache
        // Move element to first position, so that it is removed last
        sHardBitmapCache.remove(url);
        sHardBitmapCache.put(url, bitmap);
        return bitmap;
      }
    }

    // Then try the soft reference cache
    SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
    if (bitmapReference != null) {
      final Bitmap bitmap = bitmapReference.get();
      if (bitmap != null) {
        // Bitmap found in soft cache
        return bitmap;
      } else {
        // Soft reference has been Garbage Collected
        sSoftBitmapCache.remove(url);
      }
    }

    return null;
  }

  /**
   * Clears the image cache used internally to improve performance. Note that
   * for memory efficiency reasons, the cache will automatically be cleared
   * after a certain inactivity delay.
   */
  public void clearCache() {
    sHardBitmapCache.clear();
    sSoftBitmapCache.clear();
  }

  /**
   * Allow a new delay before the automatic cache clear is done.
   */
  private void resetPurgeTimer() {
    purgeHandler.removeCallbacks(purger);
    purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);
  }
}

package com.cuubonandroid.sugaredlistanimations.sample;

import java.util.Arrays;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

public class NowImageAdapter extends GNowListAdapter {
  private final ImageDownloader imageDownloader = new ImageDownloader();

  private static final String[] URLS =
      {
          "http://lh5.ggpht.com/_mrb7w4gF8Ds/TCpetKSqM1I/AAAAAAAAD2c/Qef6Gsqf12Y/s144-c/_DSC4374%20copy.jpg",
          "http://lh5.ggpht.com/_Z6tbBnE-swM/TB0CryLkiLI/AAAAAAAAVSo/n6B78hsDUz4/s144-c/_DSC3454.jpg",
          "http://lh3.ggpht.com/_GEnSvSHk4iE/TDSfmyCfn0I/AAAAAAAAF8Y/cqmhEoxbwys/s144-c/_MG_3675.jpg",
          "http://lh6.ggpht.com/_Nsxc889y6hY/TBp7jfx-cgI/AAAAAAAAHAg/Rr7jX44r2Gc/s144-c/IMGP9775a.jpg",
          "http://lh3.ggpht.com/_lLj6go_T1CQ/TCD8PW09KBI/AAAAAAAAQdc/AqmOJ7eg5ig/s144-c/Juvenile%20Gannet%20despute.jpg",
          "http://lh6.ggpht.com/_ZN5zQnkI67I/TCFFZaJHDnI/AAAAAAAABVk/YoUbDQHJRdo/s144-c/P9250508.JPG",
          "http://lh4.ggpht.com/_XjNwVI0kmW8/TCOwNtzGheI/AAAAAAAAC84/SxFJhG7Scgo/s144-c/0014.jpg",
          "http://lh6.ggpht.com/_lnDTHoDrJ_Y/TBvKsJ9qHtI/AAAAAAAAG6g/Zll2zGvrm9c/s144-c/000007.JPG",
          "http://lh6.ggpht.com/_qvCl2efjxy0/TCIVI-TkuGI/AAAAAAAAOUY/vbk9MURsv48/s144-c/DSC_0844.JPG",
          "http://lh4.ggpht.com/_TPlturzdSE8/TBv4ugH60PI/AAAAAAAAMsI/p2pqG85Ghhs/s144-c/_MG_3963.jpg",
          "http://lh4.ggpht.com/_4f1e_yo-zMQ/TCe5h9yN-TI/AAAAAAAAXqs/8X2fIjtKjmw/s144-c/IMG_1786.JPG",
          "http://lh6.ggpht.com/_iFt5VZDjxkY/TB9rQyWnJ4I/AAAAAAAADpU/lP2iStizJz0/s144-c/DSCF1014.JPG",
          "http://lh5.ggpht.com/_hepKlJWopDg/TB-_WXikaYI/AAAAAAAAElI/715k4NvBM4w/s144-c/IMG_0075.JPG",
          "http://lh6.ggpht.com/_OfRSx6nn68g/TCzsQic_z3I/AAAAAAABOOI/5G4Kwzb2qhk/s144-c/EASTER%20ISLAND_Hanga%20Roa_31.5.08_46.JPG",
          "http://lh6.ggpht.com/_ZGv_0FWPbTE/TB-_GLhqYBI/AAAAAAABVxs/cVEvQzt0ke4/s144-c/IMG_1288_hf.jpg",
          "http://lh6.ggpht.com/_a29lGRJwo0E/TBqOK_tUKmI/AAAAAAAAVbw/UloKpjsKP3c/s144-c/31012332.jpg",
          "http://lh3.ggpht.com/_55Lla4_ARA4/TB6xbyxxJ9I/AAAAAAABTWo/GKe24SwECns/s144-c/Bluebird%20049.JPG",
          "http://lh3.ggpht.com/_iVnqmIBYi4Y/TCaOH6rRl1I/AAAAAAAA1qg/qeMerYQ6DYo/s144-c/Kwiat_100626_0016.jpg",
          "http://lh6.ggpht.com/_QFsB_q7HFlo/TCItd_2oBkI/AAAAAAAAFsk/4lgJWweJ5N8/s144-c/3705226938_d6d66d6068_o.jpg",
          "http://lh5.ggpht.com/_JTI0xxNrKFA/TBsKQ9uOGNI/AAAAAAAChQg/z8Exh32VVTA/s144-c/CRW_0015-composite.jpg",
          "http://lh6.ggpht.com/_loGyjar4MMI/S-InVNkTR_I/AAAAAAAADJY/Fb5ifFNGD70/s144-c/Moving%20Rock.jpg",
          "http://lh4.ggpht.com/_L7i4Tra_XRY/TBtxjScXLqI/AAAAAAAAE5o/ue15HuP8eWw/s144-c/opera%20house%20II.jpg",
          "http://lh3.ggpht.com/_rfAz5DWHZYs/S9cstBTv1iI/AAAAAAAAeYA/EyZPUeLMQ98/s144-c/DSC_6425.jpg",
          "http://lh6.ggpht.com/_iGI-XCxGLew/S-iYQWBEG-I/AAAAAAAACB8/JuFti4elptE/s144-c/norvig-polar-bear.jpg",
          "http://lh3.ggpht.com/_M3slUPpIgmk/SlbnavqG1cI/AAAAAAAACvo/z6-CnXGma7E/s144-c/mf_003.jpg",
          "http://lh4.ggpht.com/_loGyjar4MMI/S-InQvd_3hI/AAAAAAAADIw/dHvCFWfyHxQ/s144-c/Rainbokeh.jpg",
          "http://lh4.ggpht.com/_yy6KdedPYp4/SB5rpK3Zv0I/AAAAAAAAOM8/mokl_yo2c9E/s144-c/Point%20Reyes%20road%20.jpg",
          "http://lh5.ggpht.com/_6_dLVKawGJA/SMwq86HlAqI/AAAAAAAAG5U/q1gDNkmE5hI/s144-c/mobius-glow.jpg",
          "http://lh3.ggpht.com/_QFsB_q7HFlo/TCItc19Jw3I/AAAAAAAAFs4/nPfiz5VGENk/s144-c/4551649039_852be0a952_o.jpg",
          "http://lh6.ggpht.com/_TQY-Nm7P7Jc/TBpjA0ks2MI/AAAAAAAABcI/J6ViH98_poM/s144-c/IMG_6517.jpg",
          "http://lh3.ggpht.com/_rfAz5DWHZYs/S9cLAeKuueI/AAAAAAAAeYU/E19G8DOlJRo/s144-c/DSC_4397_8_9_tonemapped2.jpg",
          "http://lh4.ggpht.com/_TQY-Nm7P7Jc/TBpi6rKfFII/AAAAAAAABbg/79FOc0Dbq0c/s144-c/david_lee_sakura.jpg",
          "http://lh3.ggpht.com/_TQY-Nm7P7Jc/TBpi8EJ4eDI/AAAAAAAABb0/AZ8Cw1GCaIs/s144-c/Hokkaido%20Swans.jpg",
          "http://lh3.ggpht.com/_1aZMSFkxSJI/TCIjB6od89I/AAAAAAAACHM/CLWrkH0ziII/s144-c/079.jpg",
          "http://lh5.ggpht.com/_loGyjar4MMI/S-InWuHkR9I/AAAAAAAADJE/wD-XdmF7yUQ/s144-c/Colorado%20River%20Sunset.jpg",
          "http://lh3.ggpht.com/_0YSlK3HfZDQ/TCExCG1Zc3I/AAAAAAAAX1w/9oCH47V6uIQ/s144-c/3138923889_a7fa89cf94_o.jpg",
          "http://lh6.ggpht.com/_K29ox9DWiaM/TAXe4Fi0xTI/AAAAAAAAVIY/zZA2Qqt2HG0/s144-c/IMG_7100.JPG",
          "http://lh6.ggpht.com/_0YSlK3HfZDQ/TCEx16nJqpI/AAAAAAAAX1c/R5Vkzb8l7yo/s144-c/4235400281_34d87a1e0a_o.jpg",
          "http://lh4.ggpht.com/_8zSk3OGcpP4/TBsOVXXnkTI/AAAAAAAAAEo/0AwEmuqvboo/s144-c/yosemite_forrest.jpg",
          "http://lh4.ggpht.com/_6_dLVKawGJA/SLZToqXXVrI/AAAAAAAAG5k/7fPSz_ldN9w/s144-c/coastal-1.jpg",
          "http://lh4.ggpht.com/_WW8gsdKXVXI/TBpVr9i6BxI/AAAAAAABhNg/KC8aAJ0wVyk/s144-c/IMG_6233_1_2-2.jpg",
          "http://lh3.ggpht.com/_loGyjar4MMI/S-InS0tJJSI/AAAAAAAADHU/E8GQJ_qII58/s144-c/Windmills.jpg",
          "http://lh4.ggpht.com/_loGyjar4MMI/S-InbXaME3I/AAAAAAAADHo/4gNYkbxemFM/s144-c/Frantic.jpg",
          "http://lh5.ggpht.com/_loGyjar4MMI/S-InKAviXzI/AAAAAAAADHA/NkyP5Gge8eQ/s144-c/Rice%20Fields.jpg",
          "http://lh3.ggpht.com/_loGyjar4MMI/S-InZA8YsZI/AAAAAAAADH8/csssVxalPcc/s144-c/Seahorse.jpg",
          "http://lh3.ggpht.com/_syQa1hJRWGY/TBwkCHcq6aI/AAAAAAABBEg/R5KU1WWq59E/s144-c/Antelope.JPG",
          "http://lh5.ggpht.com/_MoEPoevCLZc/S9fHzNgdKDI/AAAAAAAADwE/UAno6j5StAs/s144-c/c84_7083.jpg",
          "http://lh4.ggpht.com/_DJGvVWd7IEc/TBpRsGjdAyI/AAAAAAAAFNw/rdvyRDgUD8A/s144-c/Free.jpg",
          "http://lh6.ggpht.com/_iO97DXC99NY/TBwq3_kmp9I/AAAAAAABcz0/apq1ffo_MZo/s144-c/IMG_0682_cp.jpg",
          "http://lh4.ggpht.com/_7V85eCJY_fg/TBpXudG4_PI/AAAAAAAAPEE/8cHJ7G84TkM/s144-c/20100530_120257_0273-Edit-2.jpg"};

  private Context context;

  public NowImageAdapter(Context context, SpeedScrollListener scrollListener) {
    super(context, scrollListener, Arrays.asList(URLS));
    this.context = context;
  }

  @Override
  protected View getRowView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;

    if (convertView == null) {
      convertView = LayoutInflater.from(context).inflate(R.layout.row, parent, false);

      holder = new ViewHolder();
      holder.image = (ImageView) convertView.findViewById(R.id.image);
      holder.text = (TextView) convertView.findViewById(R.id.row_text);

      convertView.setTag(holder);
    } else
      holder = (ViewHolder) convertView.getTag();

    imageDownloader.download(URLS[position], holder.image);
    holder.text.setText("Olá Mundo " + position);

    return convertView;
  }

  static class ViewHolder {
    ImageView image;
    TextView text;
  }
}

package com.cuubonandroid.sugaredlistanimations.sample;

import java.util.Arrays;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

public class PlusImageAdapter extends GPlusListAdapter {
  private final ImageDownloader imageDownloader = new ImageDownloader();

  private static final String[] URLS =
      {
          "http://lh5.ggpht.com/_mrb7w4gF8Ds/TCpetKSqM1I/AAAAAAAAD2c/Qef6Gsqf12Y/s144-c/_DSC4374%20copy.jpg",
          "http://lh5.ggpht.com/_Z6tbBnE-swM/TB0CryLkiLI/AAAAAAAAVSo/n6B78hsDUz4/s144-c/_DSC3454.jpg",
          "http://lh3.ggpht.com/_GEnSvSHk4iE/TDSfmyCfn0I/AAAAAAAAF8Y/cqmhEoxbwys/s144-c/_MG_3675.jpg",
          "http://lh6.ggpht.com/_Nsxc889y6hY/TBp7jfx-cgI/AAAAAAAAHAg/Rr7jX44r2Gc/s144-c/IMGP9775a.jpg",
          "http://lh3.ggpht.com/_lLj6go_T1CQ/TCD8PW09KBI/AAAAAAAAQdc/AqmOJ7eg5ig/s144-c/Juvenile%20Gannet%20despute.jpg",
          "http://lh6.ggpht.com/_ZN5zQnkI67I/TCFFZaJHDnI/AAAAAAAABVk/YoUbDQHJRdo/s144-c/P9250508.JPG",
          "http://lh4.ggpht.com/_XjNwVI0kmW8/TCOwNtzGheI/AAAAAAAAC84/SxFJhG7Scgo/s144-c/0014.jpg",
          "http://lh6.ggpht.com/_lnDTHoDrJ_Y/TBvKsJ9qHtI/AAAAAAAAG6g/Zll2zGvrm9c/s144-c/000007.JPG",
          "http://lh6.ggpht.com/_qvCl2efjxy0/TCIVI-TkuGI/AAAAAAAAOUY/vbk9MURsv48/s144-c/DSC_0844.JPG",
          "http://lh4.ggpht.com/_TPlturzdSE8/TBv4ugH60PI/AAAAAAAAMsI/p2pqG85Ghhs/s144-c/_MG_3963.jpg",
          "http://lh4.ggpht.com/_4f1e_yo-zMQ/TCe5h9yN-TI/AAAAAAAAXqs/8X2fIjtKjmw/s144-c/IMG_1786.JPG",
          "http://lh6.ggpht.com/_iFt5VZDjxkY/TB9rQyWnJ4I/AAAAAAAADpU/lP2iStizJz0/s144-c/DSCF1014.JPG",
          "http://lh5.ggpht.com/_hepKlJWopDg/TB-_WXikaYI/AAAAAAAAElI/715k4NvBM4w/s144-c/IMG_0075.JPG",
          "http://lh6.ggpht.com/_OfRSx6nn68g/TCzsQic_z3I/AAAAAAABOOI/5G4Kwzb2qhk/s144-c/EASTER%20ISLAND_Hanga%20Roa_31.5.08_46.JPG",
          "http://lh6.ggpht.com/_ZGv_0FWPbTE/TB-_GLhqYBI/AAAAAAABVxs/cVEvQzt0ke4/s144-c/IMG_1288_hf.jpg",
          "http://lh6.ggpht.com/_a29lGRJwo0E/TBqOK_tUKmI/AAAAAAAAVbw/UloKpjsKP3c/s144-c/31012332.jpg",
          "http://lh3.ggpht.com/_55Lla4_ARA4/TB6xbyxxJ9I/AAAAAAABTWo/GKe24SwECns/s144-c/Bluebird%20049.JPG",
          "http://lh3.ggpht.com/_iVnqmIBYi4Y/TCaOH6rRl1I/AAAAAAAA1qg/qeMerYQ6DYo/s144-c/Kwiat_100626_0016.jpg",
          "http://lh6.ggpht.com/_QFsB_q7HFlo/TCItd_2oBkI/AAAAAAAAFsk/4lgJWweJ5N8/s144-c/3705226938_d6d66d6068_o.jpg",
          "http://lh5.ggpht.com/_JTI0xxNrKFA/TBsKQ9uOGNI/AAAAAAAChQg/z8Exh32VVTA/s144-c/CRW_0015-composite.jpg",
          "http://lh6.ggpht.com/_loGyjar4MMI/S-InVNkTR_I/AAAAAAAADJY/Fb5ifFNGD70/s144-c/Moving%20Rock.jpg",
          "http://lh4.ggpht.com/_L7i4Tra_XRY/TBtxjScXLqI/AAAAAAAAE5o/ue15HuP8eWw/s144-c/opera%20house%20II.jpg",
          "http://lh3.ggpht.com/_rfAz5DWHZYs/S9cstBTv1iI/AAAAAAAAeYA/EyZPUeLMQ98/s144-c/DSC_6425.jpg",
          "http://lh6.ggpht.com/_iGI-XCxGLew/S-iYQWBEG-I/AAAAAAAACB8/JuFti4elptE/s144-c/norvig-polar-bear.jpg",
          "http://lh3.ggpht.com/_M3slUPpIgmk/SlbnavqG1cI/AAAAAAAACvo/z6-CnXGma7E/s144-c/mf_003.jpg",
          "http://lh4.ggpht.com/_loGyjar4MMI/S-InQvd_3hI/AAAAAAAADIw/dHvCFWfyHxQ/s144-c/Rainbokeh.jpg",
          "http://lh4.ggpht.com/_yy6KdedPYp4/SB5rpK3Zv0I/AAAAAAAAOM8/mokl_yo2c9E/s144-c/Point%20Reyes%20road%20.jpg",
          "http://lh5.ggpht.com/_6_dLVKawGJA/SMwq86HlAqI/AAAAAAAAG5U/q1gDNkmE5hI/s144-c/mobius-glow.jpg",
          "http://lh3.ggpht.com/_QFsB_q7HFlo/TCItc19Jw3I/AAAAAAAAFs4/nPfiz5VGENk/s144-c/4551649039_852be0a952_o.jpg",
          "http://lh6.ggpht.com/_TQY-Nm7P7Jc/TBpjA0ks2MI/AAAAAAAABcI/J6ViH98_poM/s144-c/IMG_6517.jpg",
          "http://lh3.ggpht.com/_rfAz5DWHZYs/S9cLAeKuueI/AAAAAAAAeYU/E19G8DOlJRo/s144-c/DSC_4397_8_9_tonemapped2.jpg",
          "http://lh4.ggpht.com/_TQY-Nm7P7Jc/TBpi6rKfFII/AAAAAAAABbg/79FOc0Dbq0c/s144-c/david_lee_sakura.jpg",
          "http://lh3.ggpht.com/_TQY-Nm7P7Jc/TBpi8EJ4eDI/AAAAAAAABb0/AZ8Cw1GCaIs/s144-c/Hokkaido%20Swans.jpg",
          "http://lh3.ggpht.com/_1aZMSFkxSJI/TCIjB6od89I/AAAAAAAACHM/CLWrkH0ziII/s144-c/079.jpg",
          "http://lh5.ggpht.com/_loGyjar4MMI/S-InWuHkR9I/AAAAAAAADJE/wD-XdmF7yUQ/s144-c/Colorado%20River%20Sunset.jpg",
          "http://lh3.ggpht.com/_0YSlK3HfZDQ/TCExCG1Zc3I/AAAAAAAAX1w/9oCH47V6uIQ/s144-c/3138923889_a7fa89cf94_o.jpg",
          "http://lh6.ggpht.com/_K29ox9DWiaM/TAXe4Fi0xTI/AAAAAAAAVIY/zZA2Qqt2HG0/s144-c/IMG_7100.JPG",
          "http://lh6.ggpht.com/_0YSlK3HfZDQ/TCEx16nJqpI/AAAAAAAAX1c/R5Vkzb8l7yo/s144-c/4235400281_34d87a1e0a_o.jpg",
          "http://lh4.ggpht.com/_8zSk3OGcpP4/TBsOVXXnkTI/AAAAAAAAAEo/0AwEmuqvboo/s144-c/yosemite_forrest.jpg",
          "http://lh4.ggpht.com/_6_dLVKawGJA/SLZToqXXVrI/AAAAAAAAG5k/7fPSz_ldN9w/s144-c/coastal-1.jpg",
          "http://lh4.ggpht.com/_WW8gsdKXVXI/TBpVr9i6BxI/AAAAAAABhNg/KC8aAJ0wVyk/s144-c/IMG_6233_1_2-2.jpg",
          "http://lh3.ggpht.com/_loGyjar4MMI/S-InS0tJJSI/AAAAAAAADHU/E8GQJ_qII58/s144-c/Windmills.jpg",
          "http://lh4.ggpht.com/_loGyjar4MMI/S-InbXaME3I/AAAAAAAADHo/4gNYkbxemFM/s144-c/Frantic.jpg",
          "http://lh5.ggpht.com/_loGyjar4MMI/S-InKAviXzI/AAAAAAAADHA/NkyP5Gge8eQ/s144-c/Rice%20Fields.jpg",
          "http://lh3.ggpht.com/_loGyjar4MMI/S-InZA8YsZI/AAAAAAAADH8/csssVxalPcc/s144-c/Seahorse.jpg",
          "http://lh3.ggpht.com/_syQa1hJRWGY/TBwkCHcq6aI/AAAAAAABBEg/R5KU1WWq59E/s144-c/Antelope.JPG",
          "http://lh5.ggpht.com/_MoEPoevCLZc/S9fHzNgdKDI/AAAAAAAADwE/UAno6j5StAs/s144-c/c84_7083.jpg",
          "http://lh4.ggpht.com/_DJGvVWd7IEc/TBpRsGjdAyI/AAAAAAAAFNw/rdvyRDgUD8A/s144-c/Free.jpg",
          "http://lh6.ggpht.com/_iO97DXC99NY/TBwq3_kmp9I/AAAAAAABcz0/apq1ffo_MZo/s144-c/IMG_0682_cp.jpg",
          "http://lh4.ggpht.com/_7V85eCJY_fg/TBpXudG4_PI/AAAAAAAAPEE/8cHJ7G84TkM/s144-c/20100530_120257_0273-Edit-2.jpg"};

  private Context context;

  public PlusImageAdapter(Context context, SpeedScrollListener scrollListener) {
    super(context, scrollListener, Arrays.asList(URLS));
    this.context = context;
  }

  @Override
  protected View getRowView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;

    if (convertView == null) {
      convertView = LayoutInflater.from(context).inflate(R.layout.row, parent, false);

      holder = new ViewHolder();
      holder.image = (ImageView) convertView.findViewById(R.id.image);
      holder.text = (TextView) convertView.findViewById(R.id.row_text);

      convertView.setTag(holder);
    } else
      holder = (ViewHolder) convertView.getTag();

    imageDownloader.download(URLS[position], holder.image);
    holder.text.setText("Olá Mundo " + position);

    return convertView;
  }

  static class ViewHolder {
    ImageView image;
    TextView text;
  }
}

package com.cuubonandroid.sugaredlistanimations.sample;

import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;

/**
 * 
 * @author <a href="http://www.hugofernandes.pt">Hugo Fernandes</a>
 * 
 */
public class SpeedScrollListener implements OnScrollListener {

  private int previousFirstVisibleItem = 0;
  private long previousEventTime = 0, currTime, timeToScrollOneElement;
  private double speed = 0;

  @Override
  public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    if (previousFirstVisibleItem != firstVisibleItem) {
      currTime = System.currentTimeMillis();
      timeToScrollOneElement = currTime - previousEventTime;
      speed = ((double) 1 / timeToScrollOneElement) * 1000;

      previousFirstVisibleItem = firstVisibleItem;
      previousEventTime = currTime;

    }
  }

  @Override
  public void onScrollStateChanged(AbsListView view, int scrollState) {
  }

  public double getSpeed() {
    return speed;
  }

}

package com.cuubonandroid.sugaredlistanimations.sample;

import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;


public class MainActivity extends ListActivity {

  private SpeedScrollListener listener;
  private PlusImageAdapter plusAdapter;
  private NowImageAdapter nowAdapter;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    listener = new SpeedScrollListener();
    getListView().setOnScrollListener(listener);
    plusAdapter = new PlusImageAdapter(getApplicationContext(), listener);
    setListAdapter(plusAdapter);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.test, menu);
    return true;
  }

  @Override
  public boolean onMenuItemSelected(int featureId, MenuItem item) {
    boolean handled = false;
    switch (item.getItemId()) {
      case R.id.google_plus:
        plusAdapter = new PlusImageAdapter(getApplicationContext(), listener);
        setListAdapter(plusAdapter);
        break;

      case R.id.google_now:
        nowAdapter = new NowImageAdapter(getApplicationContext(), listener);
        setListAdapter(nowAdapter);
        break;
    }
    return handled;
  }

}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值