編輯:關於Android編程
視差效果是什麼?
所謂的視差效果在Web設計和移動應用中都非常常見,我們在一些主要的平台都可以發現它的身影,從Windows Phone到iOS乃至Android。按照維基百科的說法,視差滾動是計算機圖形學中的一種特殊的滾動技術,在此相機移動背景圖像比前景圖像慢,從而引起了視覺深度的假象。
那麼到底什麼是視差效果呢?一起來看效果圖就知道了:
我們可以看到 ListView
的 HeaderView
會跟隨 ListView
的滑動而變大,HeaderView
裡的圖片會有縮放效果。這些可以使用屬性動畫來實現。接下來我們就來動手吧!
首先自定義幾個屬性,在之後可以用到:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ZoomListView"> <!-- headerView的高度 --> <attr name="header_height" format="dimension|reference"></attr> <!-- headerView的最大高度 --> <attr name="header_max_height" format="dimension|reference"></attr> <!-- headerView裡面的圖片最大的伸縮量 --> <attr name="header_max_scale" format="float"></attr> </declare-styleable> </resources>
之後創建 ZoomListView
類,繼承自 ListView
:
public class ZoomListView extends ListView { // 最大的伸縮量 private final float defaultHeaderMaxScale = 1.2f; // 頭部最大的高度 private float headerMaxHeight; // 頭部初始高度 private float headerHeight; // 頭部默認初始高度 private float defaultHeaderHeight; // 頭部默認最大的高度 private float defaultHeaderMaxHeight; private ImageView headerView; private ViewGroup.LayoutParams layoutParams; private LinearLayout linearLayout; // 最大的縮放值 private float headerMaxScale; public ZoomListView(Context context) { this(context, null); } public ZoomListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ZoomListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); defaultHeaderHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 160, context.getResources().getDisplayMetrics()); defaultHeaderMaxHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 240, context.getResources().getDisplayMetrics()); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZoomListView); headerHeight = a.getDimension(R.styleable.ZoomListView_header_height, defaultHeaderHeight); headerMaxHeight = a.getDimension(R.styleable.ZoomListView_header_max_height, defaultHeaderMaxHeight); headerMaxScale = a.getFloat(R.styleable.ZoomListView_header_max_scale, defaultHeaderMaxScale); a.recycle(); initView(); } ... }
到這裡都是按部就班式的,設置好自定義屬性的初始值,之後調用 initView()
,那就來看看 initView()
方法:
private void initView() { headerView = new ImageView(getContext()); headerView.setScaleType(ImageView.ScaleType.CENTER_CROP); linearLayout = new LinearLayout(getContext()); linearLayout.addView(headerView); layoutParams = headerView.getLayoutParams(); if (layoutParams == null) { layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) headerHeight); } else { layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; layoutParams.height = (int) headerHeight; } headerView.setLayoutParams(layoutParams); addHeaderView(linearLayout); } public void setDrawableId(int id) { headerView.setImageResource(id); }
可以看出在 initView()
裡我們創建了 headerView
,並添加到了ListView
的頭部。而 setDrawableId(int id)
就是給 headerView
設置相關圖片的。
下面就是視差效果的主要實現代碼了:
@Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { if (deltaY < 0 && isTouchEvent) { if (headerView.getHeight() < headerMaxHeight) { int newHeight = headerView.getHeight() + Math.abs(deltaY / 3); headerView.getLayoutParams().height = newHeight; headerView.requestLayout(); float temp = 1 + (headerMaxScale - 1f) * (headerView.getHeight() - headerHeight) / (headerMaxHeight - headerHeight); headerView.animate().scaleX(temp) .scaleY(temp).setDuration(0).start(); } } return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); }
我們重寫了 overScrollBy()
方法,當 deltaY 小於0時(即 ListView
已經到頂端,但是用戶手勢還是向下拉),去動態地設置 headerView
的高度以及 headerView
的 scale
值。這樣就可以產生 headerView
變高以及圖片放大的效果了。
接下來要考慮的問題就是當用戶松開手指時,要恢復回原來的樣子。所以我們應該在 onTouchEvent(MotionEvent ev)
裡去實現相關操作:
@Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_UP: startAnim(); break; } return super.onTouchEvent(ev); } // 開始執行動畫 private void startAnim() { ValueAnimator animator = ValueAnimator.ofFloat(headerView.getHeight(), headerHeight); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float fraction = (float) animation.getAnimatedValue(); headerView.getLayoutParams().height = (int) fraction; headerView.requestLayout(); } }); animator.setDuration(500); animator.setInterpolator(new LinearInterpolator()); ValueAnimator animator2 = ValueAnimator.ofFloat(headerView.getScaleX(), 1f); animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float fraction = (float) animation.getAnimatedValue(); headerView.setScaleX(fraction); headerView.setScaleY(fraction); } }); animator2.setDuration(500); animator2.setInterpolator(new LinearInterpolator()); animator.start(); animator2.start(); }
上面的代碼簡單點來說,就是在 ACTION_UP 時,去開始兩個屬性動畫,一個屬性動畫是將 headerView
的高度恢復成原來的值,另一個屬性動畫就是把 headerView
的 scale 重新恢復為1f。相信大家都可以看懂的。
總結
以上就是這篇文章的全部內容了,希望這篇文章的內容對各位Android開發者們能有所幫助,如果有疑問大家可以留言交流。
看了很長時間Vold存儲模塊的相關知識,也深入的研究一段時間的Android源碼,打算把自己看過的經驗之貼、參考資料和自己的一些見解,以帖子的形式發出來,供有興趣的同仁們
在我們第一次安裝app的時候,有一些app會出現一個覆蓋在我們原來View上面的浮層view,用來做一個app的指示,讓用戶更快的知道app的整體架構和功能點。下面大家看
引言隨著項目中動態鏈接庫越來越多,我們也遇到了很多奇怪的問題,比如只在某一種 OS 上會出現的 java.lang.UnsatisfiedLinkError,但是明明我們
寫程序的過程中,想法總會不斷地變,有時候會很糾結,到底做哪種效果好,怎麼做好呢? 就比如這個音樂播放器,我原來的想法是把列表頁面跟歌詞頁面放在同一個Activity中的兩