1. 程式人生 > >Android自定義控制元件——ListView的下拉重新整理與上拉載入

Android自定義控制元件——ListView的下拉重新整理與上拉載入

1.簡介

       無疑,在Android開發中,ListView是使用非常頻繁的控制元件之一,ListView提供一個列表的容易,允許我們以列表的形式將資料展示到介面上,但是Google給我們提供的原生ListView的控制元件,雖然在功能上很強大,但是在使用者體驗和動態效果上,還是比較差勁的。為了改善使用者體驗,市面上紛紛出現了各種各樣的自定義的ListView,他們功能強大,介面美觀,使我們該需要學習的地方。其中,使用最頻繁的功能無疑就是ListView的下拉重新整理和上拉載入資料了,幾乎在沒一款內容型的App中都可以找到這種控制元件的身影,尤其是需要聯網獲取資料的模組,使用的就更為頻繁了,so,我們很有必要了解下這種效果是怎麼實現的。

2.開源元件PullToRefreshList介紹      

      既然Android和Java都是開源的,一些常見的功能或者效果就不難被找到。PullToRefresh就是一個典型的例子,PullToRefresh是老外寫的一個開源ListView元件,這個專案在ListView的基礎上擴充套件了ListView的功能,增強了Listview的使用者體驗,功能十分強大,而且很容易被整合到當前的專案中,你只需要呼叫簡單的API,即可省去很多不必要的麻煩,非常棒。以上是專案在Github的連結,有興趣的可以戳進去down下來,使用一下。這裡不是我們的重點,不想廢話了。

3.自定義ListView——下拉重新整理&上拉載入

     本部落格的重點講述一下自定義LisView,實現下拉重新整理和上拉載入的功能,實現類似於開源專案PullToRefresh的效果。好,既然如此,先看看我實現後的效果圖,再分析:      
        好,效果圖如上所示,下面逐步講解下實現的過程。首先,來觀察一下,ListView上方的佈局,我這裡稱其為“頭佈局”,這個所謂的頭佈局,大致功能是這樣的,一個ImageView顯示上下拉動方向的狀態的,ImageView相同的位置隱藏了一個ProgressBar,用來在資料重新整理時給個提示作用的。還有兩個TextView,上面用來顯示下拉重新整理時提醒使用者是如何操作的,例如“下拉重新整理”“鬆開重新整理”“正在重新整理”,另一個是用來顯示本次重新整理的時間的。比較簡單的佈局,下面是XML程式碼:
<?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="wrap_content"
    android:orientation="horizontal" >

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dip" >

        <ImageView
            android:id="@+id/iv_listview_header_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:minWidth="30dip"
            android:src="@drawable/common_listview_headview_red_arrow" />

        <ProgressBar
            android:id="@+id/pb_listview_header"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:indeterminateDrawable="@drawable/common_progressbar"
            android:visibility="gone" />
    </FrameLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:gravity="center_horizontal"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/tv_listview_header_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="下拉重新整理"
            android:textColor="#FF0000"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/tv_listview_header_last_update_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dip"
            android:text="最後重新整理時間: 2014-10-10 12:56:12"
            android:textColor="@android:color/white"
            android:textSize="14sp" />
    </LinearLayout>

</LinearLayout>
         下面講解一下ListView下方的佈局,我稱其為“腳佈局”,這個腳佈局就更簡單了,直接看XML程式碼好了:
<?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="wrap_content"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_margin="10dip"
        android:gravity="center_vertical"
        android:orientation="horizontal" >

        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:indeterminateDrawable="@drawable/common_progressbar" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dip"
            android:text="載入更多..."
            android:textColor="#FF0000"
            android:textSize="18sp" />
    </LinearLayout>

</LinearLayout>
此外,兩個佈局都用到一個ProgressBar的背景,其XML如下:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360" >

    <shape
        android:innerRadiusRatio="3"
        android:shape="ring"
        android:useLevel="false" >
        <gradient
            android:centerColor="#FF6666"
            android:endColor="#FF0000"
            android:startColor="#FFFFFF"
            android:type="sweep" />
    </shape>

