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——下拉重新整理&上拉載入
好,效果圖如上所示,下面逐步講解下實現的過程。首先,來觀察一下,ListView上方的佈局,我這裡稱其為“頭佈局”,這個所謂的頭佈局,大致功能是這樣的,一個ImageView顯示上下拉動方向的狀態的,ImageView相同的位置隱藏了一個ProgressBar,用來在資料重新整理時給個提示作用的。還有兩個TextView,上面用來顯示下拉重新整理時提醒使用者是如何操作的,例如“下拉重新整理”“鬆開重新整理”“正在重新整理”,另一個是用來顯示本次重新整理的時間的。比較簡單的佈局,下面是XML程式碼:
下面講解一下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="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>
<?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控制元件,本文主要實現了一個帶文