欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

【Android界面實(shí)現(xiàn)】XListView實(shí)現(xiàn)原理講解及分析 - 新聞資訊 - 云南小程序開(kāi)發(fā)|云南軟件開(kāi)發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開(kāi)發(fā)/軟件開(kāi)發(fā)

知識(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í)提供便捷的支持!

您當(dāng)前位置>首頁(yè) » 新聞資訊 » 技術(shù)分享 >

【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)案例查看更多