</rotate>
        ListView的頭佈局和腳佈局已經做好了,那麼接下來該怎麼整合到ListView上去呢?首先我們來看看ListView的部分原始碼,很輕鬆能找到這兩個方法:addHeaderView(View v)addFooterView(View v),通過字面的意思就可以理解,這兩個方法分別是向ListView頂部新增一個View和向ListView的底部新增View的,有了這兩個方法,那麼上面的頭佈局和腳佈局就很容易被新增到ListView上了,並且成為ListView的一體。        其實,再看會發現,簡單的使用這兩個方法分別往ListView上新增頭佈局和腳佈局是不合理,新增上去的頭佈局和腳佈局會被顯示出來,並沒有被隱藏掉,為了實現下拉和上拉時能夠將頭佈局和腳佈局都“拉出來”並且還可以鬆開時,再次隱藏起來,我們可以使用View下的一個方法setPadding(int left, int top, int right, int bottom),這個方法設定View的Padding屬性,這裡,我們不必管left、right、bottom,只要設定top的值為頭佈局或者腳佈局的高度即可“隱藏”這兩個佈局,而且還可以在手指滑動螢幕的時候,動態的設定這個top的值,來實現頭佈局和腳佈局的顯示-隱藏-顯示。        還有一個非常重要的話題,就是這個top的值還設定為多少合適?上面說了,我們來頭佈局來說明一下,隱藏這個頭佈局需要將top值設定成top=- 頭佈局高度,那麼這個頭佈局的高度怎麼求得呢?很顯然,使用getHeight()是得不到頭佈局高度的,因為getHeight()方法是先控制元件在螢幕上展示完畢後得到的高度,顯然在這裡,這個ListView還在構建中,並沒有展示到螢幕上。所以注意了,我們先呼叫View下的measure(int widthMeasureSpec, int heightMeasureSpec)方法,將widthMeasureSpec和heightMeasureSpec分別設定為0,這裡的widthMeasureSpec和heightMeasureSpec並不是一個準備的值,而且指定一個規格或者標準讓系統幫我們測量View的寬高,當我們指定widthMeasureSpec和heightMeasureSpec分別為0的時候,系統將不採用這個規格去測量,而是根據實際情況去測量。之後,我們可以呼叫View下的getMeasuredHeight()方法獲取真實的頭佈局的高度,然後設定top = - 頭佈局實際高度,實現隱藏頭佈局。
public class RefreshListView extends ListView implements OnScrollListener {

	private static final String TAG = "RefreshListView";
	private int firstVisibleItemPosition; // 螢幕顯示在第一個的item的索引
	private int downY; // 按下時y軸的偏移量
	private int headerViewHeight; // 頭佈局的高度
	private View headerView; // 頭佈局的物件

	private final int DOWN_PULL_REFRESH = 0; // 下拉重新整理狀態
	private final int RELEASE_REFRESH = 1; // 鬆開重新整理
	private final int REFRESHING = 2; // 正在重新整理中
	private int currentState = DOWN_PULL_REFRESH; // 頭佈局的狀態: 預設為下拉重新整理狀態

	private Animation upAnimation; // 向上旋轉的動畫
	private Animation downAnimation; // 向下旋轉的動畫

	private ImageView ivArrow; // 頭佈局的剪頭
	private ProgressBar mProgressBar; // 頭佈局的進度條
	private TextView tvState; // 頭佈局的狀態
	private TextView tvLastUpdateTime; // 頭佈局的最後更新時間

	private OnRefreshListener mOnRefershListener;
	private boolean isScrollToBottom; // 是否滑動到底部
	private View footerView; // 腳佈局的物件
	private int footerViewHeight; // 腳佈局的高度
	private boolean isLoadingMore = false; // 是否正在載入更多中

