欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

自定义控件:视差特效

发布时间:2025/4/16 编程问答 61 豆豆
生活随笔 收集整理的这篇文章主要介绍了 自定义控件:视差特效 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

ParallaxEffects 视差特效

  • 了解ImageView 的scaleType 属性
  • 掌握ListView 的overScrollBy()方法

应用场景:QQ 空间,微信朋友圈,微博,需要快速定位的列表效果图

界面初始化

填充ListView

自定义ParallaxListView 继承ListView

public class ParallaxListView extends ListView {public ParallaxListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public ParallaxListView(Context context, AttributeSet attrs) {super(context, attrs);}public ParallaxListView(Context context) {super(context);} }

填充ListView 的数据

public class Cheeses {public static final String[] NAMES = new String[]{"宋江", "卢俊义", "吴用","公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智 深","武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", " 穆弘","雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", " 解珍"," 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪 ","魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方 ","郭盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", " 项充","李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿 ","陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永 ", "施恩","周通", "李忠", "杜兴", "汤隆", "邹渊", "邹润", "朱富", "朱贵", "蔡福", "蔡庆", " 李立","李云", "焦挺", "石勇", "孙新", "顾大嫂", "张青", "孙二娘", " 王定六", "郁保四", " 白胜","时迁", "段景柱"}; }

activity_main.xml

<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" ><com.example.parallax.widget.ParallaxListView android:id="@+id/plv"android:layout_width="match_parent"android:layout_height="match_parent"/> </RelativeLayout>

MainActivity 填充数据

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);plv = (ParallaxListView) findViewById(R.id.plv);plv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Cheeses.NAMES)); }

ListView 添加Header

创建header 布局文件

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><ImageView android:id="@+id/iv_header"android:layout_width="match_parent"android:layout_height="160dp"android:contentDescription="@null"android:scaleType="centerCrop"android:src="@drawable/parallax_img"/> </RelativeLayout>

第10 行scaleType 为图片的填充模式

图片填充模式对比

  • matrix:图片宽高不变,以ImageView 左上角为基准向右向下填充ImageView
  • fixXY:图片宽高分别拉伸或压缩,以ImageView 左上角为基准向右向下填充满ImageView 的宽和高
  • fitStart:图片宽高分别拉伸或压缩,以ImageView 左上角为基准向右向下填充满ImageView 的宽,ImageView的高不用管
  • fitCenter:图片宽高分别拉伸或压缩,图片居中显示,填充满ImageView 的宽,ImageView 的高不用管
  • fitEnd:图片宽高分别拉伸或压缩,以ImageView 左下角为基准向右向上填充满ImageView 的宽,ImageView的高不用管
  • center:图片宽高不变,图片居中显示,填充ImageView
  • centerCrop:图片宽高分别拉伸或压缩,图片居中显示,直到填充满ImageView
  • centerInside:原图比ImageView 小,图片居中显示,填充ImageView,原图比ImageView 大,图片宽高分别拉伸或压缩,直到填充满ImageView 的宽或高即可

MainActivity.java 中添加头布局

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);plv = (ParallaxListView) findViewById(R.id.plv);View headerView = View.inflate(this,R.layout.layout_header, null);//添加头布局,需要在setAdapter 前添加plv.addHeaderView(headerView);plv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,Cheeses.NAMES)); }

下拉放大

overScrollBy()方法参数解析

ParallaxListView 需要重写overScrollBy()方法,api 要求9 以上

