Android-自定義控件之ListView下拉刷新的實(shí)現(xiàn)
自定義控件學(xué)了很久了,發(fā)現(xiàn)學(xué)了總是忘,于是打算用博客來(lái)記錄自己學(xué)習(xí)的知識(shí)點(diǎn)。
今天是自定義ListView來(lái)實(shí)現(xiàn)下拉刷新,這些文章都是借鑒慕課網(wǎng)上的視頻來(lái)寫(xiě)的.
自定義一個(gè)控件,先是看它繼承于那個(gè)控件,如果我們繼承View控件的話,那得讓我們寫(xiě)很多關(guān)于ListView的功能,這些東西我自己覺(jué)得很麻煩,而且也沒(méi)有那個(gè)必要因?yàn)槲覀兛梢灾苯永^承ListView,在listView的基礎(chǔ)上來(lái)加一些我們需要的東西。
1.向ListView加Header布局
private void initView(Context context)
{
mLayoutInflater = LayoutInflater.from(context);
mHeaerView = mLayoutInflater.inflate(R.layout.header_layout, null, false);
addHeaderView(mHeaerView);
}
2.隱藏Header布局
private void initView(Context context) {
mLayoutInflater = LayoutInflater.from(context);
mHeaerView = mLayoutInflater.inflate(R.layout.header_layout, null, false);
measureView(mHeaerView);
mHeaderViewHeight = mHeaerView.getMeasuredHeight();
setHeaderViewHeightPadding(mHeaderViewHeight);
Log.i("main", mHeaderViewHeight + "");
addHeaderView(mHeaerView);
}
private void measureView(View view)
{
ViewGroup.LayoutParams lp = view.getLayoutParams();
if(lp == null)
{
lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
//mHeaerView.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
/**
* width 和height里面包含的不僅僅有View的寬和高,還有View控件的測(cè)量模式
* 測(cè)量模式的產(chǎn)生方式就是如下所示
*/
int width = ViewGroup.getChildMeasureSpec(0,0,lp.width);
int height = 0;
int tempHeight = lp.height;
if(tempHeight > 0)
{
height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
}
view.measure(width, height);
}
private void setHeaderViewHeightPadding(int padding) {
mHeaerView.setPadding(mHeaerView.getPaddingLeft(), -padding, mHeaerView.getPaddingRight(), mHeaerView.getPaddingBottom());
mHeaerView.invalidate();
}
3.實(shí)現(xiàn)ListView的下拉刷新(一)
要想實(shí)現(xiàn)ListView的下拉刷新,必須監(jiān)聽(tīng)ListView是否滑動(dòng)到最頂端,因此要實(shí)現(xiàn)ListView的監(jiān)聽(tīng)接口OnScrollListener,并且要監(jiān)聽(tīng)ListView的OnTouch事件。根據(jù)滑動(dòng)的情況來(lái)判斷刷新的情況。
首先我們?cè)诙x了一個(gè)成員變量來(lái)保存ListView的狀態(tài)--mState
其次定義了幾個(gè)靜態(tài)常量來(lái)表示不同的狀態(tài)
private final static int NONE = 0; // 無(wú)狀態(tài) private final static int DOWN_UPDATE = 1; // 提示下拉可以刷新 private final static int UPDATE = 2; // 提示松開(kāi)可以刷新 private final static int REFLASH = 3; // 更新
最后則是根據(jù)不同的滑動(dòng)來(lái)更改mState的狀態(tài)
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (mFirstVisibleItem == 0) {
mIsRemark = true; // mIsRemark只是一個(gè)標(biāo)記,表示當(dāng)前可見(jiàn)的第一個(gè)Item是不是所有的Item中的第一個(gè)
mStartY = (int) ev.getY();
Log.i("main", "我進(jìn)來(lái)了");
}
break;
}
case MotionEvent.ACTION_MOVE: {
onMove(ev);
tempY = (int) (ev.getY() - mStartY);
Log.i("main", "tempY = " + tempY);
break;
}
case MotionEvent.ACTION_UP: {
if(mState == DOWN_UPDATE)
{
mState = NONE;
}
if(mState == UPDATE)
{
mState = REFLASH;
mListener.reFlash();
Log.i("main", "我來(lái)了");
}
Log.i("main", "tempY11 = " + tempY);
if(tempY <= 0 && mIsRemark)
{
Log.i("main", "我進(jìn)來(lái)le");
mState = NONE;
}
change();
break;
}
}
return super.onTouchEvent(ev);
}
private void onMove(MotionEvent ev) {
if (mIsRemark) {
if (ev.getY() - mStartY > 0) {
int dy = (int) (ev.getY() - mStartY);
if (dy > mHeaderViewHeight + 20) {
mState = UPDATE;
} else {
mState = DOWN_UPDATE;
}
setHeaderViewHeightPadding(mHeaderViewHeight - dy);
change();
}
return;
}
return;
}
/**
*change方法主要是用來(lái)處理不同狀態(tài)下的事件
*
*/
private void change() {
initChildView();
RotateAnimation ani = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
ani.setDuration(500);
ani.setFillAfter(true);
RotateAnimation ani1 = new RotateAnimation(180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
ani1.setDuration(500);
ani1.setFillAfter(true);
if (mState == UPDATE)
{
mProgressBar.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
mImageView.clearAnimation();
mImageView.setAnimation(ani);
mTextViewFlash.setText("松開(kāi)可以刷新!");
mTextViewTime.setVisibility(View.VISIBLE);
mTextViewTime.setText("上次更新的時(shí)間:" + mUpdateTime);
}
if (mState == DOWN_UPDATE)
{
mProgressBar.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
mTextViewTime.setVisibility(View.VISIBLE);
mImageView.clearAnimation();
mImageView.setAnimation(ani1);
mTextViewFlash.setText("下拉可以刷新");
mTextViewTime.setText("上次更新的時(shí)間:" + mUpdateTime);
}
if (mState == REFLASH)
{
setHeaderViewHeightPadding(10);
mProgressBar.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.GONE);
mTextViewTime.setVisibility(View.GONE);
mTextViewFlash.setText("正在刷新...");
mImageView.clearAnimation();
}
if(mState == NONE)
{
Log.i("main", "workspace");
setHeaderViewHeightPadding(mHeaderViewHeight);
mIsRemark = false;
mProgressBar.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
mImageView.setAnimation(ani1);
}
}
private void initChildView()
{
if(mTextViewFlash == null)
{
mTextViewFlash = (TextView) mHeaerView.findViewById(R.id.id_textView_Flash);
}
if(mTextViewTime == null)
{
mTextViewTime = (TextView) mHeaerView.findViewById(R.id.id_textView_Time);
}
if(mImageView == null)
{
mImageView = (ImageView) mHeaerView.findViewById(R.id.id_imagView);
}
if(mProgressBar == null)
{
mProgressBar = (ProgressBar) mHeaerView.findViewById(R.id.id_progressbar);
}
}
4.實(shí)現(xiàn)ListView的下拉刷新(二)
經(jīng)過(guò)上面的過(guò)程,是可以下拉的,處理不同狀態(tài)下的事件。還有一個(gè)問(wèn)題就是刷新,也就是加載新的數(shù)據(jù)。加載刷新的操作肯定必須在UI線程中,因此ListView中必須得有一個(gè)回調(diào)接口,用來(lái)MinaActivity來(lái)實(shí)現(xiàn),并且來(lái)進(jìn)行一些操作。
回調(diào)接口:
public void setOnFlashListener(FlashListener listener)
{
this.mListener = listener;
}
public interface FlashListener
{
void reFlash();
}
回調(diào)接口的調(diào)用:
if(mState == UPDATE)
{
mState = REFLASH;
mListener.reFlash();
Log.i("main", "我來(lái)了");
}
MainActivity中回調(diào)接口的實(shí)現(xiàn)和接口方法的實(shí)現(xiàn):
mListView.setOnFlashListener(new FlashListView.FlashListener() {
@Override
public void reFlash() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
addDatas();
loadDatas();
mListView.reFalshComplete();
}
}, 5000);
}
});
private void addDatas()
{
int i = mDatas.size();
for(int j = i; j < i + 10; j++)
{
mDatas.add(new Bean("Title" + j, "Content" + j, R.mipmap.ic_launcher));
}
myAdapter.dataChange(mDatas);
}
private void loadDatas()
{
mListView.setAdapter(myAdapter);
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android Camera實(shí)現(xiàn)毫秒級(jí)拍照實(shí)例
本篇文章主要介紹了Android Camera實(shí)現(xiàn)毫秒級(jí)拍照實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-06-06
Android?Studio開(kāi)發(fā)實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了Android?Studio開(kāi)發(fā)實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
Android 屬性動(dòng)畫(huà)ValueAnimator與插值器詳解
這篇文章主要介紹了Android 屬性動(dòng)畫(huà)ValueAnimator與插值器詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05
深入了解Android Okio的超時(shí)機(jī)制
Okio是一個(gè)IO庫(kù),底層基于Java原生的輸入輸出流實(shí)現(xiàn)。但原生的輸入輸出流并沒(méi)有提供超時(shí)的檢測(cè)機(jī)制。而Okio實(shí)現(xiàn)了這個(gè)功能,本文就來(lái)為大家詳細(xì)講講2023-02-02
Android RecyclerView自由拖動(dòng)item的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android RecyclerView自由拖動(dòng)item的實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01
Android實(shí)現(xiàn)短信發(fā)送功能
這篇文章主要介紹了Android實(shí)現(xiàn)短信發(fā)送功能,對(duì)Android實(shí)現(xiàn)短信發(fā)送的每一步都進(jìn)行了詳細(xì)的介紹,感興趣的小伙伴們可以參考一下2015-12-12
android popuwindow點(diǎn)擊外部窗口不消失的實(shí)例
下面小編就為大家?guī)?lái)一篇android popuwindow點(diǎn)擊外部窗口不消失的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
Android獲取當(dāng)前已連接的wifi信號(hào)強(qiáng)度的方法
這篇文章主要介紹了Android獲取當(dāng)前已連接的wifi信號(hào)強(qiáng)度的方法,主要通過(guò)系統(tǒng)自帶的WifiInfo類實(shí)現(xiàn),需要的朋友可以參考下2014-09-09
Android使用OKHTTP解析JSON數(shù)據(jù)的實(shí)例代碼
本篇文章主要介紹了Android使用OKHTTP解析JSON數(shù)據(jù)的實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
詳解Android使用Gradle統(tǒng)一配置依賴管理
本篇文章主要介紹了詳解Android 使用 Gradle 統(tǒng)一配置依賴管理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01