	public RefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initHeaderView();
		initFooterView();
		this.setOnScrollListener(this);
	}

	/**
	 * 初始化腳佈局
	 */
	private void initFooterView() {
		footerView = View.inflate(getContext(), R.layout.listview_footer, null);
		footerView.measure(0, 0);
		footerViewHeight = footerView.getMeasuredHeight();
		footerView.setPadding(0, -footerViewHeight, 0, 0);
		this.addFooterView(footerView);
	}

	/**
	 * 初始化頭佈局
	 */
	private void initHeaderView() {
		headerView = View.inflate(getContext(), R.layout.listview_header, null);
		ivArrow = (ImageView) headerView
				.findViewById(R.id.iv_listview_header_arrow);
		mProgressBar = (ProgressBar) headerView
				.findViewById(R.id.pb_listview_header);
		tvState = (TextView) headerView
				.findViewById(R.id.tv_listview_header_state);
		tvLastUpdateTime = (TextView) headerView
				.findViewById(R.id.tv_listview_header_last_update_time);

		// 設定最後重新整理時間
		tvLastUpdateTime.setText("最後重新整理時間: " + getLastUpdateTime());

		headerView.measure(0, 0); // 系統會幫我們測量出headerView的高度
		headerViewHeight = headerView.getMeasuredHeight();
		headerView.setPadding(0, -headerViewHeight, 0, 0);
		this.addHeaderView(headerView); // 向ListView的頂部新增一個view物件
		initAnimation();
	}

	/**
	 * 獲得系統的最新時間
	 * 
	 * @return
	 */
	private String getLastUpdateTime() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.format(System.currentTimeMillis());
	}

	/**
	 * 初始化動畫
	 */
	private void initAnimation() {
		upAnimation = new RotateAnimation(0f, -180f,
				Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
				0.5f);
		upAnimation.setDuration(500);
		upAnimation.setFillAfter(true); // 動畫結束後, 停留在結束的位置上

		downAnimation = new RotateAnimation(-180f, -360f,
				Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
				0.5f);
		downAnimation.setDuration(500);
		downAnimation.setFillAfter(true); // 動畫結束後, 停留在結束的位置上
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
			case MotionEvent.ACTION_DOWN :
				downY = (int) ev.getY();
				break;
			case MotionEvent.ACTION_MOVE :
				int moveY = (int) ev.getY();
				// 移動中的y - 按下的y = 間距.
				int diff = (moveY - downY) / 2;
				// -頭佈局的高度 + 間距 = paddingTop
				int paddingTop = -headerViewHeight + diff;
				// 如果: -頭佈局的高度 > paddingTop的值 執行super.onTouchEvent(ev);
				if (firstVisibleItemPosition == 0
						&& -headerViewHeight < paddingTop) {
					if (paddingTop > 0 && currentState == DOWN_PULL_REFRESH) { // 完全顯示了.
						Log.i(TAG, "鬆開重新整理");
						currentState = RELEASE_REFRESH;
						refreshHeaderView();
					} else if (paddingTop < 0
							&& currentState == RELEASE_REFRESH) { // 沒有顯示完全
						Log.i(TAG, "下拉重新整理");
						currentState = DOWN_PULL_REFRESH;
						refreshHeaderView();
					}
					// 下拉頭佈局
					headerView.setPadding(0, paddingTop, 0, 0);
					return true;
				}
				break;
			case MotionEvent.ACTION_UP :
				// 判斷當前的狀態是鬆開重新整理還是下拉重新整理
				if (currentState == RELEASE_REFRESH) {
					Log.i(TAG, "重新整理資料.");
					// 把頭佈局設定為完全顯示狀態
					headerView.setPadding(0, 0, 0, 0);
					// 進入到正在重新整理中狀態
					currentState = REFRESHING;
					refreshHeaderView();

					if (mOnRefershListener != null) {
						mOnRefershListener.onDownPullRefresh(); // 呼叫使用者的監聽方法
					}
				} else if (currentState == DOWN_PULL_REFRESH) {
					// 隱藏頭佈局
					headerView.setPadding(0, -headerViewHeight, 0, 0);
				}
				break;
			default :
				break;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * 根據currentState重新整理頭佈局的狀態
	 */
	private void refreshHeaderView() {
		switch (currentState) {
			case DOWN_PULL_REFRESH : // 下拉重新整理狀態
				tvState.setText("下拉重新整理");
				ivArrow.startAnimation(downAnimation); // 執行向下旋轉
				break;
			case RELEASE_REFRESH : // 鬆開重新整理狀態
				tvState.setText("鬆開重新整理");
				ivArrow.startAnimation(upAnimation); // 執行向上旋轉
				break;
			case REFRESHING : // 正在重新整理中狀態
				ivArrow.clearAnimation();
				ivArrow.setVisibility(View.GONE);
				mProgressBar.setVisibility(View.VISIBLE);
				tvState.setText("正在重新整理中...");
				break;
			default :
				break;
		}
	}

	/**
	 * 當滾動狀態改變時回撥
	 */
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {

		if (scrollState == SCROLL_STATE_IDLE
				|| scrollState == SCROLL_STATE_FLING) {
			// 判斷當前是否已經到了底部
			if (isScrollToBottom && !isLoadingMore) {
				isLoadingMore = true;
				// 當前到底部
				Log.i(TAG, "載入更多資料");
				footerView.setPadding(0, 0, 0, 0);
				this.setSelection(this.getCount());

				if (mOnRefershListener != null) {
					mOnRefershListener.onLoadingMore();
				}
			}
		}
	}

	/**
	 * 當滾動時呼叫
	 * 
	 * @param firstVisibleItem
	 *            當前螢幕顯示在頂部的item的position
	 * @param visibleItemCount
	 *            當前螢幕顯示了多少個條目的總數
	 * @param totalItemCount
	 *            ListView的總條目的總數
	 */
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		firstVisibleItemPosition = firstVisibleItem;

		if (getLastVisiblePosition() == (totalItemCount - 1)) {
			isScrollToBottom = true;
		} else {
			isScrollToBottom = false;
		}
	}

	/**
	 * 設定重新整理監聽事件
	 * 
	 * @param listener
	 */
	public void setOnRefreshListener(OnRefreshListener listener) {
		mOnRefershListener = listener;
	}

	/**
	 * 隱藏頭佈局
	 */
	public void hideHeaderView() {
		headerView.setPadding(0, -headerViewHeight, 0, 0);
		ivArrow.setVisibility(View.VISIBLE);
		mProgressBar.setVisibility(View.GONE);
		tvState.setText("下拉重新整理");
		tvLastUpdateTime.setText("最後重新整理時間: " + getLastUpdateTime());
		currentState = DOWN_PULL_REFRESH;
	}

	/**
	 * 隱藏腳佈局
	 */
	public void hideFooterView() {
		footerView.setPadding(0, -footerViewHeight, 0, 0);
		isLoadingMore = false;
	}
}
        還有2個重要的問題就是,1.我們如何知道這個頭佈局的狀態什麼時候改變?顯示,當我們將頭佈局完全拉下的時候,我們就應該改變頭佈局上面的一些描述的資訊。那麼,怎麼判斷這個頭佈局什麼時候重新整理狀態呢?提供如下的演算法供參考: 1,計算手指在螢幕中滑動的間距。       移動中的Y軸座標 - 按下的Y軸座標 = 間距 2,計算頭佈局距離頂部的距離       -頭佈局的高度 + 間距 = paddingTop 3,如果: -頭佈局的高度 > paddingTop的值 執行super.onTouchEvent(ev);手指繼續滑動,頭佈局狀態為“鬆開重新整理”;       如果:-頭佈局的高度 < paddingTop的值,返回true,頭佈局狀態為“下拉重新整理” 4,手指擡起的時候,重新整理        問題2是如何知道,腳佈局滑動到了ListView的底部,即最後一個可見的item。我們可以藉助android.widget.AbsListView.OnScrollListener介面下的兩個方法onScrollStateChanged(AbsListView view, int scrollState)onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount),現在ListView滾動時被呼叫的onScroll()方法中,判斷當前的Item是不是最後一個Item,如果是的話就標記一個boolean的狀態值,然後在監聽ListView滑動狀態onScrollStateChanged方法中,判斷狀態scrollState == SCROLL_STATE_IDLE|| scrollState == SCROLL_STATE_FLING;若是,就說明ListView滑動到了底部了,這是需要上拉載入更多的資料。詳細請參考上面貼出的程式碼,帶上註釋去看,也很好理解。

