Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android實現簡單的下拉刷新pulltorefresh

Android實現簡單的下拉刷新pulltorefresh

編輯:關於Android編程

網上下拉刷新的DEMO很多,但是總有各種不滿意的地方,有些會下拉卡住,有些回彈不流暢,有些性能太低會各種卡頓,有些emptyView無法下拉...... 

自己寫的才是最合適自己的,代碼很簡單,也很容易修改,稍微閱讀下代碼就能改出自己需要的各種效果。

首先,重寫ListView,自定義Touch事件,為了使emptyView也可下拉,emptyView也加上Touch事件。 如果要實現GridView,把這裡的ListView改成GridView即可。

PullableListView :

public class PullableListView extends ListView {
  private boolean inited;
  private float density;
  private int mDownY, mMoveY;
  private int mPullY;
  private boolean isPull;
  private PullListener mPullListener;
  private VelocityTracker mVelocityTracker;

  public interface PullListener {

    public boolean onPullDownStart();

    public void onPullDown(int moveY);

    public void onPullDownDrop();
  }

  public PullableListView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
  }

  public PullableListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  public PullableListView(Context context) {
    super(context);
    init();
  }

  private void init() {
    if (!inited) {
      density = getResources().getDisplayMetrics().density;
    }
  }

  public void setPullListener(PullListener mPullListener) {
    this.mPullListener = mPullListener;
  }

  public boolean isPulling() {
    return isPull;
  }

  @Override
  public void setEmptyView(View emptyView) {
    super.setEmptyView(emptyView);
    // 重寫emptyView的Touch事件,使顯示emptyView時也可以下拉刷新
    emptyView.setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent ev) {
        if (mVelocityTracker == null) {
          mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);
        switch (ev.getAction()) {
          case MotionEvent.ACTION_DOWN:
            mDownY = (int) ev.getY();
            break;
          case MotionEvent.ACTION_MOVE:
            mMoveY = (int) ev.getY();
            if (!isPull) {
              mVelocityTracker.computeCurrentVelocity(1000, 8000f);
              if (mVelocityTracker.getYVelocity() > 500 // 下拉速度大於500
                  && Math.abs(mMoveY - mDownY) > 20 * density) { // 下拉距離超過20dp
                mPullY = mMoveY;
                if (mPullListener.onPullDownStart()) {
                  isPull = true;
                }
              }
            } else {
              // 阻尼下拉(隨著下拉距離增加,阻力增加)
              mPullListener.onPullDown(mMoveY - mPullY + v.getScrollY());
              // 等阻力下拉(阻力恆定,不隨下拉距離增加而增加)
              // mPullListener.onPullDown(mMoveY - mPullY);
              if (mMoveY < mPullY) {
                isPull = false;
              }
              return true;
            }
            break;
          case MotionEvent.ACTION_UP:
            if (mVelocityTracker != null) {
              mVelocityTracker.clear();
              mVelocityTracker.recycle();
              mVelocityTracker = null;
            }
            if (isPull) {
              mPullY = 0;
              isPull = false;
              mPullListener.onPullDownDrop();
              return true;
            }
            break;
        }
        return true;
      }
    });
  }

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (isPull) {
      // 正在下拉時,阻住Touch事件向下傳遞,同時會向各個ChildView發送ACTION_CANLE事件,
      // 使之前捕捉到了ACTION_DOWN事件的ChildView回復到正常狀態
      return true;
    }

    return super.onInterceptTouchEvent(ev);
  }

  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(ev);
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        mDownY = (int) ev.getY();
        break;
      case MotionEvent.ACTION_MOVE:
        mMoveY = (int) ev.getY();
        if (!isPull) {
          if (getFirstVisiblePosition() == 0) {
            View view = getChildAt(0);
            mVelocityTracker.computeCurrentVelocity(1000, 8000f);
            if (mVelocityTracker.getYVelocity() > 500// 下拉速度大於500
                && (view == null || view.getTop() == getPaddingTop()) // 已拉動到頂部
                && Math.abs(mMoveY - mDownY) > 15 * density) { // 下拉距離超過20dp
              mPullY = mMoveY;
              if (mPullListener.onPullDownStart()) {
                // 根據返回值確認是否進入下拉狀態
                isPull = true;
              }
            }
          }
        } else {
          // 阻尼下拉(隨著下拉距離增加,阻力增加)
          mPullListener.onPullDown(mMoveY - mPullY);
          // 等阻力下拉(阻力恆定,不隨下拉距離增加而增加)
          // mPullListener.onPullDown(mMoveY - mPullY - getScrollY());
          if (mMoveY < mPullY) {
            isPull = false;
          }
          return true;
        }
        break;
      case MotionEvent.ACTION_UP:
        if (mVelocityTracker != null) {
          mVelocityTracker.clear();
          mVelocityTracker.recycle();
          mVelocityTracker = null;
        }
        if (isPull) {
          mPullY = 0;
          isPull = false;
          mPullListener.onPullDownDrop();
          return true;
        }
        break;
      case MotionEvent.ACTION_CANCEL:
        break;
    }
    return super.onTouchEvent(ev);
  }
}

