知識(shí)
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們?cè)谧非笃湟曈X(jué)表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營(yíng)銷的便利,運(yùn)營(yíng)的高效,讓網(wǎng)站成為營(yíng)銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏?jí)提供便捷的支持!
【Android界面實(shí)現(xiàn)】XListView實(shí)現(xiàn)原理講解及分析
發(fā)表時(shí)間:2020-10-19
發(fā)布人:葵宇科技
瀏覽次數(shù):23
轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/zhaokaiqiang1992
XListview是一個(gè)異常受迎接的下拉刷新控件,然則已經(jīng)停止保護(hù)了。之前寫(xiě)過(guò)一篇XListview的應(yīng)用介紹,用起來(lái)異常簡(jiǎn)單,這兩天放假無(wú)聊,研究了下XListview的實(shí)現(xiàn)道理,學(xué)到了很多,今天稟享給大年夜家。
提前聲明,為了讓代碼更好的懂得,我對(duì)代碼進(jìn)行了部分刪減和重構(gòu),如不雅大年夜家想看原版代碼,請(qǐng)去github自行下載。
Xlistview項(xiàng)目主如果三部分:XlistView,XListViewHeader,XListViewFooter,分別是XListView主體、header、footer的實(shí)現(xiàn)。下面我們分開(kāi)來(lái)介紹。
下面是修改之后的XListViewHeader代碼
public class XListViewHeader extends LinearLayout { private static final String HINT_NORMAL = "下拉刷新"; private static final String HINT_READY = "松開(kāi)刷新數(shù)據(jù)"; private static final String HINT_LOADING = "正在加載..."; // 正常狀況 public final static int STATE_NORMAL = 0; // 預(yù)備刷新?tīng)顩r,也就是箭頭偏向產(chǎn)生改變之后的狀況 public final static int STATE_READY = 1; // 刷新?tīng)顩r,箭頭變成了progressBar public final static int STATE_REFRESHING = 2; // 構(gòu)造容器,也就是根構(gòu)造 private LinearLayout container; // 箭頭圖片 private ImageView mArrowImageView; // 刷新?tīng)顩r顯示 private ProgressBar mProgressBar; // 解釋文本 private TextView mHintTextView; // 記錄當(dāng)前的狀況 private int mState; // 用于改變箭頭的偏向的動(dòng)畫(huà) private Animation mRotateUpAnim; private Animation mRotateDownAnim; // 動(dòng)畫(huà)持續(xù)時(shí)光 private final int ROTATE_ANIM_DURATION = 180; public XListViewHeader(Context context) { super(context); initView(context); } public XListViewHeader(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } private void initView(Context context) { mState = STATE_NORMAL; // 初始情況下,設(shè)置下拉刷新view高度為0 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, 0); container = (LinearLayout) LayoutInflater.from(context).inflate( R.layout.xlistview_header, null); addView(container, lp); // 初始化控件 mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow); mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview); mProgressBar = (ProgressBar) findViewById(R.id.xlistview_header_progressbar); // 初始化動(dòng)畫(huà) 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); } // 設(shè)置header的狀況 public void setState(int state) { if (state == mState) return; // 顯示進(jìn)度 if (state == STATE_REFRESHING) { mArrowImageView.clearAnimation(); mArrowImageView.setVisibility(View.INVISIBLE); mProgressBar.setVisibility(View.VISIBLE); } else { // 顯示箭頭 mArrowImageView.setVisibility(View.VISIBLE); mProgressBar.setVisibility(View.INVISIBLE); } switch (state) { case STATE_NORMAL: if (mState == STATE_READY) { mArrowImageView.startAnimation(mRotateDownAnim); } if (mState == STATE_REFRESHING) { mArrowImageView.clearAnimation(); } mHintTextView.setText(HINT_NORMAL); break; case STATE_READY: if (mState != STATE_READY) { mArrowImageView.clearAnimation(); mArrowImageView.startAnimation(mRotateUpAnim); mHintTextView.setText(HINT_READY); } break; case STATE_REFRESHING: mHintTextView.setText(HINT_LOADING); break; } mState = state; } public void setVisiableHeight(int height) { if (height < 0) height = 0; LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) container .getLayoutParams(); lp.height = height; container.setLayoutParams(lp); } public int getVisiableHeight() { return container.getHeight(); } public void show() { container.setVisibility(View.VISIBLE); } public void hide() { container.setVisibility(View.INVISIBLE); } }
XListViewHeader持續(xù)自linearLayout,用來(lái)實(shí)現(xiàn)下拉刷新時(shí)的界面展示,可以分為三種狀況:正常、預(yù)備刷新、正在加載。
在Linearlayout構(gòu)造瑯綾擎,重要有指導(dǎo)箭頭、解釋文本、圓形加載條三個(gè)控件。在構(gòu)造函數(shù)中,調(diào)用了initView()進(jìn)行控件的初始化操作。在添加構(gòu)造文件的時(shí)刻,指定高度為0,這是為了隱蔽header,然后初始化動(dòng)畫(huà),是為了完成箭頭的扭遷移轉(zhuǎn)變作。
setState()是設(shè)置header的狀況,因?yàn)閔eader須要根據(jù)不合的狀況,完成控件隱蔽、顯示、改變文字等操作,這個(gè)辦法主如果在XListView瑯綾擎調(diào)用。除此之外,還有setVisiableHeight()和getVisiableHeight(),這兩個(gè)辦法是為了設(shè)置和獲取Header中根構(gòu)造文件的高度屬性,大年夜而完成拉伸和緊縮的效不雅,而show()和hide()則顯然就是完成顯示和隱蔽的效不雅。
下面是Header的構(gòu)造文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="bottom" > <RelativeLayout android:id="@+id/xlistview_header_content" android:layout_width="match_parent" android:layout_height="60dp" tools:ignore="UselessParent" > <TextView android:id="@+id/xlistview_header_hint_textview" android:layout_width="100dp" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:text="正在加載" android:textColor="@android:color/black" android:textSize="14sp" /> <ImageView android:id="@+id/xlistview_header_arrow" android:layout_width="30dp" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toLeftOf="@id/xlistview_header_hint_textview" android:src=http://www.sjsjw.com/100/000238MYM031158/"@drawable/xlistview_arrow" />
說(shuō)完了Header,我們?cè)倏纯碏ooter。Footer是為了完成加載更多功能時(shí)刻的界面展示,根本思路和Header是一樣的,下面是Footer的代碼
public class XListViewFooter extends LinearLayout { // 正常狀況 public final static int STATE_NORMAL = 0; // 預(yù)備狀況 public final static int STATE_READY = 1; // 加載狀況 public final static int STATE_LOADING = 2; private View mContentView; private View mProgressBar; private TextView mHintView; public XListViewFooter(Context context) { super(context); initView(context); } public XListViewFooter(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } private void initView(Context context) { LinearLayout moreView = (LinearLayout) LayoutInflater.from(context) .inflate(R.layout.xlistview_footer, null); addView(moreView); moreView.setLayoutParams(new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); mContentView = moreView.findViewById(R.id.xlistview_footer_content); mProgressBar = moreView.findViewById(R.id.xlistview_footer_progressbar); mHintView = (TextView) moreView .findViewById(R.id.xlistview_footer_hint_textview); } /** * 設(shè)置當(dāng)前的狀況 * * @param state */ public void setState(int state) { mProgressBar.setVisibility(View.INVISIBLE); mHintView.setVisibility(View.INVISIBLE); switch (state) { case STATE_READY: mHintView.setVisibility(View.VISIBLE); mHintView.setText(R.string.xlistview_footer_hint_ready); break; case STATE_NORMAL: mHintView.setVisibility(View.VISIBLE); mHintView.setText(R.string.xlistview_footer_hint_normal); break; case STATE_LOADING: mProgressBar.setVisibility(View.VISIBLE); break; } } public void setBottomMargin(int height) { if (height > 0) { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView .getLayoutParams(); lp.bottomMargin = height; mContentView.setLayoutParams(lp); } } public int getBottomMargin() { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView .getLayoutParams(); return lp.bottomMargin; } public void hide() { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView .getLayoutParams(); lp.height = 0; mContentView.setLayoutParams(lp); } public void show() { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView .getLayoutParams(); lp.height = LayoutParams.WRAP_CONTENT; mContentView.setLayoutParams(lp); } }
大年夜膳綾擎的代率攀瑯綾擎,我們可以看出,footer和header的思路是一樣的,只不過(guò),footer的拉伸和顯示效不雅不是經(jīng)由過(guò)程高度來(lái)模仿的,而是經(jīng)由過(guò)程設(shè)置BottomMargin來(lái)完成的。
下面是Footer的構(gòu)造文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="wrap_content" > <RelativeLayout android:id="@+id/xlistview_footer_content" android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="5dp" tools:ignore="UselessParent" > <ProgressBar android:id="@+id/xlistview_footer_progressbar" style="@style/progressbar_style" android:layout_width="30dp" android:layout_height="30dp" android:layout_centerInParent="true" android:visibility="invisible" /> <TextView android:id="@+id/xlistview_footer_hint_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="@string/xlistview_footer_hint_normal" android:textColor="@android:color/black" android:textSize="14sp" /> </RelativeLayout> </LinearLayout>
在懂得了Header和footer之后,我們就要介紹最核心的XListView的代碼實(shí)現(xiàn)了。
在介紹代碼實(shí)現(xiàn)之前,我先介紹一下XListView的實(shí)現(xiàn)道理。
起首,一旦應(yīng)用XListView,F(xiàn)ooter和Header就已經(jīng)添加到我們的ListView膳綾擎了,XListView就是經(jīng)由過(guò)程持續(xù)ListView,然后處理了屏幕點(diǎn)擊事宜和控制滑動(dòng)實(shí)現(xiàn)效不雅的。所以,如不雅我們的Adapter中g(shù)etCount()返回的值是20,那么其實(shí)XListView瑯綾擎是有20+2個(gè)item的,這個(gè)數(shù)量即使我們封閉了XListView的刷新和加載功能,也是不會(huì)變更的。Header和Footer經(jīng)由過(guò)程addHeaderView和addFooterView添加上去之后,如不雅想實(shí)現(xiàn)下拉刷新和上拉加載功能,那么就必須有拉伸效不雅,所以就像膳綾擎的那樣,Header是經(jīng)由過(guò)程設(shè)置height,F(xiàn)ooter是經(jīng)由過(guò)程設(shè)置BottomMargin來(lái)模仿拉伸效不雅。那么回彈效不雅呢??jī)H僅經(jīng)由過(guò)程設(shè)置高度或者是距離是達(dá)不到模仿回彈效不雅的,是以,就須要用Scroller來(lái)實(shí)現(xiàn)模仿回彈效不雅。在解釋道理之后,我們開(kāi)端介紹XListView的核心實(shí)現(xiàn)道理。
再次提示,下面的代碼經(jīng)由我重構(gòu)了,只是為了看起來(lái)更好的懂得。
public class XListView extends ListView { private final static int SCROLLBACK_HEADER = 0; private final static int SCROLLBACK_FOOTER = 1; // 滑動(dòng)時(shí)長(zhǎng) private final static int SCROLL_DURATION = 400; // 加載更多的距離 private final static int PULL_LOAD_MORE_DELTA = 100; // 滑動(dòng)比例 private final static float OFFSET_RADIO = 2f; // 記錄按下點(diǎn)的y坐標(biāo) private float lastY; // 用往返滾 private Scroller scroller; private IXListViewListener mListViewListener; private XListViewHeader headerView; private RelativeLayout headerViewContent; // header的高度 private int headerHeight; // 是否可以或許刷新 private boolean enableRefresh = true; // 是否正在刷新 private boolean isRefreashing = false; // footer private XListViewFooter footerView; // 是否可以加載更多 private boolean enableLoadMore; // 是否正在加載 private boolean isLoadingMore; // 是否footer預(yù)備狀況 private boolean isFooterAdd = false; // total list items, used to detect is at the bottom of listview. private int totalItemCount; // 記錄是大年夜header照樣footer返回 private int mScrollBack; private static final String TAG = "XListView"; public XListView(Context context) { super(context); initView(context); } public XListView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public XListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(context); } private void initView(Context context) { scroller = new Scroller(context, new DecelerateInterpolator()); headerView = new XListViewHeader(context); footerView = new XListViewFooter(context); headerViewContent = (RelativeLayout) headerView .findViewById(R.id.xlistview_header_content); headerView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @SuppressWarnings("deprecation") @Override public void onGlobalLayout() { headerHeight = headerViewContent.getHeight(); getViewTreeObserver() .removeGlobalOnLayoutListener(this); } }); addHeaderView(headerView); } @Override public void setAdapter(ListAdapter adapter) { // 確保footer最后添加并且只添加一次 if (isFooterAdd == false) { isFooterAdd = true; addFooterView(footerView); } super.setAdapter(adapter); } @Override public boolean onTouchEvent(MotionEvent ev) { totalItemCount = getAdapter().getCount(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: // 記錄按下的坐標(biāo) lastY = ev.getRawY(); break; case MotionEvent.ACTION_MOVE: // 計(jì)算移動(dòng)距離 float deltaY = ev.getRawY() - lastY; lastY = ev.getRawY(); // 是第一項(xiàng)并且標(biāo)題已經(jīng)顯示或者是鄙人拉 if (getFirstVisiblePosition() == 0 && (headerView.getVisiableHeight() > 0 || deltaY > 0)) { updateHeaderHeight(deltaY / OFFSET_RADIO); } else if (getLastVisiblePosition() == totalItemCount - 1 && (footerView.getBottomMargin() > 0 || deltaY < 0)) { updateFooterHeight(-deltaY / OFFSET_RADIO); } break; case MotionEvent.ACTION_UP: if (getFirstVisiblePosition() == 0) { if (enableRefresh && headerView.getVisiableHeight() > headerHeight) { isRefreashing = true; headerView.setState(XListViewHeader.STATE_REFRESHING); if (mListViewListener != null) { mListViewListener.onRefresh(); } } resetHeaderHeight(); } else if (getLastVisiblePosition() == totalItemCount - 1) { if (enableLoadMore && footerView.getBottomMargin() > PULL_LOAD_MORE_DELTA) { startLoadMore(); } resetFooterHeight(); } break; } return super.onTouchEvent(ev); } @Override public void computeScroll() { // 松手之后調(diào)用 if (scroller.computeScrollOffset()) { if (mScrollBack == SCROLLBACK_HEADER) { headerView.setVisiableHeight(scroller.getCurrY()); } else { footerView.setBottomMargin(scroller.getCurrY()); } postInvalidate(); } super.computeScroll(); } public void setPullRefreshEnable(boolean enable) { enableRefresh = enable; if (!enableRefresh) { headerView.hide(); } else { headerView.show(); } } public void setPullLoadEnable(boolean enable) { enableLoadMore = enable; if (!enableLoadMore) { footerView.hide(); footerView.setOnClickListener(null); } else { isLoadingMore = false; footerView.show(); footerView.setState(XListViewFooter.STATE_NORMAL); footerView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startLoadMore(); } }); } } public void stopRefresh() { if (isRefreashing == true) { isRefreashing = false; resetHeaderHeight(); } } public void stopLoadMore() { if (isLoadingMore == true) { isLoadingMore = false; footerView.setState(XListViewFooter.STATE_NORMAL); } } private void updateHeaderHeight(float delta) { headerView.setVisiableHeight((int) delta + headerView.getVisiableHeight()); // 未處于刷新?tīng)顩r,更新箭頭 if (enableRefresh && !isRefreashing) { if (headerView.getVisiableHeight() > headerHeight) { headerView.setState(XListViewHeader.STATE_READY); } else { headerView.setState(XListViewHeader.STATE_NORMAL); } } } private void resetHeaderHeight() { // 當(dāng)前的可見(jiàn)高度 int height = headerView.getVisiableHeight(); // 如不雅正在刷新并且高度沒(méi)有完全展示 if ((isRefreashing && height <= headerHeight) || (height == 0)) { return; } // 默認(rèn)會(huì)回滾到header的地位 int finalHeight = 0; // 如不雅是正在刷新?tīng)顩r,則回滾到header的高度 if (isRefreashing && height > headerHeight) { finalHeight = headerHeight; } mScrollBack = SCROLLBACK_HEADER; // 回滾到指定地位 scroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION); // 觸發(fā)computeScroll invalidate(); } private void updateFooterHeight(float delta) { int height = footerView.getBottomMargin() + (int) delta; if (enableLoadMore && !isLoadingMore) { if (height > PULL_LOAD_MORE_DELTA) { footerView.setState(XListViewFooter.STATE_READY); } else { footerView.setState(XListViewFooter.STATE_NORMAL); } } footerView.setBottomMargin(height); } private void resetFooterHeight() { int bottomMargin = footerView.getBottomMargin(); if (bottomMargin > 0) { mScrollBack = SCROLLBACK_FOOTER; scroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION); invalidate(); } } private void startLoadMore() { isLoadingMore = true; footerView.setState(XListViewFooter.STATE_LOADING); if (mListViewListener != null) { mListViewListener.onLoadMore(); } } public void setXListViewListener(IXListViewListener l) { mListViewListener = l; } public interface IXListViewListener { public void onRefresh(); public void onLoadMore(); } }
在三個(gè)構(gòu)造函數(shù)中,都調(diào)用initView進(jìn)行了header和footer的初始化,并且定義了一個(gè)Scroller,并傳入了一個(gè)減速的插值器,為了模仿回彈效不雅。在initView辦法瑯綾擎,因?yàn)閔eader可能還沒(méi)初始化完畢,所以經(jīng)由過(guò)程GlobalLayoutlistener來(lái)獲取了header的高度,然后addHeaderView添加到了listview膳綾擎。
經(jīng)由過(guò)程重寫(xiě)setAdapter辦法,包管Footer最后天假,并且只添加一次。
最重要的,要屬onTouchEvent了。在辦法開(kāi)端之前,經(jīng)由過(guò)程getAdapter().getCount()獲取到了item的總數(shù),便于計(jì)算地位。這個(gè)操作在源代碼中是經(jīng)由過(guò)程scrollerListener完成的,因?yàn)镾crollerListener在這瑯綾腔大年夜有效,所以我直接去掉落了,然后把地位改到了這里。如不雅在setAdapter瑯綾擎獲取的話,只能獲取到?jīng)]有header和footer的item數(shù)量。
在ACTION_DOWN瑯綾擎,進(jìn)行了lastY的初始化,lastY是為了斷定移動(dòng)偏向的,因?yàn)樵贏CTION_MOVE瑯綾擎,經(jīng)由過(guò)程ev.getRawY()-lastY可以計(jì)算出手指的移動(dòng)趨勢(shì),如不雅>0,那么就是向下滑動(dòng),反之向上。getRowY()是獲取元Y坐標(biāo),意思就是和Window和View坐標(biāo)沒(méi)有關(guān)系的坐標(biāo),代表在屏幕上的絕對(duì)地位。然后鄙人面的代率攀瑯綾擎,如不雅第一項(xiàng)可見(jiàn)并且header的可見(jiàn)高度>0或者是向下滑動(dòng),就解釋用戶在向下拉動(dòng)或者是向上拉動(dòng)header,也就是指導(dǎo)箭頭顯示的時(shí)刻的狀況,這時(shí)刻調(diào)用了updateHeaderHeight,來(lái)更新header的高度,實(shí)現(xiàn)header可以追順手指動(dòng)作高低移動(dòng)。這里有個(gè)OFFSET_RADIO,這個(gè)值是一個(gè)移動(dòng)比例,就是說(shuō),你手指在Y偏向上移動(dòng)400px,如不雅比例是2,那么屏幕上的控件移動(dòng)就是400px/2=200px,可以經(jīng)由過(guò)程這個(gè)值來(lái)控制用戶的滑動(dòng)體驗(yàn)。下面的關(guān)于footer的斷定與此類似,不再贅述。
當(dāng)用戶移開(kāi)手指之后,ACTION_UP辦法就會(huì)被調(diào)用。在這瑯綾擎,只對(duì)可見(jiàn)地位是0和item總數(shù)-1的地位進(jìn)行了處理,其拭魅正好對(duì)應(yīng)header和footer。如不雅地位是0,并且可以刷新,然后當(dāng)前的header可見(jiàn)高度>原始高度的話,就解釋用戶確切是要進(jìn)行刷新操作,所以經(jīng)由過(guò)程setState改變header的狀況,如不雅有監(jiān)聽(tīng)器的話,就調(diào)用onRefresh辦法,然后調(diào)用resetHeaderHeight初始化header的狀況,因?yàn)閒ooter的操作千篇一律,所以不再贅述。然則在footer中有一個(gè)PULL_LOAD_MORE_DELTA,這個(gè)值是加載更多觸發(fā)前提的臨界值,只有footer的距離跨越這個(gè)值之后,才能夠觸發(fā)加載更多的功能,是以我們可以修改┞封個(gè)值來(lái)改變用戶體驗(yàn)。
說(shuō)到如今,大年夜家應(yīng)當(dāng)明白根本的道理了,其實(shí)XListView就是經(jīng)由過(guò)程對(duì)用戶手勢(shì)的偏向和距離的斷定,來(lái)動(dòng)態(tài)的改變Header和Footer實(shí)現(xiàn)的功能,所以如不雅我們也有類似的需求,就可以參照這種思路進(jìn)行自定義。
下面再說(shuō)幾個(gè)比較重要的辦法。
前面我們說(shuō)道,在ACTION_MOVE瑯綾擎,會(huì)賡續(xù)的調(diào)用下面的updateXXXX辦法,來(lái)動(dòng)態(tài)的改變header和fooer的狀況,
private void updateHeaderHeight(float delta) { headerView.setVisiableHeight((int) delta + headerView.getVisiableHeight()); // 未處于刷新?tīng)顩r,更新箭頭 if (enableRefresh && !isRefreashing) { if (headerView.getVisiableHeight() > headerHeight) { headerView.setState(XListViewHeader.STATE_READY); } else { headerView.setState(XListViewHeader.STATE_NORMAL); } } } private void updateFooterHeight(float delta) { int height = footerView.getBottomMargin() + (int) delta; if (enableLoadMore && !isLoadingMore) { if (height > PULL_LOAD_MORE_DELTA) { footerView.setState(XListViewFooter.STATE_READY); } else { footerView.setState(XListViewFooter.STATE_NORMAL); } } footerView.setBottomMargin(height); }在移開(kāi)手指之后,會(huì)調(diào)用下面的resetXXX來(lái)初始化header和footer的狀況
private void resetHeaderHeight() { // 當(dāng)前的可見(jiàn)高度 int height = headerView.getVisiableHeight(); // 如不雅正在刷新并且高度沒(méi)有完全展示 if ((isRefreashing && height <= headerHeight) || (height == 0)) { return; } // 默認(rèn)會(huì)回滾到header的地位 int finalHeight = 0; // 如不雅是正在刷新?tīng)顩r,則回滾到header的高度 if (isRefreashing && height > headerHeight) { finalHeight = headerHeight; } mScrollBack = SCROLLBACK_HEADER; // 回滾到指定地位 scroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION); // 觸發(fā)computeScroll invalidate(); } private void resetFooterHeight() { int bottomMargin = footerView.getBottomMargin(); if (bottomMargin > 0) { mScrollBack = SCROLLBACK_FOOTER; scroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION); invalidate(); } }我們可以看到,滾動(dòng)操作不是經(jīng)由過(guò)程直接的設(shè)置高度來(lái)實(shí)現(xiàn)的,而是經(jīng)由過(guò)程Scroller.startScroll()來(lái)實(shí)現(xiàn)的,經(jīng)由過(guò)程調(diào)用此辦法,computeScroll()就會(huì)被調(diào)用,然后在這個(gè)瑯綾擎,根據(jù)mScrollBack區(qū)分是哪一個(gè)滾動(dòng),然后再經(jīng)由過(guò)程設(shè)置高度和距離,就可以完成緊縮的效不雅了。
至此,全部XListView的實(shí)現(xiàn)道理就完全的搞明白了,今后如不雅做滾動(dòng)類的自定義控件,應(yīng)當(dāng)也有思路了。
感謝不雅看,歇息一下。。。
相關(guān)案例查看更多
相關(guān)閱讀
- 云南網(wǎng)站建設(shè)快速優(yōu)化
- 云南小程序開(kāi)發(fā)報(bào)價(jià)
- web服務(wù)
- 小程序開(kāi)發(fā)聯(lián)系方式
- 云南網(wǎng)站建設(shè)列表網(wǎng)
- 支付寶小程序被騙
- 小程序表單
- 網(wǎng)站建設(shè)
- 大理小程序開(kāi)發(fā)
- 網(wǎng)絡(luò)公司電話
- 云南軟件開(kāi)發(fā)
- 云南小程序哪家好
- 云南企業(yè)網(wǎng)站
- 汽車報(bào)廢回收管理系統(tǒng)
- 模版信息
- 快排推廣
- 高端網(wǎng)站建設(shè)公司
- 微信小程序
- 表單
- 網(wǎng)絡(luò)公司排名
- 小程序定制
- 云南網(wǎng)站建設(shè)方案 doc
- 云南網(wǎng)站建設(shè)價(jià)格
- 云南小程序制作
- 云南微信小程序開(kāi)發(fā)
- 用戶登錄
- 云南省建設(shè)廳網(wǎng)站官網(wǎng)
- 南通小程序制作公司
- 保山小程序開(kāi)發(fā)
- 小程序被騙退款成功