4.為ListView添加回調函式

       上面的效果是實現了,主要就講解了UI的實現,接下來,我們瞭解一下邏輯方法的東西。顯然,這個ListView還不能動態的“下來重新整理”和“上拉載入”,為什麼呢?很簡單啊,暫時還沒有向外界暴露出一個設定資料的方法。為了實現這個能夠動態實施資料更新的功能,我們需要寫一個回撥提供給其它的類使用,首先看一下這個回撥的介面:
public interface OnRefreshListener {

	/**
	 * 下拉重新整理
	 */
	void onDownPullRefresh();

	/**
	 * 上拉載入更多
	 */
	void onLoadingMore();
}
這個回撥的介面定義了兩個方法,分別是“下拉重新整理”和“上拉載入”,然後還必須在ListView中暴露一個介面與外面的類連結,最好的方法就暴露公共方法,例如:
public void setOnRefreshListener(OnRefreshListener listener) {
		mOnRefershListener = listener;
	}
這樣這個介面的物件就在ListView中建立了,我們只要拿著這個物件,就可以在相應的位置上呼叫該物件的“下拉重新整理”“上拉載入”的方法了,不必在乎方法體是什麼,因為具體實現的方式,具體的資料結構都是其他類中定義的,我們只要提供實現的方式即可。

