用户要求一个特效,做了一个Demo,分享一下。
1.罗列代码
- ListView headView布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<View
android:id="@+id/placeholder_top"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@android:color/transparent" />
<View
android:id="@+id/placeholder_sticky"
android:layout_width="match_parent"
android:layout_height="50dp" />
</LinearLayout>
- 主布局文件
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<View
android:id="@+id/top"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@drawable/ic_launcher" />
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
<TextView
android:id="@+id/sticky"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#00ffff"
android:gravity="center"
android:textColor="#ff0000"
android:text="Sticky"
android:textSize="20sp" />
</FrameLayout>
- 主代码
package com.example.android.listviewscrolltrick;
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.example.listviewscrolltrick.R;
public class MainActivity extends Activity {
private TextView mStickyView;
private View mPlaceHolderSticky;
private ListView mListView;
private View mTopView;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mStickyView = (TextView) findViewById(R.id.sticky);
mListView = (ListView) findViewById(R.id.listView);
mTopView = findViewById(R.id.top);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View headView = inflater.inflate(R.layout.layout_headview, null);
mPlaceHolderSticky = headView.findViewById(R.id.placeholder_sticky);
mListView.addHeaderView(headView);
mListView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
onScrollChanged();
ViewTreeObserver obs = mListView.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
obs.removeOnGlobalLayoutListener(this);
} else {
obs.removeGlobalOnLayoutListener(this);
}
}
});
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
onScrollChanged();
}
});
List<String> dataSource = new ArrayList<String>();
for (int j = 0; j < 20; j++) {
dataSource.add("item-----" + j);
}
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, dataSource);
mListView.setAdapter(arrayAdapter);
}
private void onScrollChanged() {
View v = mListView.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
if (mListView.getFirstVisiblePosition() == 0) {
mStickyView.setTranslationY(Math.max(mPlaceHolderSticky.getTop()
+ top, 0));
mTopView.setTranslationY(top);
}
}
}
2.大致原理讲解
ListView headView存在两块区域:R.id.placeholder_top /R.id.placeholder_sticky
这两者是“地下工作者”,他们分别与mTopView/mStickView对应——高度相同,颜色透明ListView加载完成后,调整mTopView/mStickView组件的位置
- ListView滑动时,调整mTopView&&mStickyView的位置
3.结合源代码对原理进行深入讲解
- 初始阶段位置的调整
对ListView加载完成进行监听
注意:要取消监听
mListView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
onScrollChanged();
ViewTreeObserver obs = mListView.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
obs.removeOnGlobalLayoutListener(this);
} else {
obs.removeGlobalOnLayoutListener(this);
}
}
});
- 根据ListView中headView中的对应部分 ,设置mTopView/mStickView位置
private void onScrollChanged() {
View v = mListView.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
if (mListView.getFirstVisiblePosition() == 0) {
mStickyView.setTranslationY(Math.max(mPlaceHolderSticky.getTop()
+ top, 0));
mTopView.setTranslationY(top);
}
}
由于mStickyView要一直显示在可见范围内,所以mStickyView.setTranslationY一定不能为负数.
- ListView滑动
监听ListView滑动,调整mTopView/mStickView的位置(同上)
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
onScrollChanged();
}
});