android 高仿IOS水滴版上下拉重新整理的Listview
阿新 • • 發佈:2019-02-17
之前有分享一些重新整理的Demo,最近找到一個重新整理的例子,分享給大家。同時感謝原作者的分享!
現在給大家分享一個高仿IOS的Listview重新整理效果,支援上下拉重新整理。有些類似自定義的XListView 控制元件,與其不同的在於它重新整理的時候有類似水滴的下拉效果。
現在給大家分享一個高仿IOS的Listview重新整理效果,支援上下拉重新整理。有些類似自定義的XListView 控制元件,與其不同的在於它重新整理的時候有類似水滴的下拉效果。
給大家看看效果圖先:
下面是它定義動畫的類,
主要是在判斷距離的時候建立回彈動畫,程式碼有註釋可以瞅瞅先
- package medusa.theone.waterdroplistview.view;
- import android.animation.Animator;
- import android.animation.ValueAnimator;
- import android.annotation.SuppressLint;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.Path;
- import android.graphics.RectF;
- import android.graphics.drawable.Drawable;
- import android.util.AttributeSet;
- import android.view.View;
- import android.view.animation.DecelerateInterpolator;
- import medusa.theone.waterdroplistview.R;
- import medusa.theone.waterdroplistview.entity.Circle;
- import medusa.theone.waterdroplistview.utils.Utils;
- /**
- * 下拉頭中間的“水滴”
- */
- public class WaterDropView extends View {
- private Circle topCircle;
- private Circle bottomCircle;
- private Paint mPaint;
- private Path mPath;
- private float mMaxCircleRadius;//圓半徑最大值
- private float mMinCircleRaidus;//圓半徑最小值
- private Bitmap arrowBitmap;//箭頭
- private final static int BACK_ANIM_DURATION = 180;
- private final static float STROKE_WIDTH = 2;//邊線寬度
- public WaterDropView(Context context) {
- super(context);
- init(context, null);
- }
- public WaterDropView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
- public WaterDropView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context, attrs);
- }
- private void parseAttrs(Context context, AttributeSet attrs) {
- if (attrs != null) {
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaterDropView, 0, 0);
- try {
- /*if (a.hasValue(R.styleable.WaterDropView_topcircle_x)) {
- topCircle.setX(a.getDimensionPixelSize(R.styleable.WaterDropView_topcircle_x, 0));
- }
- if (a.hasValue(R.styleable.WaterDropView_topcircle_y)) {
- topCircle.setY(a.getDimensionPixelSize(R.styleable.WaterDropView_topcircle_y, 0));
- }
- if (a.hasValue(R.styleable.WaterDropView_bottomcircle_x)) {
- bottomCircle.setX(a.getDimensionPixelSize(R.styleable.WaterDropView_topcircle_x, 0));
- }
- if (a.hasValue(R.styleable.WaterDropView_bottomcircle_y)) {
- bottomCircle.setY(a.getDimensionPixelSize(R.styleable.WaterDropView_topcircle_y, 0));
- }*/
- if(a.hasValue(R.styleable.WaterDropView_waterdrop_color)){
- int waterDropColor = a.getColor(R.styleable.WaterDropView_waterdrop_color, Color.GRAY);
- mPaint.setColor(waterDropColor);
- }
- if (a.hasValue(R.styleable.WaterDropView_max_circle_radius)) {
- mMaxCircleRadius = a.getDimensionPixelSize(R.styleable.WaterDropView_max_circle_radius, 0);
- topCircle.setRadius(mMaxCircleRadius);
- bottomCircle.setRadius(mMaxCircleRadius);
- topCircle.setX(STROKE_WIDTH + mMaxCircleRadius);
- topCircle.setY(STROKE_WIDTH + mMaxCircleRadius);
- bottomCircle.setX(STROKE_WIDTH + mMaxCircleRadius);
- bottomCircle.setY(STROKE_WIDTH + mMaxCircleRadius);
- }
- if (a.hasValue(R.styleable.WaterDropView_min_circle_radius)) {
- mMinCircleRaidus = a.getDimensionPixelSize(R.styleable.WaterDropView_min_circle_radius, 0);
- if (mMinCircleRaidus > mMaxCircleRadius) {
- throw new IllegalStateException("Circle's MinRaidus should be equal or lesser than the MaxRadius");
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- a.recycle();
- }
- }
- }
- private void init(Context context, AttributeSet attrs) {
- topCircle = new Circle();
- bottomCircle = new Circle();
- mPath = new Path();
- mPaint = new Paint();
- mPaint.setColor(Color.GRAY);
- mPaint.setAntiAlias(true);
- mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- mPaint.setStrokeWidth(STROKE_WIDTH);
- @SuppressWarnings("deprecation")
- Drawable drawable = getResources().getDrawable(R.drawable.refresh_arrow);
- arrowBitmap = Utils.drawableToBitmap(drawable);
- parseAttrs(context, attrs);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- //寬度:上圓和下圓的最大直徑
- int width = (int) ((mMaxCircleRadius + STROKE_WIDTH) * 2);
- //高度:上圓半徑 + 圓心距 + 下圓半徑
- int height = (int) Math.ceil(bottomCircle.getY()+bottomCircle.getRadius() + STROKE_WIDTH * 2);
- setMeasuredDimension(width, height);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- makeBezierPath();
- // mPaint.setColor(Color.RED);
- // mPaint.setAlpha(200);
- canvas.drawPath(mPath, mPaint);
- // mPaint.setColor(Color.GRAY);
- // mPaint.setAlpha(50);
- canvas.drawCircle(topCircle.getX(), topCircle.getY(), topCircle.getRadius(), mPaint);
- canvas.drawCircle(bottomCircle.getX(), bottomCircle.getY(), bottomCircle.getRadius(), mPaint);
- // canvas.drawBitmap(arrowBitmap, topCircle.getX() - topCircle.getRadius(), topCircle.getY() - topCircle.getRadius(), mPaint);
- 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());
- canvas.drawBitmap(arrowBitmap,null,bitmapArea,mPaint);
- super.onDraw(canvas);
- }
- private void makeBezierPath() {
- mPath.reset();
- //獲取兩圓的兩個切線形成的四個切點
- double angle = getAngle();
- float top_x1 = (float) (topCircle.getX() - topCircle.getRadius() * Math.cos(angle));
- float top_y1 = (float) (topCircle.getY() + topCircle.getRadius() * Math.sin(angle));
- float top_x2 = (float) (topCircle.getX() + topCircle.getRadius() * Math.cos(angle));
- float top_y2 = top_y1;
- float bottom_x1 = (float) (bottomCircle.getX() - bottomCircle.getRadius() * Math.cos(angle));
- float bottom_y1 = (float) (bottomCircle.getY() + bottomCircle.getRadius() * Math.sin(angle));
- float bottom_x2 = (float) (bottomCircle.getX() + bottomCircle.getRadius() * Math.cos(angle));
- float bottom_y2 = bottom_y1;
- mPath.moveTo(topCircle.getX(), topCircle.getY());
- mPath.lineTo(top_x1, top_y1);
- mPath.quadTo((bottomCircle.getX() - bottomCircle.getRadius()),
- (bottomCircle.getY() + topCircle.getY()) / 2,
- bottom_x1,
- bottom_y1);
- mPath.lineTo(bottom_x2, bottom_y2);
- mPath.quadTo((bottomCircle.getX() + bottomCircle.getRadius()),
- (bottomCircle.getY() + top_y2) / 2,
- top_x2,
- top_y2);
- mPath.close();
- }
- /**
- * 獲得兩個圓切線與圓心連線的夾角
- *
- * @return
- */
- private double getAngle() {
- if (bottomCircle.getRadius() > topCircle.getRadius()) {
- throw new IllegalStateException("bottomCircle's radius must be less than the topCircle's");
- }
- return Math.asin((topCircle.getRadius() - bottomCircle.getRadius()) / (bottomCircle.getY() - topCircle.getY()));
- }
- /**
- * 建立回彈動畫
- * 上圓半徑減速恢復至最大半徑
- * 下圓半徑減速恢復至最大半徑
- * 圓心距減速從最大值減到0(下圓Y從當前位置移動到上圓Y)。
- *
- * @return
- */
- @SuppressLint("NewApi")
- public Animator createAnimator() {
- ValueAnimator valueAnimator = ValueAnimator.ofFloat(1, 0).setDuration(BACK_ANIM_DURATION);
- valueAnimator.setInterpolator(new DecelerateInterpolator());
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- updateComleteState((Float) valueAnimator.getAnimatedValue());
- }
- });
- return valueAnimator;
- }
- /**
- * 完成的百分比
- *
- * @param percent between[0,1]
- */
- public void updateComleteState(float percent) {
- if (percent < 0 || percent > 1) {
- throw new IllegalStateException("completion percent should between 0 and 1!");
- }
- float top_r = (float) (mMaxCircleRadius - 0.25 * percent * mMaxCircleRadius);
- float bottom_r = (mMinCircleRaidus - mMaxCircleRadius) * percent + mMaxCircleRadius;
- float bottomCricleOffset = 2 * percent * mMaxCircleRadius;
- topCircle.setRadius(top_r);
- bottomCircle.setRadius(bottom_r);
- bottomCircle.setY(topCircle.getY() + bottomCricleOffset);
- requestLayout();
- postInvalidate();
- }
- public Circle getTopCircle() {
- return topCircle;
- }
- public Circle getBottomCircle() {
- return bottomCircle;
- }
- public void setIndicatorColor(int color) {
- mPaint.setColor(color);
- }
- public int getIndicatorColor() {
- return mPaint.getColor();
- }
- }
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/black"
- tools:context="medusa.theone.waterdroplistview.activity.MainActivity" >
- <medusa.theone.waterdroplistview.view.WaterDropListView
- android:id="@+id/waterdrop_listview"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:cacheColorHint="#00000000"
- android:divider="@null"
- android:listSelector="@android:color/transparent" >
- </medusa.theone.waterdroplistview.view.WaterDropListView>
- </RelativeLayout>
- package medusa.theone.waterdroplistview.activity;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import medusa.theone.waterdroplistview.R;
- import medusa.theone.waterdroplistview.view.WaterDropListView;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.view.Menu;
- import android.view.MenuItem;
- import android.widget.ArrayAdapter;
- /**
- * 主頁面
- *
- * @author seven2729
- * @Blog http://blog.csdn.net/seven2729
- * @version v1.0
- * @copyright 2010-2015
- * @create-time 2015年5月14日 下午2:19:56
- *
- */
- public class MainActivity extends Activity implements WaterDropListView.IWaterDropListViewListener {
- private WaterDropListView waterDropListView;
- @SuppressLint("HandlerLeak")
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what){
- case 1:
- waterDropListView.stopRefresh();
- break;
- case 2:
- waterDropListView.stopLoadMore();
- break;
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- waterDropListView = (WaterDropListView) findViewById(R.id.waterdrop_listview);
- waterDropListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, getData()));
- waterDropListView.setWaterDropListViewListener(this);
- waterDropListView.setPullLoadEnable(true);
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_second, menu);
- return true;
- }
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- int id = item.getItemId();
- //noinspection SimplifiableIfStatement
- if (id == R.id.action_settings) {
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
- private List<String> getData(){
- List<String> data = new ArrayList<String>();
- data.add("To see a world in a grain of sand,");
- data.add("And a heaven in a wild flower,");
- data.add("Hold infinity in the palm of your hand,");
- data.add("And eternity in an hour.");
- return data;
- }
- @Override
- public void onRefresh() {
- ExecutorService executorService = Executors.newSingleThreadExecutor();
- executorService.execute(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(2000);
- handler.sendEmptyMessage(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- }
- @Override
- public void onLoadMore() {
- ExecutorService executorService = Executors.newSingleThreadExecutor();
- executorService.execute(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(2000);
- handler.sendEmptyMessage(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- }
- }