5.使用這個自定義的ListView

       使用這個自定義的ListView特別簡單的,這裡不多說了,看程式碼:
public class MainActivity extends Activity implements OnRefreshListener {

	private List<String> textList;
	private MyAdapter adapter;
	private RefreshListView rListView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		rListView = (RefreshListView) findViewById(R.id.refreshlistview);
		textList = new ArrayList<String>();
		for (int i = 0; i < 25; i++) {
			textList.add("這是一條ListView的資料" + i);
		}
		adapter = new MyAdapter();
		rListView.setAdapter(adapter);
		rListView.setOnRefreshListener(this);
	}

	private class MyAdapter extends BaseAdapter {

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

		@Override
		public Object getItem(int position) {
			// TODO Auto-generated method stub
			return textList.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
			TextView textView = new TextView(MainActivity.this);
			textView.setText(textList.get(position));
			textView.setTextColor(Color.WHITE);
			textView.setTextSize(18.0f);
			return textView;
		}

	}

	@Override
	public void onDownPullRefresh() {
		new AsyncTask<Void, Void, Void>() {

			@Override
			protected Void doInBackground(Void... params) {
				SystemClock.sleep(2000);
				for (int i = 0; i < 2; i++) {
					textList.add(0, "這是下拉刷新出來的資料" + i);
				}
				return null;
			}

			@Override
			protected void onPostExecute(Void result) {
				adapter.notifyDataSetChanged();
				rListView.hideHeaderView();
			}
		}.execute(new Void[]{});
	}

	@Override
	public void onLoadingMore() {
		new AsyncTask<Void, Void, Void>() {

			@Override
			protected Void doInBackground(Void... params) {
				SystemClock.sleep(5000);

				textList.add("這是載入更多出來的資料1");
				textList.add("這是載入更多出來的資料2");
				textList.add("這是載入更多出來的資料3");
				return null;
			}

			@Override
			protected void onPostExecute(Void result) {
				adapter.notifyDataSetChanged();

				// 控制腳佈局隱藏
				rListView.hideFooterView();
			}
		}.execute(new Void[]{});
	}

}
我們模擬的是聯網更新資料,所以必須要開啟新的執行緒去獲取資料,聯網獲取資料的方式有很多種,我這裡使用的Android為我們提供好的AsyncTask輕量型的框架,關於這個框架,在下面有一些簡單的介紹。

6.AsyncTask簡單介紹

