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