然後是外層的LinearyLayer,監聽PullableListView的下拉回調,實現下拉效果。同時提供ListView(GridView)的外部接口,如 setEmptyView(View view),setAdapter(ListAdapter adapter)...等等,這裡只提供部分我需要使用的,可以根據自身需求去提供外部接口。 
代碼中R.drawable.pulltorefresh 和 R.drawable.loading 分別是下拉箭頭 和 刷新滾動條 的圖片,這裡不提供了,自己隨意找兩張圖片貼上就行了。 

PullToRefreshView: 

public class PullToRefreshView extends LinearLayout {

  protected static final String TAG = "PullToRefreshView";

  /**
   * 下拉阻力系數
   */
  private static final float SCALL_PULL_DOWW = 2.0f;

  private View mView;

  private PullableListView mListView;

  private TextView mPullTv;

  private ImageView mProgressBar;

  private View mPullV;

  private View mEmptyView;

  private boolean isInited;

  private boolean canRefresh;

  private boolean isRefreshing;

  private boolean isPullable = true;

  private int mOrMargin;

  private ObjectAnimator mArrowRotateAnimator;

  private Animation mProAnimation;

  private PullToRefreshListener mPullToRefreshListener;

  public PullToRefreshView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initView(context);
  }

  public PullToRefreshView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
  }

  public PullToRefreshView(Context context) {
    super(context);
    initView(context);
  }

  public interface PullToRefreshListener {
    /**
     * do data refresh here
     */
    public void onRefreshStart();

    /**
     * do view update here
     */
    public void onRefreshFinished();
  }

  private void initView(Context context) {
    if (!isInited) {
      isInited = true;
      mView = LayoutInflater.from(context).inflate(R.layout.view_pulltorefresh, null);
      mProgressBar = (ImageView) mView.findViewById(R.id.iv_pulltorefresh_arrow);
      mProgressBar.setImageResource(R.drawable.pulltorefresh);
      mPullTv = (TextView) mView.findViewById(R.id.tv_pulltorefresh);
      mPullV = mView.findViewById(R.id.ly_pulltorefresh_pull);
      mListView = (PullableListView) mView.findViewById(R.id.gv_smarturc_urcs);
      mListView.setPullListener(mPullListener);
      LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
          LayoutParams.MATCH_PARENT);
      addView(mView, lp);
      LayoutParams lParams = (LayoutParams) mPullV.getLayoutParams();
      mOrMargin = lParams.topMargin;
      mProAnimation = AnimationUtils.loadAnimation(getContext(),
          R.anim.anim_progressbar);
    }
  }

  private PullListener mPullListener = new PullListener() {

    @Override
    public boolean onPullDownStart() {
      if (isRefreshing || !isPullable) {
        return false;
      }
      mPullTv.setText("下拉刷新");
      mProgressBar.setRotation(0f);
      mProgressBar.setImageResource(R.drawable.pulltorefresh);
      if (mProgressBar.getAnimation() != null) {
        mProgressBar.clearAnimation();
      }
      return true;
    }

    @Override
    public void onPullDown(int moveY) {
      if (isRefreshing || !isPullable) {
        return;
      }
      moveY = (int) Math.max(0, moveY / SCALL_PULL_DOWW);
      mView.scrollTo(0, -moveY);
      mEmptyView.scrollTo(0, -moveY);
      if (!canRefresh
          && Math.abs(mView.getScrollY()) > Math.abs(mOrMargin)) {
        mPullTv.setText("松開刷新");
        canRefresh = true;
        if (mArrowRotateAnimator != null) {
          mArrowRotateAnimator.cancel();
        }
        float rotation = mProgressBar.getRotation();
        mArrowRotateAnimator = ObjectAnimator.ofFloat(mProgressBar, "rotation",
            rotation, 180f);
        mArrowRotateAnimator.setDuration(100).start();
      } else if (canRefresh
          && Math.abs(mView.getScrollY()) <= Math.abs(mOrMargin)) {
        mPullTv.setText("下拉刷新");
        canRefresh = false;
        if (mArrowRotateAnimator != null) {
          mArrowRotateAnimator.cancel();
        }
        float rotation = mProgressBar.getRotation();
        mArrowRotateAnimator = ObjectAnimator.ofFloat(mProgressBar, "rotation",
            rotation, 0f);
        mArrowRotateAnimator.setDuration(100).start();
      }
    }

    @Override
    public void onPullDownDrop() {
      if (canRefresh) {
        setRefreshing();
      } else {
        isRefreshing = false;
        backTo(mView.getScrollY(), 0);
      }
    }
  };

  private void backTo(final int from, final int to) {
    ObjectAnimator.ofInt(mView, "scrollY", from, to).setDuration(300)
        .start();
    ObjectAnimator.ofInt(mEmptyView, "scrollY", from, to).setDuration(300)
        .start();
  }

  /**
   * 設置為正在刷新狀態
   */
  public void setRefreshing() {
    isRefreshing = true;
    mProgressBar.setImageResource(R.drawable.loading);
    mProgressBar.startAnimation(mProAnimation);
    mPullTv.setText("正在刷新");
    backTo(mView.getScrollY(), mOrMargin);
    if (mPullToRefreshListener != null) {
      mPullToRefreshListener.onRefreshStart();
    }
  }

  /**
   * 刷新完成
   */
  public void setRrefreshFinish() {
    if (isRefreshing) {
      isRefreshing = false;
      backTo(mView.getScrollY(), 0);
    }
    if (mPullToRefreshListener != null) {
      mPullToRefreshListener.onRefreshFinished();
    }
  }

  public void setPullable(boolean pullable) {
    isPullable = pullable;
  }

  public void setPullToRefreshListener(
      PullToRefreshListener mPullToRefreshListener) {
    this.mPullToRefreshListener = mPullToRefreshListener;
  }

  public void setAdapter(ListAdapter adapter) {
    mListView.setAdapter(adapter);
  }

  public void setEmptyView(View emptyView) {
    mListView.setEmptyView(emptyView);
    this.mEmptyView = emptyView;
  }

  public void setOnItemClickListener(OnItemClickListener itemClickListener) {
    mListView.setOnItemClickListener(itemClickListener);
  }

  public void setOnItemLongClickListener(OnItemLongClickListener itemLongClickListener) {
    mListView.setOnItemLongClickListener(itemLongClickListener);
  }
}