先來看看AsyncTask的定義:
public abstract class AsyncTask<Params, Progress, Result> {

三種泛型型別分別代表

Params :“啟動任務執行的輸入引數”,Progress:“後臺任務執行的進度”,Result:“後臺計算結果的型別”。在特定場合下,並不是所有型別都被使用,如果沒有被使用,可以用java.lang.Void型別代替。

一個非同步任務的執行一般包括以下幾個步驟:

1.execute(Params... params),執行一個非同步任務,需要我們在程式碼中呼叫此方法,觸發非同步任務的執行。

2.onPreExecute(),在execute(Params... params)被呼叫後立即執行,一般用來在執行後臺任務前對UI做一些標記。

3.doInBackground(Params... params),在onPreExecute()完成後立即執行,用於執行較為費時的操作,此方法將接收輸入引數和返回計算結果。在執行過程中可以呼叫publishProgress(Progress... values)來更新進度資訊。

4.onProgressUpdate(Progress... values),在呼叫publishProgress(Progress... values)時,此方法被執行,直接將進度資訊更新到UI元件上。

5.onPostExecute(Result result),當後臺操作結束時,此方法將會被呼叫,計算結果將做為引數傳遞到此方法中,直接將結果顯示到UI元件上。

下面是一個圖解:
通過這個草圖,我們可以分析出AsyncTask三個泛型引數的用處,1,第一個泛型引數Params就是execute()方法中的引數,這二者要保持一致,因為這個引數Params會直接傳遞給doInBackground(Params...params)方法中,作為這個方法的引數存在。2,第二個引數Progress代表任務執行的進度,通常設定為Void型別,不使用它。3,第三個引數Result代表的是doInBackground()方法的返回值型別,這個返回值型別決定該方法在子執行緒中獲取的是什麼型別的資料,並且獲取到的資料將被傳遞給onPostExecute()方法中作為引數,就是程式執行的結果,在該方法中拿到這個結果在UI上實現資料更新。        以上是對AsyncTask的簡單介紹,沒有深入,想要深入瞭解AsyncTask,請移步到以下這兩篇部落格中。

相關推薦

Android定義控制元件——ListView重新整理載入

1.簡介        無疑,在Android開發中,ListView是使用非常頻繁的控制元件之一,ListView提供一個列表的容易,允許我們以列表的形式將資料展示到介面上,但是Google給我們提供的原生ListView的控制元件,雖然在功能上很強大,但是在使用者體

Android定義控制元件之入門篇---整理網路的資源

前言, 我的視訊系列 http://edu.csdn.net/course/detail/2741, 一起來學習Android… 本篇部落格主要是想要講解一下自定義控制元件如何入門,其中有好多資料資源來源自網路,綜合了網路上一些有些的博文

Android定義控制元件(一) 重新整理分頁載入更多(支援ListView, GridView, ScrollView)

        首先說明,這幾篇文章是對一些前輩的成果進行學習後的心得總結。也借這種方式對他們表示謝意。         最近專案中好幾個模組都用到listview和gridview的下拉重新整理,上拉載入更多等功能,而且涉及到圖片的批量下載。水平有限,首先是想到找一些比較

Android定義控制元件之仿汽車之家重新整理

關於下拉重新整理的實現原理我在上篇文章Android自定義控制元件之仿美團下拉重新整理中已經詳細介紹過了,這篇文章主要介紹錶盤的動畫實現原理 汽車之家的下拉重新整理分為三個狀態: 第一個狀態為下拉重新整理狀態(pull to refresh),在這個狀

Android定義控制元件實戰——實現仿IOS重新整理載入 PullToRefreshLayout

         下拉重新整理控制元件,網上有很多版本,有自定義Layout佈局的,也有封裝控制元件的,各種實現方式的都有。但是很少有人告訴你具體如何實現的,今天我們就來一步步實現自己封裝的 PullToRefreshLayout 完美的解決下拉重新整理,上拉載入問題。  

Android定義控制元件系列八:詳解onMeasure()(二)--利用onMeasure測量來實現圖片伸永不變形,解決螢幕適配問題

        上一篇文章詳細講解了一下onMeasure/measure方法在Android自定義控制元件時的原理和作用,參看博文:Android自定義控制元件系列七:詳解onMeasure()方法中如何測量一個控制元件尺寸(一),今天就來真正實踐一下,讓這兩個方法大顯神威來幫我們搞定圖片的螢幕適配問題。

Android 定義view:實現ListView的視差特效

一、概述: 現在流型的APP如微信朋友圈,QQ空間,微博個人展示都有視差特效的影子。 如圖:下拉圖片會產生圖片拉昇的效果,放手後圖片有彈回到原處: 那我們如何實現呢? 1)重寫ListView控制元件: 2)重寫裡面的overScrollBy方法

Android定義控制元件2:定義劃線的文字或按鈕、組合使用可切換tab

