自定义控件:视差特效
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 的高度
頂部下拉時動態設置頭部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位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的自定义控件:视差特效的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自定义控件:SlidingMenu,侧边
- 下一篇: 自定义控件:侧拉删除