/*** 滑动到ListView 两端才会调用*/ @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,int scrollY, int scrollRangeX, int scrollRangeY,int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {//deltaY 竖直方向滑动的瞬时变化量,顶部下拉为-,底部上拉为+//scrollY 两端滑动超出的距离,顶部为-,底部为+//scrollRangeY 竖立方向滑动的范围//maxOverScrollY 竖立方向最大的滑动位置//isTouchEvent 是否是用户触摸拉动,true 表示用户手指触摸拉动,false 表示惯性System.out.println("deltaY:" + deltaY + " scrollY:" + scrollY+ " scrollRangeY:" + scrollRangeY + " maxOverScrollY:"+ maxOverScrollY + " isTouchEvent:" + isTouchEvent);return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); }

 理解deltaY 及isTouchEvent 参数的含义

动态改变头布局的高度

ParallaxListView 添加setParallaxImage()方法

private ImageView headerImage; public void setParallaxImage(ImageView imageView){headerImage = imageView; }

MainActivity 调用ParallaxListView 的setParallaxImage()方法

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);plv = (ParallaxListView) findViewById(R.id.plv);View headerView = View.inflate(this,R.layout.layout_header, null);final ImageView headerImage = (ImageView) headerView.findViewById(R.id.iv_header);//等view 的树状结构渲染完毕时,再将headerImage 设置到plv 中headerImage.getViewTreeObserver().addOnGlobalLayoutListener(newOnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {//宽高已经测量完毕plv.setParallaxImage(headerImage);//移除监听,避免下次渲染时还调用headerImage.getViewTreeObserver().removeGlobalOnLayoutListener(this);}});//添加头布局,需要在setAdapter 前添加plv.addHeaderView(headerView);plv.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, Cheeses.NAMES)); }

第7-18 行view 树状结构渲染完毕时,再将头布局中ImageView 设置到ParallaxListView 中,这样在
ParallaxListView 的setParallaxImage()方法中才能获取到ImageView 的宽高
获取头部ImageView 的高度

private int orignalHeight; private int drawableHeight;public void setParallaxImage(ImageView imageView){headerImage = imageView;//ImageView 初始高度orignalHeight = imageView.getHeight();//图片原始高度drawableHeight = imageView.getDrawable().getIntrinsicHeight(); }

顶部下拉时动态设置头部ImageView 的高度

/*** 滑动到ListView 两端才会调用*/ @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,int scrollY, int scrollRangeX, int scrollRangeY,int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {//deltaY 竖直方向滑动的瞬时变化量,顶部下拉为-,底部上拉为+//scrollY 两端滑动超出的距离,顶部为-,底部为+//scrollRangeY 竖立方向滑动的范围//maxOverScrollY 竖立方向最大的滑动位置//isTouchEvent 是否是用户触摸拉动,true 表示用户手指触摸拉动,false 表示惯性System.out.println("deltaY:" + deltaY + " scrollY:" + scrollY+ " scrollRangeY:" + scrollRangeY + " maxOverScrollY:"+ maxOverScrollY + " isTouchEvent:" + isTouchEvent);//顶部下拉,用户触摸时,将deltaY 累加给Headerif(deltaY < 0 && isTouchEvent){int newHeight = headerImage.getHeight()+Math.abs(deltaY);//新高度小于图片原始高度才允许累加变化量if(newHeight <= drawableHeight){//让新的值生效headerImage.getLayoutParams().height = newHeight;headerImage.requestLayout();}}return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); }

第16-24 行动态设置头部ImageView 的高度

回弹动画

ParallaxListView 重写onTouchEvent()方法

@Override public boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_UP://松手时,把currentHeight 恢复到orignalHeightint currentHeight = headerImage.getHeight();//300->160 300,299,280,250,200,...160 随时间生成300 到160 间的值ValueAnimator animator = ValueAnimator.ofInt(currentHeight,orignalHeight);animator.setDuration(500);//动画更新的监听animator.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//获取随时间变化得到的currentHeight 到orignalHeight 间的值int value = (Integer) animation.getAnimatedValue();//让新的值生效headerImage.getLayoutParams().height = value;headerImage.requestLayout();}});//设置插值器实现回弹效果animator.setInterpolator(new OvershootInterpolator(2));animator.start();break;default:break;}return super.onTouchEvent(ev); }

第8-23 行手指抬起时,用属性动画实现headerImage 恢复到初始高度

《新程序员》:云原生和全面数字化实践50位技术专家共同创作,文字、视频、音频交互阅读

总结

以上是生活随笔为你收集整理的自定义控件:视差特效的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。