1. 程式人生 > >android 高仿IOS水滴版上下拉重新整理的Listview

android 高仿IOS水滴版上下拉重新整理的Listview

       之前有分享一些重新整理的Demo,最近找到一個重新整理的例子,分享給大家。同時感謝原作者的分享!
       現在給大家分享一個高仿IOS的Listview重新整理效果,支援上下拉重新整理。有些類似自定義的XListView 控制元件,與其不同的在於它重新整理的時候有類似水滴的下拉效果。

給大家看看效果圖先:

下面是它定義動畫的類,

主要是在判斷距離的時候建立回彈動畫,程式碼有註釋可以瞅瞅先

  1. package medusa.theone.waterdroplistview.view;
  2. import android.animation.Animator;
  3. import android.animation.ValueAnimator;
  4. import android.annotation.SuppressLint;
  5. import android.content.Context;
  6. import android.content.res.TypedArray;
  7. import android.graphics.Bitmap;
  8. import android.graphics.Canvas;
  9. import android.graphics.Color;
  10. import android.graphics.Paint;
  11. import android.graphics.Path;
  12. import android.graphics.RectF;
  13. import android.graphics.drawable.Drawable;
  14. import android.util.AttributeSet;
  15. import android.view.View;
  16. import android.view.animation.DecelerateInterpolator;
  17. import medusa.theone.waterdroplistview.R;
  18. import medusa.theone.waterdroplistview.entity.Circle;
  19. import medusa.theone.waterdroplistview.utils.Utils;
  20. /**
  21.  * 下拉頭中間的“水滴”
  22.  */
  23. public class WaterDropView extends View {
  24.     private Circle topCircle;
  25.     private Circle bottomCircle;
  26.     private Paint mPaint;
  27.     private Path mPath;
  28.     private float mMaxCircleRadius;//圓半徑最大值
  29.     private float mMinCircleRaidus;//圓半徑最小值
  30.     private Bitmap arrowBitmap;//箭頭
  31.     private final static int BACK_ANIM_DURATION = 180;
  32.     private final static float STROKE_WIDTH = 2;//邊線寬度
  33.     public WaterDropView(Context context) {
  34.         super(context);
  35.         init(context, null);
  36.     }
  37.     public WaterDropView(Context context, AttributeSet attrs) {
  38.         super(context, attrs);
  39.         init(context, attrs);
  40.     }
  41.     public WaterDropView(Context context, AttributeSet attrs, int defStyleAttr) {
  42.         super(context, attrs, defStyleAttr);
  43.         init(context, attrs);
  44.     }
  45.     private void parseAttrs(Context context, AttributeSet attrs) {
  46.         if (attrs != null) {
  47.             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaterDropView, 0, 0);
  48.             try {
  49.                 /*if (a.hasValue(R.styleable.WaterDropView_topcircle_x)) {
  50.                     topCircle.setX(a.getDimensionPixelSize(R.styleable.WaterDropView_topcircle_x, 0));
  51.                 }
  52.                 if (a.hasValue(R.styleable.WaterDropView_topcircle_y)) {
  53.                     topCircle.setY(a.getDimensionPixelSize(R.styleable.WaterDropView_topcircle_y, 0));
  54.                 }
  55.                 if (a.hasValue(R.styleable.WaterDropView_bottomcircle_x)) {
  56.                     bottomCircle.setX(a.getDimensionPixelSize(R.styleable.WaterDropView_topcircle_x, 0));
  57.                 }
  58.                 if (a.hasValue(R.styleable.WaterDropView_bottomcircle_y)) {
  59.                     bottomCircle.setY(a.getDimensionPixelSize(R.styleable.WaterDropView_topcircle_y, 0));
  60.                 }*/
  61.                 if(a.hasValue(R.styleable.WaterDropView_waterdrop_color)){
  62.                    int waterDropColor =  a.getColor(R.styleable.WaterDropView_waterdrop_color, Color.GRAY);
  63.                     mPaint.setColor(waterDropColor);
  64.                 }
  65.                 if (a.hasValue(R.styleable.WaterDropView_max_circle_radius)) {
  66.                     mMaxCircleRadius = a.getDimensionPixelSize(R.styleable.WaterDropView_max_circle_radius, 0);
  67.                     topCircle.setRadius(mMaxCircleRadius);
  68.                     bottomCircle.setRadius(mMaxCircleRadius);
  69.                     topCircle.setX(STROKE_WIDTH + mMaxCircleRadius);
  70.                     topCircle.setY(STROKE_WIDTH + mMaxCircleRadius);
  71.                     bottomCircle.setX(STROKE_WIDTH + mMaxCircleRadius);
  72.                     bottomCircle.setY(STROKE_WIDTH + mMaxCircleRadius);
  73.                 }
  74.                 if (a.hasValue(R.styleable.WaterDropView_min_circle_radius)) {
  75.                     mMinCircleRaidus = a.getDimensionPixelSize(R.styleable.WaterDropView_min_circle_radius, 0);
  76.                     if (mMinCircleRaidus > mMaxCircleRadius) {
  77.                         throw new IllegalStateException("Circle's MinRaidus should be equal or lesser than the MaxRadius");
  78.                     }
  79.                 }
  80.             } catch (Exception e) {
  81.                 e.printStackTrace();
  82.             } finally {
  83.                 a.recycle();
  84.             }
  85.         }
  86.     }
  87.     private void init(Context context, AttributeSet attrs) {
  88.         topCircle = new Circle();
  89.         bottomCircle = new Circle();
  90.         mPath = new Path();
  91.         mPaint = new Paint();
  92.         mPaint.setColor(Color.GRAY);
  93.         mPaint.setAntiAlias(true);
  94.         mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
  95.         mPaint.setStrokeWidth(STROKE_WIDTH);
  96.         @SuppressWarnings("deprecation")
  97. Drawable drawable = getResources().getDrawable(R.drawable.refresh_arrow);
  98.         arrowBitmap = Utils.drawableToBitmap(drawable);
  99.         parseAttrs(context, attrs);
  100.     }
  101.     @Override
  102.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  103.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  104.         //寬度:上圓和下圓的最大直徑
  105.         int width = (int) ((mMaxCircleRadius + STROKE_WIDTH) * 2);
  106.         //高度:上圓半徑 + 圓心距 + 下圓半徑
  107.         int height = (int) Math.ceil(bottomCircle.getY()+bottomCircle.getRadius() + STROKE_WIDTH * 2);
  108.         setMeasuredDimension(width, height);
  109.     }
  110.     @Override
  111.     protected void onDraw(Canvas canvas) {
  112.         makeBezierPath();
  113. //        mPaint.setColor(Color.RED);
  114. //        mPaint.setAlpha(200);
  115.         canvas.drawPath(mPath, mPaint);
  116. //        mPaint.setColor(Color.GRAY);
  117. //        mPaint.setAlpha(50);
  118.         canvas.drawCircle(topCircle.getX(), topCircle.getY(), topCircle.getRadius(), mPaint);
  119.         canvas.drawCircle(bottomCircle.getX(), bottomCircle.getY(), bottomCircle.getRadius(), mPaint);
  120. //        canvas.drawBitmap(arrowBitmap, topCircle.getX() - topCircle.getRadius(), topCircle.getY() - topCircle.getRadius(), mPaint);
  121.         RectF bitmapArea = new RectF(topCircle.getX()-0.5f*topCircle.getRadius(),topCircle.getY()-0.5f*topCircle.getRadius(),topCircle.getX()+ 0.5f*topCircle.getRadius(),topCircle.getY()+0.5f*topCircle.getRadius());
  122.         canvas.drawBitmap(arrowBitmap,null,bitmapArea,mPaint);
  123.         super.onDraw(canvas);
  124.     }
  125.     private void makeBezierPath() {
  126.         mPath.reset();
  127.         //獲取兩圓的兩個切線形成的四個切點
  128.         double angle = getAngle();
  129.         float top_x1 = (float) (topCircle.getX() - topCircle.getRadius() * Math.cos(angle));
  130.         float top_y1 = (float) (topCircle.getY() + topCircle.getRadius() * Math.sin(angle));
  131.         float top_x2 = (float) (topCircle.getX() + topCircle.getRadius() * Math.cos(angle));
  132.         float top_y2 = top_y1;
  133.         float bottom_x1 = (float) (bottomCircle.getX() - bottomCircle.getRadius() * Math.cos(angle));
  134.         float bottom_y1 = (float) (bottomCircle.getY() + bottomCircle.getRadius() * Math.sin(angle));
  135.         float bottom_x2 = (float) (bottomCircle.getX() + bottomCircle.getRadius() * Math.cos(angle));
  136.         float bottom_y2 = bottom_y1;
  137.         mPath.moveTo(topCircle.getX(), topCircle.getY());
  138.         mPath.lineTo(top_x1, top_y1);
  139.         mPath.quadTo((bottomCircle.getX() - bottomCircle.getRadius()),
  140.                 (bottomCircle.getY() + topCircle.getY()) / 2,
  141.                 bottom_x1,
  142.                 bottom_y1);
  143.         mPath.lineTo(bottom_x2, bottom_y2);
  144.         mPath.quadTo((bottomCircle.getX() + bottomCircle.getRadius()),
  145.                 (bottomCircle.getY() + top_y2) / 2,
  146.                 top_x2,
  147.                 top_y2);
  148.         mPath.close();
  149.     }
  150.     /**
  151.      * 獲得兩個圓切線與圓心連線的夾角
  152.      *
  153.      * @return
  154.      */
  155.     private double getAngle() {
  156.         if (bottomCircle.getRadius() > topCircle.getRadius()) {
  157.             throw new IllegalStateException("bottomCircle's radius must be less than the topCircle's");
  158.         }
  159.         return Math.asin((topCircle.getRadius() - bottomCircle.getRadius()) / (bottomCircle.getY() - topCircle.getY()));
  160.     }
  161.     /**
  162.      * 建立回彈動畫
  163.      * 上圓半徑減速恢復至最大半徑
  164.      * 下圓半徑減速恢復至最大半徑
  165.      * 圓心距減速從最大值減到0(下圓Y從當前位置移動到上圓Y)。
  166.      *
  167.      * @return
  168.      */
  169.     @SuppressLint("NewApi")
  170. public Animator createAnimator() {
  171.         ValueAnimator valueAnimator = ValueAnimator.ofFloat(1, 0).setDuration(BACK_ANIM_DURATION);
  172.         valueAnimator.setInterpolator(new DecelerateInterpolator());
  173.         valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  174.             @Override
  175.             public void onAnimationUpdate(ValueAnimator valueAnimator) {
  176.                 updateComleteState((Float) valueAnimator.getAnimatedValue());
  177.             }
  178.         });
  179.         return valueAnimator;
  180.     }
  181.     /**
  182.      * 完成的百分比
  183.      *
  184.      * @param percent between[0,1]
  185.      */
  186.     public void updateComleteState(float percent) {
  187.         if (percent < 0 || percent > 1) {
  188.             throw new IllegalStateException("completion percent should between 0 and 1!");
  189.         }
  190.         float top_r = (float) (mMaxCircleRadius - 0.25 * percent * mMaxCircleRadius);
  191.         float bottom_r = (mMinCircleRaidus - mMaxCircleRadius) * percent + mMaxCircleRadius;
  192.         float bottomCricleOffset = 2 * percent * mMaxCircleRadius;
  193.         topCircle.setRadius(top_r);
  194.         bottomCircle.setRadius(bottom_r);
  195.         bottomCircle.setY(topCircle.getY() + bottomCricleOffset);
  196.         requestLayout();
  197.         postInvalidate();
  198.     }
  199.     public Circle getTopCircle() {
  200.         return topCircle;
  201.     }
  202.     public Circle getBottomCircle() {
  203.         return bottomCircle;
  204.     }
  205.     public void setIndicatorColor(int color) {
  206.         mPaint.setColor(color);
  207.     }
  208.     public int getIndicatorColor() {
  209.         return mPaint.getColor();
  210.     }
  211. }