package com.custom.controls.button; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.u

android-定義控制元件

自定義控制元件兩種方式 1、繼承ViewGroup 例如:ViewGroup , LinearLayout, FrameLayout, RelativeLayout等。 2、繼承View 例如:View, TextView, ImageView, Button等。 View的測量

Android 定義控制元件-星級評分

在學習自定義控制元件時需要一些例子來練練手,本文這個控制元件就是在這種環境下產生的(可能有BUG); 這個控制元件設計的特點: 1,可以任意修改星星數量 2,可以星星大小會隨控制元件大小而縮小,在控制元件足夠大的情況可以任意設定星星大小 3,滑動監聽,根據滑動距離選擇星級 4,可

[Android定義控制元件] Android定義控制元件

轉載自: http://blog.163.com/[email protected]/blog/static/103242241201382210910473/ 開發自定義控制元件的步驟: 1、瞭解View的工作原理  2、

Android 定義控制元件釋義

由於經常在android的開發過過程中與控制元件打交道,所以有些android控制元件並不能滿足我們的需求, 進而需要自定義一 些為我們所用,但是本文只是一些名詞解析,具體實現可以網路搜尋獲得答案,在此只是個人的筆錄 1:自定義控制元件方式: 1.1:繼承view,button,

Android定義控制元件】炫酷的底部導航欄

https://github.com/WakeHao/NavBar 基本使用 使用這個控制元件,只需要簡單的幾部 引入該控制元件到你的專案中 compile 'com.chen.wakehao.library:bottom-navigation-bar:1.0.0'

Android定義控制元件之區域性圖片放大鏡--BiggerView

零、前言: 本文的知識點一覽 1.自定義控制元件及自定義屬性的寫法,你也將對onMesure有更深的認識 2.關於bitmap的簡單處理,及canvas區域裁剪 3.本文會實現兩個自定義控制元件:FitImageView(圖片自適應)和BiggerView(放大鏡),前者為後者作為鋪墊。 4.最後會

Android學習(十)—— Android定義控制元件

Android自定義控制元件 安卓在使用中大多數使用已有的一些控制元件,用法比較簡單,還有一部分是比較複雜的、使用者自己想的控制元件,這些就需要進行自定義控制元件,今天就來簡單說一下自定義控制元件。 1、繪製過程 建立一個類,繼承View類 onMeasure()方法,測量計算檢視的大小

Android定義控制元件-不同形狀的ImageView

實現分析: 依然是用到Paint的圖層混合模式, 1、畫一個你想要的形狀 || 圖片 2、修改Paint的圖層混合模式,畫圖片。 3、然後就能得到你想要的形狀的ImageView paint.setXfermode(new PorterDuffXfermode(Mode

Android定義控制元件----繼承ViewGroup側滑選單3,普通側滑選單,新增選單切換按鈕(完結)

專案結構: 思路: 對外暴露一個選單開啟和關閉選單的方法toggle 當開啟時this.smoothScrollTo(0, 0);滑動選單顯示 當關閉時this.smoothScrollTo(mMenuWidth, 0);選單隱藏

Android定義控制元件view(草稿版)

Ⅰ、繼承現有控制元件,對其控制元件的功能進行拓展。(拓展功能) Ⅱ、將現有控制元件進行組合,實現功能更加強大控制元件。(佈局重用) Ⅲ、重寫View實現全新的控制元件(不規則效果控制元件) 本文來討論最難的一種自定義控制元件形式,重寫View來實現全新的控制元件。 1.構

Android定義控制元件屬性(草稿版)

    常用的format型別: 1) string:字串型別; 2) integer:整數型別; 3) float:浮點型; 4) dimension:尺寸,後面必須跟dp、dip、px、sp等單位; 5) Boolean:布林值; 6) refer

Android定義控制元件 帶文字提示的SeekBar

轉載請註明出處:juejin.im/post/5c1af0… 本文出自 容華謝後的部落格 1.寫在前面 SeekBar控制元件在開發中還是比較常見的,比如音視訊進度、音量調節等,但是原生控制元件有時還不能滿足我們的需求,今天就來學習一下如何自定義SeekBar控制元件,本文主要實現了一個帶文