滑動ListView自動隱藏頁面頭部和底部元素的例子
來源:程序員人生 發布時間:2014-12-10 08:57:02 閱讀次數:3621次
完全工程代碼在這:https://github.com/NashLegend/Auto-Hide-ListView
現在很多軟件都有這類滑動列表的時候自動隱藏頁面頭部和底部元素的功能,比如Google+。在剛剛進入Activity的時候,頁面是1個列表,底部有1個view,頭部1個view,當列表向上滑動的時候,隱藏頭尾元素,以顯示更多內容,當列表向下滑動的時候,再將頭尾元素拉出來。比如Google+。
剛剛進入時是這個模樣:
再把列表身上1拉,頭尾隱藏,成了這個模樣:
再往下拉,就會再變回第1張圖的模樣。
這個例籽實現的就是這個功能
這個例子里面,MainActivity的布局以下,ToolBar是頂部元素,Button為底部元素。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:headerDividersEnabled="false" />
<android.support.v7.widget.Toolbar
android:id="@+id/action_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/holo_blue_light" />
<Button
android:id="@+id/footer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="@string/ScrollDown" />
</RelativeLayout>
public class MainActivity extends ActionBarActivity {
ListView listView;
Toolbar toolbar;
View header;
View footer;
int touchSlop = 10;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touchSlop = (int) (ViewConfiguration.get(MainActivity.this).getScaledTouchSlop() * 0.9);//轉動過量少距離后才開始計算是不是隱藏/顯示頭尾元素。這里用了默許touchslop的0.9倍。
listView = (ListView) findViewById(R.id.list_view);
footer = findViewById(R.id.footer);
toolbar = (Toolbar) findViewById(R.id.action_bar);
// 下面這句將這個ToolBar設置為ActionBar,在這個例子里面,這句其實用不著,但是如果用了這句,就得把Theme設置為NoActionBar了,無關這里要說的,具體見上面的鏈接中的Style
setSupportActionBar(toolbar);
//為這個ListView填充元素。
String[] str = new String[64];
for (int i = 0; i < str.length; i++) {
str[i] = "Android " + i;
}
//R.layout.simple_layout是1個TextView,詳見上面的鏈接……
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, R.layout.simple_layout, str);
listView.setAdapter(adapter);
//為ListView添加1個Header,這個Header與ToolBar1樣高。這樣我們可以正確的看到列表中的第1個元素而不被遮住。
header = new View(MainActivity.this);
header.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) getResources().getDimension(R.dimen.abc_action_bar_default_height_material)));
header.setBackgroundColor(Color.parseColor("#00000000"));
listView.addHeaderView(header);
//為ListView設置觸摸事件和轉動事件,這是核心
listView.setOnTouchListener(onTouchListener);
listView.setOnScrollListener(onScrollListener);
footer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//為button設置點擊事件,點擊1次轉動10個item
listView.smoothScrollByOffset(10);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
AnimatorSet backAnimatorSet;//這是顯示頭尾元素使用的動畫
private void animateBack() {
//先清除其他動畫
if (hideAnimatorSet != null && hideAnimatorSet.isRunning()) {
hideAnimatorSet.cancel();
}
if (backAnimatorSet != null && backAnimatorSet.isRunning()) {
//如果這個動畫已在運行了,就不管它
} else {
backAnimatorSet = new AnimatorSet();
//下面兩句是將頭尾元素放回初始位置。
ObjectAnimator headerAnimator = ObjectAnimator.ofFloat(toolbar, "translationY", toolbar.getTranslationY(), 0f);
ObjectAnimator footerAnimator = ObjectAnimator.ofFloat(footer, "translationY", footer.getTranslationY(), 0f);
ArrayList<Animator> animators = new ArrayList<>();
animators.add(headerAnimator);
animators.add(footerAnimator);
backAnimatorSet.setDuration(300);
backAnimatorSet.playTogether(animators);
backAnimatorSet.start();
}
}
AnimatorSet hideAnimatorSet;//這是隱藏頭尾元素使用的動畫
private void animateHide() {
//先清除其他動畫
if (backAnimatorSet != null && backAnimatorSet.isRunning()) {
backAnimatorSet.cancel();
}
if (hideAnimatorSet != null && hideAnimatorSet.isRunning()) {
//如果這個動畫已在運行了,就不管它
} else {
hideAnimatorSet = new AnimatorSet();
ObjectAnimator headerAnimator = ObjectAnimator.ofFloat(toolbar, "translationY", toolbar.getTranslationY(), -toolbar.getHeight());//將ToolBar隱藏到上面
ObjectAnimator footerAnimator = ObjectAnimator.ofFloat(footer, "translationY", footer.getTranslationY(), footer.getHeight());//將Button隱藏到下面
ArrayList<Animator> animators = new ArrayList<>();
animators.add(headerAnimator);
animators.add(footerAnimator);
hideAnimatorSet.setDuration(200);
hideAnimatorSet.playTogether(animators);
hideAnimatorSet.start();
}
}
View.OnTouchListener onTouchListener = new View.OnTouchListener() {
float lastY = 0f;
float currentY = 0f;
//下面兩個表示滑動的方向,大于0表示向下滑動,小于0表示向上滑動,等于0表示未滑動
int lastDirection = 0;
int currentDirection = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastY = event.getY();
currentY = event.getY();
currentDirection = 0;
lastDirection = 0;
break;
case MotionEvent.ACTION_MOVE:
if (listView.getFirstVisiblePosition() > 0) {
//只有在listView.getFirstVisiblePosition()>0的時候才判斷是不是進行顯隱動畫。由于listView.getFirstVisiblePosition()==0時,
//ToolBar――也就是頭部元素必須是可見的,如果這時候候隱藏了起來,那末占位置用了headerview就被用戶發現了
//但是當用戶將列表向下拉露出列表的headerview的時候,應當要讓頭尾元素再次出現才對――這個判斷寫在了后面onScrollListener里面……
float tmpCurrentY = event.getY();
if (Math.abs(tmpCurrentY - lastY) > touchSlop) {//滑動距離大于touchslop時才進行判斷
currentY = tmpCurrentY;
currentDirection = (int) (currentY - lastY);
if (lastDirection != currentDirection) {
//如果與上次方向不同,則履行顯/隱動畫
if (currentDirection < 0) {
animateHide();
} else {
animateBack();
}
}
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
//手指抬起的時候要把currentDirection設置為0,這樣下次不管向哪拉,都與當前的不同(其實在ACTION_DOWN里寫了以后這里就用不著了……)
currentDirection = 0;
lastDirection = 0;
break;
}
return false;
}
};
AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
//這個Listener實際上是用來對付當用戶的手離開列表后列表依然在滑動的情況,也就是SCROLL_STATE_FLING
int lastPosition = 0;//上次轉動到的第1個可見元素在listview里的位置――firstVisibleItem
int state = SCROLL_STATE_IDLE;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//記錄當前列表狀態
state = scrollState;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem == 0) {
animateBack();
}
if (firstVisibleItem > 0) {
if (firstVisibleItem > lastPosition && state == SCROLL_STATE_FLING) {
//如果上次的位置小于當前位置,那末隱藏頭尾元素
animateHide();
}
//================================
if (firstVisibleItem < lastPosition && state == SCROLL_STATE_FLING) {
//如果上次的位置大于當前位置,那末顯示頭尾元素,其實本例中,這個if沒用
//如果是滑動ListView觸發的,那末,animateBack()肯定已履行過了,所以沒有必要
//如果是點擊按鈕啥的觸發轉動,那末根據設計原則,按鈕肯定是頭尾元素之1,所以也不需要animateBack()
//所以這個if塊是不需要的
animateBack();
}
//這里沒有判斷(firstVisibleItem == lastPosition && state == SCROLL_STATE_FLING)的情況,
//但是如果列表中的單個item如果很長的話還是要判斷的,只不過代碼又要多幾行
//但是可以取巧1下,在觸發滑動的時候拖動履行1下animateHide()或animateBack()――本例中的話就寫在那個點擊事件里就能夠了)
//BTW,如果列表的滑動純是靠手滑動列表,而沒有類似于點擊1個按鈕滾到某個位置的話,只要第1個if就夠了…
}
lastPosition = firstVisibleItem;
}
};
}
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