layout-view_pulltorefresh: 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#cccccc"
  android:orientation="vertical" >

  <LinearLayout
    android:id="@+id/ly_pulltorefresh_pull"
    android:layout_width="wrap_content"
    android:layout_height="48dp"
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="-48dp" >

    <ImageView
      android:id="@+id/iv_pulltorefresh_arrow"
      android:layout_width="20dp"
      android:layout_height="match_parent"
      android:scaleType="fitCenter"
      android:src="@drawable/pulltorefresh" />

    <TextView
      android:id="@+id/tv_pulltorefresh"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:layout_marginBottom="4dp"
      android:layout_marginLeft="8dp"
      android:gravity="center"
      android:textColor="@android:color/white"
      android:textSize="16sp" />
  </LinearLayout>

  <com.example.pulltorefresh.PullableListView
    android:id="@+id/gv_smarturc_urcs"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:overScrollMode="never"
    android:scrollingCache="false" >
  </com.example.pulltorefresh.PullableListView>

</LinearLayout>

anim-anim_progressbar: 

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
  android:fromDegrees="0"
  android:toDegrees="360"
  android:pivotX="50%"
  android:pivotY="50%"
  android:repeatCount="infinite"
  android:repeatMode="restart"
  android:duration="800"
  android:interpolator="@android:anim/linear_interpolator"/>

