国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android RefreshLayout實現下拉刷新布局

2019-12-12 04:55:13
字體:
來源:轉載
供稿:網友

項目中需要下拉刷新的功能,但是這個View不是ListView這類的控件,需要ViewGroup實現這個功能,一開始網上大略找了一下,沒發現特別合適的,代碼也是沒怎么看懂,所以決定還是自己寫一個。

  于是翻出XlistView的源碼看是一點一點看,再大致理解了XLisview源碼,終于決定自己動手啦

  為了省事,headView還是用了XListView的HeadView,省了很多事:)

  下拉刷新,下拉刷新,肯定是先實現下拉功能,最開始我是打算通過 extends ScrollView 來實現,因為有現成的滾動效果嘛,可是實際因為兩個原因放棄了:

1、ScrollView下只能有一個子控件View ,雖然在    Scroll下添加一個ViewGroup,然后講headView動態添加進前面的ViewGroup,但是我還是比較習慣studio的可視化預覽,總覺得不直觀!

2、  ScrollView內嵌ListView時會發生    沖突,還需要去重寫ListView。于是放棄換個思路!

 關于上面的原因1:動態添加headView進ScrollView的中GroupView中,可以在重寫ScrollView的onViewAdded()方法,將初始化時解析的headView添加進子GroupView

@Override public void onViewAdded(View child) {   super.onViewAdded(child);   //因為headView要在最上面,最先想到的就是Vertical的LinearLayout   LinearLayout linearLayout = (LinearLayout) getChildAt(0);   linearLayout.addView(view, 0); } 

  換個思路,通過extends LinearLayout來實現吧!
先做準備工作,我們需要一個HeaderView以及要獲取到HeaderView的高度,還有初始時Layout的高度

private void initView(Context context) {    mHeaderView = new SRefreshHeader(context);    mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.slistview_header_content);    setOrientation(VERTICAL);    addView(mHeaderView, 0);    getHeaderViewHeight();    getViewHeight();  } 

mHeaderView = new SRefreshHeader(context);
通過構造方法實例化HeaderView

mHeaderViewContent = (RelativeLayout)

mHeaderView.findViewById(R.id.slistview_header_content);

 這是解析headerView內容區域iew,等會兒要獲取這個view的高度,你肯定會問為啥不用上面的mHeaderView來獲取高度,點進構造方法里可以看到如下代碼

// 初始情況,設置下拉刷新view高度為0 LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0); mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.listview_head_view_layout, null); w(mContainer, lp); 

如果直接獲取mHeaderView的高度 那肯定是0
getHeaderViewHeight();
getViewHeight();

分別是獲取HeaderView的高度和Layout的初始高度

/**   * 獲取headView高度   */   private void getHeaderViewHeight() {     ViewTreeObserver vto2 = mHeaderViewContent.getViewTreeObserver();     vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {       @Override       public void onGlobalLayout() {         mHeaderViewHeight = mHeaderViewContent.getHeight();         mHeaderViewContent.getViewTreeObserver().removeGlobalOnLayoutListener(this);       }     });   }    /**   * 獲取SRefreshLayout當前實例的高度   */   private void getViewHeight() {     ViewTreeObserver thisView = getViewTreeObserver();     thisView.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {       @Override       public void onGlobalLayout() {         SRefreshLayout.this.mHeight = SRefreshLayout.this.getHeight();         SRefreshLayout.this.getViewTreeObserver().removeGlobalOnLayoutListener(this);       }     });   } 

準備工作完成了,接下來就是要成下拉操作了
到這里,肯定一下就想到了onTouchEvent()方法,是的!現在就開始在這里施工

實現下拉一共 會經歷三個過程
ACTION_UP→ACTION_MOVE→ACTION_UP
在ACTION_UP事件中,也就是手指按下的時候,我們需要做的只是記錄按下時候的坐標

