实现的效果:
这个是一个ListrView,头部添加一张图片,在下拉时,图片会全部显示出来,松开以后图片还会回弹到原来的位置;
下面直接代码:
ParallaxListView:
/**
* Created by peiyan on 2017/8/16.
* 继承式控件:
* 1.继承ListView,覆写构造方法
* 2.覆写overScrollBy方法,重点关注deltaY,isTouchEvent方法
* 3.暴露一个方法,去得到外界ImageView,并测量ImageView控件的高度
* 4.覆写onTouchEvent方法
*/
public class ParallaxListView extends ListView {
private ImageView header;
private int intrinsicHeight;
private int orignalHeight;
public ParallaxListView(Context context) {
this(context,null);
}
public ParallaxListView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public ParallaxListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setHeader(ImageView header) {
this.header = header;
//获取图片的原始高度
intrinsicHeight = header.getDrawable().getIntrinsicHeight();
//获取imageView的原始高度
orignalHeight = header.getHeight();
}
/**
* 重点:滑动到ListView两端的时候被调用(上部和底部)
* @param deltaX
* @param deltaY 竖直方向滑动的瞬时变化量,顶部下拉为-,底部上拉为+;
* @param scrollX
* @param scrollY
* @param scrollRangeX
* @param scrollRangeY
* @param maxOverScrollX
* @param maxOverScrollY
* @param isTouchEvent 是否是用户触摸拉动,true表示用户手指拉动,false是惯性;
* @return
*
*
*/
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
//通过Log来验证参数的作用
//deltaY=-270; scrollY=0滚动; scrollRangeY=0滚动范围; maxOverScrollY=0最大滚动; isTouchEvent=true是触摸事件;
Log.d("PY","deltaY"+deltaY+
"isTouchEvent"+isTouchEvent);
//顶部下拉,用户触摸的操作才执行视差效果
if (deltaY<0 && isTouchEvent==true){
//deltaY是一个负值,我们要改为绝对值 Math.abs(deltaY),累计给我们的header高度
int newHeight = header.getHeight() + Math.abs(deltaY);
//避免图片无限放大,图片最大不能超过图片本身的高度
//在上面的方法里拿到图片的原始高度
if (newHeight<=intrinsicHeight){
//把新的高度值赋值给控件,改变控件的高度
header.getLayoutParams().height=newHeight;
header.requestLayout();
}
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
//触摸事件,让滑动的图片重新回到原来的样子
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_UP:
int currentHeight = header.getHeight();
//属性动画,改变高度的值,吧我们当前头布局的高度,改为原始的高度
final ValueAnimator animator = ValueAnimator.ofInt(currentHeight, orignalHeight);
//动画更新的监听
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//获取动画执行过程中的分度值
float fraction = animator.getAnimatedFraction();
//获取中间的值,并赋值给控件新高度,可以使控件平稳回弹的效果
Integer animatedValue = (Integer) animator.getAnimatedValue();
//让新的高度值生效
header.getLayoutParams().height=animatedValue;
header.requestLayout();
}
});
//动画的回弹效果。值越大,回弹效果越明显
animator.setInterpolator(new OvershootInterpolator(2));
//设置动画执行时间
animator.setDuration(3000);
//执行动画
animator.start();
}
return super.onTouchEvent(ev);
}
}
chinaese:(自己定义放数据的类)
MainActivity:public class chinaese { public static final String[] NAMES = new String[]{"宋江", "卢俊义", "吴用", "公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智深", "武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", "穆弘", "雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", "解珍", " 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪", "魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方", "郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", "项充", "李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿", "陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永", "施恩", "周通", "李忠", "杜兴", "汤隆", "邹渊", "邹润", "朱富", "朱贵", "蔡福", "蔡庆", "李立", "李云", "焦挺", "石勇", "孙新", "顾大嫂", "张青", "孙二娘", " 王定六", "郁保四", "白胜", "时迁", "段景柱"}; }
/**
* 视差特效实现思路:(两种)
* 1.解析ontouche,Action_Down,Action_move,Action_up;业务逻辑过于复杂
* (用第二种)
* 2.重写ListView的ouverScrollBy方法,继承式自定义控件ListView,根据用户下拉的距离
* 动态修改headerView的高度;
* 在MainActivity中的实现思路:
* a.拷贝文本资源到项目中,自定义控件继承ListView
* b.使用自定义控件,并往头部添加布局,设置适配器
* c.使用视图树,把ImageView传给我们的自定义控件;
*/
public class MainActivity extends AppCompatActivity {
private ParallaxListView plv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
//listView添加一个头布局
View headerView= View.inflate(this, R.layout.layout_header, null);
plv.addHeaderView(headerView);
final ImageView header = (ImageView) headerView.findViewById(R.id.header);
//等View界面全部绘制完毕的时候,去得到已经绘制完控件的宽和高;
header.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//宽和高已经测量完毕
plv.setHeader(header);
//释放资源
header.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
//使用ListView的ArrayAdapter,添加文本item
plv.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,chinaese.NAMES));
}
private void initView() {
plv = (ParallaxListView) findViewById(R.id.plv);
}
}
下面是布局:
activity_main:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent" android:layout_height="match_parent"
>
<com.example.myshichatexiaodemo.View.ParallaxListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/plv"/>
</RelativeLayout>
layout_header:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="160dp"
android:id="@+id/header"
android:src="@drawable/parallax_img"
android:scaleType="centerCrop"
/>
</LinearLayout>