最後是DEMO ACTIVITY: 

public class PullToRefreshActivity extends Activity {

  private PullToRefreshView mPullToRefreshView;
  private List<String> data = new ArrayList<String>();
  private MyAdapter mAdapter;
  private Handler mHandler;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pulltorefresh);
    mHandler = new Handler();
    mPullToRefreshView = (PullToRefreshView) findViewById(R.id.pullToRefreshView1);
    mAdapter = new MyAdapter();
    mPullToRefreshView.setAdapter(mAdapter);
    mPullToRefreshView.setEmptyView(findViewById(R.id.empty));
    mPullToRefreshView.setOnItemLongClickListener(new OnItemLongClickListener() {
      @Override
      public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(getApplicationContext(), "Long click : " + data.get(position),
            Toast.LENGTH_SHORT).show();
        return true;
      }
    });
    mPullToRefreshView.setOnItemClickListener(new OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(getApplicationContext(), data.get(position), Toast.LENGTH_SHORT)
            .show();
      }
    });
    mPullToRefreshView.setPullToRefreshListener(new PullToRefreshListener() {
      @Override
      public void onRefreshStart() {
        // 模擬刷新數據
        mHandler.postDelayed(new Runnable() {
          @Override
          public void run() {
            data.add(String.valueOf((int) (Math.random() * 1000)));
            mPullToRefreshView.setRrefreshFinish();
          }
        }, 2000);
      }

      @Override
      public void onRefreshFinished() {
        // 更新視圖
        mAdapter.notifyDataSetChanged();
      }
    });
//    mHandler.postDelayed(new Runnable() {
//      @Override
//      public void run() {
//        // TODO Auto-generated method stub
//        mPullToRefreshView.setRefreshing();
//      }
//    }, 500);
  }

  public class MyAdapter extends BaseAdapter {

    @Override
    public int getCount() {
      // TODO Auto-generated method stub
      return data.size();
    }

    @Override
    public Object getItem(int position) {
      // TODO Auto-generated method stub
      return data.get(position);
    }

    @Override
    public long getItemId(int position) {
      // TODO Auto-generated method stub
      return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      // TODO Auto-generated method stub
      if (convertView == null) {
        convertView = new TextView(PullToRefreshActivity.this);
      }
      TextView textView = (TextView) convertView;
      textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40f);
      textView.setPadding(30, 30, 30, 30);
      textView.setText(data.get(position));
      return convertView;
    }

  }
}

layout-activity_pulltorefresh: 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/container"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >

  <com.example.pulltorefresh.PullToRefreshView
    android:id="@+id/pullToRefreshView1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true" >
  </com.example.pulltorefresh.PullToRefreshView>

  <LinearLayout
    android:id="@+id/empty"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:padding="60dp" >

    <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/ic_launcher" />

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="NO DATA" />
  </LinearLayout>

</RelativeLayout>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved