最近一直想做下拉刷新的效果,琢磨了好久,才走到通過onTouch方法把整個視圖往下拉的步驟,接下來就是能拉下來,鬆開手要能滑回去啊。網上看了好久,沒有找到詳細的下拉刷新的例子,只有自己慢慢琢磨了。昨天和今天,研究了兩天,下拉之後回滾回去的效果終於今天做出來了!開心。現在來分享下我的實現方法和一些心得體會吧。
我看了網上一個大神的例子,發現是在onTouch裏面使用View的scrollTo(int, int)方法,來使整個視圖往下滾動的,我嘗試了使用setTranslationY()來對視圖進行回滾,第一次是沒問題的,但多滾動幾次之後,整個視圖實際上已經到了非常“高”的地方了,要拉很長的距離才能看到內容。所以回滾也必須使用scrollTo(int, int)方法來操作。
但scrollTo(int, int)執行是瞬間的,方法名講是滾動到,實際上就是“瞬移到”某個位置,因此需要一步一步的去瞬移它,讓它看上去是滾過去的……
因爲等下要去跑步了,還有就是也沒有什麼很多的要點需要講解,我就直接上代碼給大家看,註釋都寫好了,要點會單獨提一下,更詳細的講解與心得就等着我哪天把下拉刷新實現了吧。
/** * @desc 平滑滾動 * @param v 需要操控的視圖 * @param fromY 起始Y座標 * @param toY 終止Y座標 * @param fps 幀率 * @param durtion 動畫完成時間(毫秒) * */ private void smoothScroll(View v, int fromY, int toY, int fps, long durtion) { smoothScrollThread = new SmoothScrollThread(v, fromY, toY, durtion, fps); smoothScrollThread.run(); } /** * @desc 平滑滾動線程,用於遞歸調用自己來實現某個視圖的平滑滾動 * */ class SmoothScrollThread implements Runnable { //需要操控的視圖 private View v = null; //原Y座標 private int fromY = 0; //目標Y座標 private int toY = 0; //動畫執行時間(毫秒) private long durtion = 0; //幀率 private int fps = 60; //間隔時間(毫秒),間隔時間 = 1000 / 幀率 private int interval = 0; //啓動時間,-1 表示尚未啓動 private long startTime = -1; //減速插值器 private DecelerateInterpolator decelerateInterpolator = null; /** * @desc 構造方法,做好第一次配置 * */ public SmoothScrollThread(View v, int fromY, int toY, long durtion, int fps) { this.v = v; this.fromY = fromY; this.toY = toY; this.durtion = durtion; this.fps = fps; this.interval = 1000 / this.fps; decelerateInterpolator = new DecelerateInterpolator(); } @Override public void run() { //先判斷是否是第一次啓動,是第一次啓動就記錄下啓動的時間戳,該值僅此一次賦值 if (startTime == -1) { startTime = System.currentTimeMillis(); } //得到當前這個瞬間的時間戳 long currentTime = System.currentTimeMillis(); //放大倍數,爲了擴大除法計算的浮點精度 int enlargement = 1000; //算出當前這個瞬間運行到整個動畫時間的百分之多少 float rate = (currentTime - startTime) * enlargement / durtion; //這個比率不可能在 0 - 1 之間,放大了之後即是 0 - 1000 之間 rate = Math.min(rate, 1000); //將動畫的進度通過插值器得出響應的比率,乘以起始與目標座標得出當前這個瞬間,視圖應該滾動的距離。 int changeDistance = (int) ((fromY - toY) * decelerateInterpolator.getInterpolation(rate / enlargement)); int currentY = fromY - changeDistance; v.scrollTo(0, currentY); if (currentY != toY) { postDelayed(this, this.interval); } else { return; } } public void stop() { removeCallbacks(this); } }
一些要點:
1.使用線程的目的是可以遞歸的調用自己,在每個run()方法裏只滾動一點點,這個一點點根據幀率和插值器來決定。
2.插值器實際上就是一個函數(數學裏的函數),輸入0-1之間的浮點數,輸出0-1之間的浮點數,輸出的曲線是什麼樣的,就看是什麼插值器了,decelerate就是減速插值器了,在平面直角座標系裏面,x值均勻變化,y軸的變化越來越慢。
3.放大倍數(就是那裏乘以1000)是爲了提高精度,因爲通過實踐發現用已經過的毫秒數除以整個動畫週期得出的結果是0.0 -> 0.0 -> 0.0 -> 0.0 -> 1.0 -> 1.0 -> 1.0 -> 1.0 -> 1.0 -> 2.0 -> 2.0 -> 2.0,雖然是浮點數,但精度卻莫名的保持在個位數上,乘以1000後,便會出現0-1000的均勻變化,這個時候去除以1000,便可得到0.000 - 1.000之間的均勻變化的數。
4.還有個很奇葩的是MotionEvent.getY()的值和scrollTo(int,int)的值貌似不是在同一個座標系裏面的。這個還有待進一步的分析和研究啊。