switch (ev.getAction()) {       case MotionEvent.ACTION_DOWN:         //記錄起始高度         mLastY = ev.getRawY();//記錄按下時的Y坐標         break; 

然后就是ACTION_MOVE事件了,這里是最重要的,因為下拉時HeadView和Layout的高度變化都在這里進行

case MotionEvent.ACTION_MOVE:        if (!isRefreashing)          isRefreashing = true;        final float deltaY = ev.getRawY() - mLastY;        mLastY = ev.getRawY();        updateHeaderViewHeight(deltaY / 1.8f);//按一定比例縮小移動距離        updateHeight();        break; 

里面的updateHeaderViewHeight和updateHeight分別是改變HeaderView的高度和Layout的高度

 private void updateHeight() {     ViewGroup.LayoutParams lp = getLayoutParams();     //更新當前layout實例高度為headerView高度加上最初的layout高度     //如果不更新layout 會造成內容高度壓縮 無法保持比例     lp.height = (mHeight + mHeaderView.getVisiableHeight());     setLayoutParams(lp);   }    private void updateHeaderViewHeight(float space) { //    if (space < 0) //      space = 0; //    int factHeight = (int) (space - mHeaderViewHeight);     if (mHeaderView.getStatus() != SRefreshHeader.STATE_REFRESHING) {       //如果不處于刷新中同時如果高度       if (mHeaderView.getVisiableHeight() < mHeaderViewHeight * 2 && mHeaderView.getStatus() != SRefreshHeader.STATE_NORMAL) {         mHeaderView.setState(SRefreshHeader.STATE_NORMAL);       }       if (mHeaderView.getVisiableHeight() > mHeaderViewHeight * 2 && mHeaderView.getStatus() != SRefreshHeader.STATE_READY) {         mHeaderView.setState(SRefreshHeader.STATE_READY);       }     }     mHeaderView.setVisiableHeight((int) space         + mHeaderView.getVisiableHeight());   } 

更新Header高度時,通過下拉的距離來判斷是否到達刷新的距離,上面代碼中我設定的是到達mHeaderView初始高度的兩倍,就進入“釋放刷新”的狀態,如果沒有達到則保持“下拉刷新”的狀態
HeaderView中的狀態一共設定了3個分別是

public final static int STATE_NORMAL = 0;//下拉刷新  public final static int STATE_READY = 1;//釋放刷新  public final static int STATE_REFRESHING = 2;//刷新中 

更新高度的方法headerView和layout都是相同的,就是原高度加上移動的距離重新賦給headerView或者layout

mHeaderView.setVisiableHeight((int) space 
               + mHeaderView.getVisiableHeight()); 

最后就是ACTION_UP事件了就是手指離開屏幕的時候,在這里我們需要根據headerView目前狀態來決定headerView的最終狀態!

case MotionEvent.ACTION_UP:         //松開時         //避免點擊事件觸發         if (!isRefreashing)           break;         //如果headView狀態處于READY狀態 則說明松開時應該進入REFRESHING狀態         if (mHeaderView.getStatus() == SRefreshHeader.STATE_READY) {           mHeaderView.setState(SRefreshHeader.STATE_REFRESHING);         }         //根據狀態重置SrefreshLayout當前實例和headView高度         resetHeadView(mHeaderView.getStatus());         reset(mHeaderView.getStatus());         mLastY = -1;//重置坐標         break; 

resetHeadView和reset分別是重置headerView高度和layout高度的方法

private void reset(int status) {     ViewGroup.LayoutParams lp = getLayoutParams();     switch (status) {       case SRefreshHeader.STATE_REFRESHING:         lp.height = mHeight + mHeaderViewHeight;         break;       case SRefreshHeader.STATE_NORMAL:         lp.height = mHeight;         break;     }     setLayoutParams(lp);   }    private void resetHeadView(int status) {     switch (status) {       case SRefreshHeader.STATE_REFRESHING:         mHeaderView.setVisiableHeight(mHeaderViewHeight);         break;       case SRefreshHeader.STATE_NORMAL:         mHeaderView.setVisiableHeight(0);         break;     }   } 

實現方式也是一樣的。根據狀態來判斷,如果是處于刷新中,那headerView應該正常顯示,并且高度是初始的高度,如果處于NORMAL,也就是"下拉刷新"狀態,那么說未觸發刷新,重置時,headerView應該被隱藏,也就是高度重置為0

到這里下拉刷新操作也基本完成了,還需要加一個回調接口進行通知

interface OnRefreshListener {     void onRefresh();   } 
case MotionEvent.ACTION_UP:         //松開時         //避免點擊事件觸發         if (!isRefreashing)           break;         //如果headView狀態處于READY狀態 則說明松開時應該進入REFRESHING狀態         if (mHeaderView.getStatus() == SRefreshHeader.STATE_READY) {           mHeaderView.setState(SRefreshHeader.STATE_REFRESHING);           if (mOnRefreshListener != null)             mOnRefreshListener.onRefresh();         }         //根據狀態重置SrefreshLayout當前實例和headView高度         resetHeadView(mHeaderView.getStatus());         reset(mHeaderView.getStatus());         mLastY = -1;//重置坐標         break; 

好,到這里就基本完成了,試試效果吧。咦,發現一個問題,嵌套ListView的時候為什么這個Layout不能執行下拉刷新!仔細想想應該是事件分發的問題,還需要處理一下事件的攔截!
關于事件攔截的處理,閱讀了鴻洋大神寫的viewgroup事件分發的博客和Android-Ultra-Pull-To-Refresh的部分源碼,從中找到了解決辦法:

@Override   public boolean onInterceptTouchEvent(MotionEvent ev) {     AbsListView absListView = null;     for (int n = 0; n < getChildCount(); n++) {       if (getChildAt(n) instanceof AbsListView) {         absListView = (ListView) getChildAt(n);         Logs.v("查找到listView");       }     }     if (absListView == null)       return super.onInterceptTouchEvent(ev);     switch (ev.getAction()) {       case MotionEvent.ACTION_DOWN:         mStartY = ev.getRawY();         break;       case MotionEvent.ACTION_MOVE:         float space = ev.getRawY() - mStartY;         Logs.v("space:" + space);         if (space > 0 && !absListView.canScrollVertically(-1) && absListView.getFirstVisiblePosition() == 0) {           Logs.v("攔截成功");           return true;         } else {           Logs.v("不攔截");           return false;         }     }     return super.onInterceptTouchEvent(ev);   } 

其中

if (space > 0 && !absListView.canScrollVertically(-1) && absListView.getFirstVisiblePosition() == 0)
space即移動的距離 canScrollVertically()是判斷ListView能否在垂直方向上滾動,參數為負數時代表向上,為正數時代碼向下滾動,最后一個就是ListView第一個可見的item的postion

加上上面的事件攔截處理,一個可以滿足開頭提到的需求的Viewgroup也就完成了!

下面貼上Layout的源碼和HeaderView(直接使用的XlistView的HeaderView)的源碼

public class SRefreshLayout extends LinearLayout {   private SRefreshHeader mHeaderView;   private RelativeLayout mHeaderViewContent;   private boolean isRefreashing;   private float mLastY = -1;//按下的起始高度   private int mHeaderViewHeight;//headerView內容高度   private int mHeight;//布局高度   private float mStartY;    interface OnRefreshListener {     void onRefresh();   }    public OnRefreshListener mOnRefreshListener;    public SRefreshLayout(Context context) {     super(context);     initView(context);   }    public SRefreshLayout(Context context, AttributeSet attrs) {     super(context, attrs);     initView(context);   }    public SRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {     super(context, attrs, defStyleAttr);     initView(context);   }    private void initView(Context context) {     mHeaderView = new SRefreshHeader(context);     mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.slistview_header_content);     setOrientation(VERTICAL);     addView(mHeaderView, 0);     getHeaderViewHeight();     getViewHeight();   }    /**    * 獲取headView高度    */   private void getHeaderViewHeight() {     ViewTreeObserver vto2 = mHeaderViewContent.getViewTreeObserver();     vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {       @Override       public void onGlobalLayout() {         mHeaderViewHeight = mHeaderViewContent.getHeight();         mHeaderViewContent.getViewTreeObserver().removeGlobalOnLayoutListener(this);       }     });   }    /**    * 獲取SRefreshLayout當前實例的高度    */   private void getViewHeight() {     ViewTreeObserver thisView = getViewTreeObserver();     thisView.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {       @Override       public void onGlobalLayout() {         SRefreshLayout.this.mHeight = SRefreshLayout.this.getHeight();         SRefreshLayout.this.getViewTreeObserver().removeGlobalOnLayoutListener(this);       }     });   }    @Override   public boolean onInterceptTouchEvent(MotionEvent ev) {     AbsListView absListView = null;     for (int n = 0; n < getChildCount(); n++) {       if (getChildAt(n) instanceof AbsListView) {         absListView = (ListView) getChildAt(n);         Logs.v("查找到listView");       }     }     if (absListView == null)       return super.onInterceptTouchEvent(ev);     switch (ev.getAction()) {       case MotionEvent.ACTION_DOWN:         mStartY = ev.getRawY();         break;       case MotionEvent.ACTION_MOVE:         float space = ev.getRawY() - mStartY;         Logs.v("space:" + space);         if (space > 0 && !absListView.canScrollVertically(-1) && absListView.getFirstVisiblePosition() == 0) {           Logs.v("攔截成功");           return true;         } else {           Logs.v("不攔截");           return false;         }     }     return super.onInterceptTouchEvent(ev);   }    @Override   public boolean onTouchEvent(MotionEvent ev) {     if (mLastY == -1)       mLastY = ev.getRawY();     switch (ev.getAction()) {       case MotionEvent.ACTION_DOWN:         //記錄起始高度         mLastY = ev.getRawY();//記錄按下時的Y坐標         break;       //手指離開屏幕時       case MotionEvent.ACTION_UP:         //松開時         //避免點擊事件觸發         if (!isRefreashing)           break;         //如果headView狀態處于READY狀態 則說明松開時應該進入REFRESHING狀態         if (mHeaderView.getStatus() == SRefreshHeader.STATE_READY) {           mHeaderView.setState(SRefreshHeader.STATE_REFRESHING);           if (mOnRefreshListener != null)             mOnRefreshListener.onRefresh();         }         //根據狀態重置SrefreshLayout當前實例和headView高度         resetHeadView(mHeaderView.getStatus());         reset(mHeaderView.getStatus());         mLastY = -1;//重置坐標         break;       case MotionEvent.ACTION_MOVE:         if (!isRefreashing)           isRefreashing = true;         final float deltaY = ev.getRawY() - mLastY;         mLastY = ev.getRawY();         updateHeaderViewHeight(deltaY / 1.8f);//按一定比例縮小移動距離         updateHeight();         break;     }     return super.onTouchEvent(ev);   }     private void reset(int status) {     ViewGroup.LayoutParams lp = getLayoutParams();     switch (status) {       case SRefreshHeader.STATE_REFRESHING:         lp.height = mHeight + mHeaderViewHeight;         break;       case SRefreshHeader.STATE_NORMAL:         lp.height = mHeight;         break;     }     setLayoutParams(lp);   }    private void resetHeadView(int status) {     switch (status) {       case SRefreshHeader.STATE_REFRESHING:         mHeaderView.setVisiableHeight(mHeaderViewHeight);         break;       case SRefreshHeader.STATE_NORMAL:         mHeaderView.setVisiableHeight(0);         break;     }   }    private void updateHeight() {     ViewGroup.LayoutParams lp = getLayoutParams();     //更新當前layout實例高度為headerView高度加上最初的layout高度     //如果不更新layout 會造成內容高度壓縮 無法保持比例     lp.height = (mHeight + mHeaderView.getVisiableHeight());     setLayoutParams(lp);   }    private void updateHeaderViewHeight(float space) { //    if (space < 0) //      space = 0; //    int factHeight = (int) (space - mHeaderViewHeight);     if (mHeaderView.getStatus() != SRefreshHeader.STATE_REFRESHING) {       //如果不處于刷新中同時如果高度       if (mHeaderView.getVisiableHeight() < mHeaderViewHeight * 2 && mHeaderView.getStatus() != SRefreshHeader.STATE_NORMAL) {         mHeaderView.setState(SRefreshHeader.STATE_NORMAL);       }       if (mHeaderView.getVisiableHeight() > mHeaderViewHeight * 2 && mHeaderView.getStatus() != SRefreshHeader.STATE_READY) {         mHeaderView.setState(SRefreshHeader.STATE_READY);       }     }     mHeaderView.setVisiableHeight((int) space         + mHeaderView.getVisiableHeight());   }     public void stopRefresh() {     if (mHeaderView.getStatus() == SRefreshHeader.STATE_REFRESHING) {       mHeaderView.setState(SRefreshHeader.STATE_NORMAL);       resetHeadView(SRefreshHeader.STATE_NORMAL);       reset(SRefreshHeader.STATE_NORMAL);     }   }    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {     this.mOnRefreshListener = onRefreshListener;   } } 

public class SRefreshHeader extends LinearLayout {   private LinearLayout mContainer;   private int mState = STATE_NORMAL;    private Animation mRotateUpAnim;   private Animation mRotateDownAnim;    private final int ROTATE_ANIM_DURATION = 500;    public final static int STATE_NORMAL = 0;//下拉刷新   public final static int STATE_READY = 1;//釋放刷新   public final static int STATE_REFRESHING = 2;//刷新中   private ImageView mHeadArrowImage;   private TextView mHeadLastRefreashTimeTxt;   private TextView mHeadHintTxt;   private TextView mHeadLastRefreashTxt;   private ProgressBar mRefreshingProgress;    public SRefreshHeader(Context context) {     super(context);     initView(context);   }    /**    * @param context    * @param attrs    */   public SRefreshHeader(Context context, AttributeSet attrs) {     super(context, attrs);     initView(context);   }    private void initView(Context context) {     // 初始情況,設置下拉刷新view高度為0     LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0);     mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.listview_head_view_layout, null);     addView(mContainer, lp);     setGravity(Gravity.BOTTOM);      mHeadArrowImage = (ImageView) findViewById(R.id.slistview_header_arrow);     mHeadLastRefreashTimeTxt = (TextView) findViewById(R.id.slistview_header_time);     mHeadHintTxt = (TextView) findViewById(R.id.slistview_header_hint_text);     mHeadLastRefreashTxt = (TextView) findViewById(R.id.slistview_header_last_refreash_txt);     mRefreshingProgress = (ProgressBar) findViewById(R.id.slistview_header_progressbar);      mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,         Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,         0.5f);     mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);     mRotateUpAnim.setFillAfter(true);     mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,         Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,         0.5f);     mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);     mRotateDownAnim.setFillAfter(true);   }    public void setState(int state) {     if (state == mState) return;      if (state == STATE_REFRESHING) {  // 顯示進度       mHeadArrowImage.clearAnimation();       mHeadArrowImage.setVisibility(View.INVISIBLE);       mRefreshingProgress.setVisibility(View.VISIBLE);     } else {  // 顯示箭頭圖片       mHeadArrowImage.setVisibility(View.VISIBLE);       mRefreshingProgress.setVisibility(View.INVISIBLE);     }     switch (state) {       case STATE_NORMAL:         if (mState == STATE_READY) {           mHeadArrowImage.startAnimation(mRotateDownAnim);         }         if (mState == STATE_REFRESHING) {           mHeadArrowImage.clearAnimation();         }         mHeadHintTxt.setText("下拉刷新");         break;       case STATE_READY:         if (mState != STATE_READY) {           mHeadArrowImage.clearAnimation();           mHeadArrowImage.startAnimation(mRotateUpAnim);           mHeadHintTxt.setText("松開刷新");         }         break;       case STATE_REFRESHING:         mHeadHintTxt.setText("正在刷新");         break;       default:     }      mState = state;   }    public void setVisiableHeight(int height) {     if (height < 0)       height = 0;     LayoutParams lp = (LayoutParams) mContainer         .getLayoutParams();     lp.height = height;     mContainer.setLayoutParams(lp);   }     public int getStatus() {     return mState;   }    public int getVisiableHeight() {     return mContainer.getHeight();   } } 

最后是布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="match_parent"   android:layout_height="wrap_content"   android:gravity="bottom">    <RelativeLayout     android:id="@+id/slistview_header_content"     android:layout_width="match_parent"     android:layout_height="60dp">      <LinearLayout       android:id="@+id/slistview_header_text"       android:layout_width="wrap_content"       android:layout_height="wrap_content"       android:layout_centerInParent="true"       android:gravity="center"       android:orientation="vertical">        <TextView         android:id="@+id/slistview_header_hint_text"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:text="下拉刷新" />        <LinearLayout         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_marginTop="3dp">          <TextView           android:id="@+id/slistview_header_last_refreash_txt"           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:text="上次刷新時間"           android:textSize="12sp" />          <TextView           android:id="@+id/slistview_header_time"           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:textSize="12sp" />       </LinearLayout>     </LinearLayout>       <ProgressBar       android:id="@+id/slistview_header_progressbar"       android:layout_width="30dp"       android:layout_height="30dp"       android:layout_centerVertical="true"       android:layout_toLeftOf="@id/slistview_header_text"       android:visibility="invisible" />      <ImageView       android:id="@+id/slistview_header_arrow"       android:layout_width="wrap_content"       android:layout_height="wrap_content"       android:layout_alignLeft="@id/slistview_header_progressbar"       android:layout_centerVertical="true"       android:layout_toLeftOf="@id/slistview_header_text"       android:src="@drawable/mmtlistview_arrow" />    </RelativeLayout>  </LinearLayout> 

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 布尔津县| 高安市| 巴彦淖尔市| 临湘市| 辽阳县| 望谟县| 松江区| 班玛县| 安陆市| 滕州市| 黄平县| 施甸县| 大埔县| 三穗县| 读书| 中西区| 娄底市| 确山县| 磴口县| 镇坪县| 龙里县| 六安市| 玛纳斯县| 鹤庆县| 尼木县| 仁化县| 清远市| 新和县| 阿城市| 汉寿县| 大田县| 博湖县| 广州市| 合阳县| 宁都县| 九龙坡区| 乐昌市| 凤台县| 方城县| 灌云县| 宜阳县|