下面是佈局的引用:
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     android:background="@color/black"
  6.     tools:context="medusa.theone.waterdroplistview.activity.MainActivity" >
  7.     <medusa.theone.waterdroplistview.view.WaterDropListView
  8.         android:id="@+id/waterdrop_listview"
  9.         android:layout_width="match_parent"
  10.         android:layout_height="wrap_content"
  11.         android:cacheColorHint="#00000000"
  12.         android:divider="@null"
  13.         android:listSelector="@android:color/transparent" >
  14.     </medusa.theone.waterdroplistview.view.WaterDropListView>
  15. </RelativeLayout>
自定義類的方法使用
  1. package medusa.theone.waterdroplistview.activity;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.concurrent.ExecutorService;
  5. import java.util.concurrent.Executors;
  6. import medusa.theone.waterdroplistview.R;
  7. import medusa.theone.waterdroplistview.view.WaterDropListView;
  8. import android.annotation.SuppressLint;
  9. import android.app.Activity;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.os.Message;
  13. import android.view.Menu;
  14. import android.view.MenuItem;
  15. import android.widget.ArrayAdapter;
  16. /**
  17.  * 主頁面
  18.  * 
  19.  * @author seven2729
  20.  * @Blog http://blog.csdn.net/seven2729
  21.  * @version v1.0
  22.  * @copyright 2010-2015
  23.  * @create-time 2015年5月14日 下午2:19:56
  24.  *
  25.  */
  26. public class MainActivity extends Activity implements WaterDropListView.IWaterDropListViewListener  {
  27.     private WaterDropListView waterDropListView;
  28.     @SuppressLint("HandlerLeak")
  29. private Handler handler = new Handler() {
  30.         @Override
  31.         public void handleMessage(Message msg) {
  32.             super.handleMessage(msg);
  33.             switch (msg.what){
  34.                 case 1:
  35.                     waterDropListView.stopRefresh();
  36.                     break;
  37.                 case 2:
  38.                     waterDropListView.stopLoadMore();
  39.                     break;
  40.             }
  41.         }
  42.     };
  43.     @Override
  44.     protected void onCreate(Bundle savedInstanceState) {
  45.         super.onCreate(savedInstanceState);
  46.         setContentView(R.layout.activity_main);
  47.         waterDropListView = (WaterDropListView) findViewById(R.id.waterdrop_listview);
  48.         waterDropListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, getData()));
  49.         waterDropListView.setWaterDropListViewListener(this);
  50.         waterDropListView.setPullLoadEnable(true);
  51.     }
  52.     @Override
  53.     public boolean onCreateOptionsMenu(Menu menu) {
  54.         // Inflate the menu; this adds items to the action bar if it is present.
  55.         getMenuInflater().inflate(R.menu.menu_second, menu);
  56.         return true;
  57.     }
  58.     @Override
  59.     public boolean onOptionsItemSelected(MenuItem item) {
  60.         // Handle action bar item clicks here. The action bar will
  61.         // automatically handle clicks on the Home/Up button, so long
  62.         // as you specify a parent activity in AndroidManifest.xml.
  63.         int id = item.getItemId();
  64.         //noinspection SimplifiableIfStatement
  65.         if (id == R.id.action_settings) {
  66.             return true;
  67.         }
  68.         return super.onOptionsItemSelected(item);
  69.     }
  70.     private List<String> getData(){
  71.         List<String> data = new ArrayList<String>();
  72.         data.add("To see a world in a grain of sand,");
  73.         data.add("And a heaven in a wild flower,");
  74.         data.add("Hold infinity in the palm of your hand,");
  75.         data.add("And eternity in an hour.");
  76.         return data;
  77.     }
  78.     @Override
  79.     public void onRefresh() {
  80.         ExecutorService executorService = Executors.newSingleThreadExecutor();
  81.         executorService.execute(new Runnable() {
  82.             @Override
  83.             public void run() {
  84.                 try {
  85.                     Thread.sleep(2000);
  86.                     handler.sendEmptyMessage(1);
  87.                 } catch (InterruptedException e) {
  88.                     e.printStackTrace();
  89.                 }
  90.             }
  91.         });
  92.     }
  93.     @Override
  94.     public void onLoadMore() {
  95.         ExecutorService executorService = Executors.newSingleThreadExecutor();
  96.         executorService.execute(new Runnable() {
  97.             @Override
  98.             public void run() {
  99.                 try {
  100.                     Thread.sleep(2000);
  101.                     handler.sendEmptyMessage(2);
  102.                 } catch (InterruptedException e) {
  103.                     e.printStackTrace();
  104.                 }
  105.             }
  106.         });
  107.     }
  108. }
因為專案的類太多,就不貼出來了。有興趣的可以到我的部落格下載相應的例子。 分享下專案的 下載地址 例子支援 studio 和 eclipse 的版本,有興趣的就趕緊下載看看是不是你想要的吧!