scrollerandroid
『壹』 android 左右滑屏怎麼實現 哪位大神教教我
代碼如下:
package kexc.scroll;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
/**
* 仿Launcher中的WorkSapce,可以左右滑動切換屏幕的類
*
*/
public class ScrollLayout extends ViewGroup {
/*
* onMeasure方法在控制項的父元素正要放置它的子控制項時調用。它會問一個問題,「你想要用多大地方啊?」,然後傳入兩個參數——
* widthMeasureSpec和heightMeasureSpec。它們指明控制項可獲得的空間以及關於這個空間描述的元數據。
* 比返回一個結果要好的方法是你傳遞View的高度和寬度到setMeasuredDimension方法里。
* 一個MeasureSpec包含一個尺寸和模式。
* 有三種可能的模式:
* UNSPECIFIED:父布局沒有給子布局任何限制,子布局可以任意大小。
* EXACTLY:父布局決定子布局的確切大小。不論子布局多大,它都必須限制在這個界限里。
* AT_MOST:子布局可以根據自己的大小選擇任意大小。
*/
/*
* VelocityTracker類
*
* 功能: 根據觸摸位置計算每像素的移動速率。
*
* 常用方法有:
*
* public void addMovement (MotionEvent ev) 功能:添加觸摸對象MotionEvent , 用於計算觸摸速率。
* public void computeCurrentVelocity (int units)
* 功能:以每像素units單位考核移動速率。額,其實我也不太懂,賦予值1000即可。 參照源碼 該units的意思如下: 參數 units :
* The units you would like the velocity in. A value of 1 provides pixels
* per millisecond, 1000 provides pixels per second, etc. public float
* getXVelocity () 功能:獲得X軸方向的移動速率。
*/
/*
* ViewConfiguration類
*
* 功能: 獲得一些關於timeouts(時間)、sizes(大小)、distances(距離)的標准常量值 。
*
* 常用方法:
*
* public int getScaledEdgeSlop()
*
* 說明:獲得一個觸摸移動的最小像素值。也就是說,只有超過了這個值,才代表我們該滑屏處理了。
*
* public static int getLongPressTimeout()
*
* 說明:獲得一個執行長按事件監聽(onLongClickListener)的值。也就是說,對某個View按下觸摸時,只有超過了
*
* 這個時間值在,才表示我們該對該View回調長按事件了;否則,小於這個時間點松開手指,只執行onClick監聽
*/
private static final String TAG = "ScrollLayout";
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private int mCurScreen;//當前屏幕
private int mDefaultScreen = 0;
//兩種狀態: 是否處於滑屏狀態
private static final int TOUCH_STATE_REST = 0;//靜止狀態
private static final int TOUCH_STATE_SCROLLING = 1;//滑屏狀態
private static final int SNAP_VELOCITY = 600; //最小的滑動速率
private int mTouchState = TOUCH_STATE_REST;
private int mTouchSlop;// change 多少像素算是發生move操作
private float mLastMotionX;
private float mLastMotionY;
public ScrollLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
}
public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
mScroller = new Scroller(context);
mCurScreen = mDefaultScreen;
//初始化一個最小滑動距離
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
/**
* 生成view
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
if (changed) {
int childLeft = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
final int childWidth = childView.getMeasuredWidth();
childView.layout(childLeft, 0, childLeft + childWidth,
childView.getMeasuredHeight());
childLeft += childWidth;
}
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.e(TAG, "onMeasure");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ScrollLayout only canmCurScreen run at EXACTLY mode!");
}
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ScrollLayout only can run at EXACTLY mode!");
}
// The children are given the same width and height as the scrollLayout
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
// Log.e(TAG, "moving to screen "+mCurScreen);
scrollTo(mCurScreen * width, 0);
}
/**
* According to the position of current layout scroll to the destination
* page.
*/
public void snapToDestination() {
// 判斷是否超過下一屏的中間位置,如果達到就抵達下一屏,否則保持在原屏幕
// 這樣的一個簡單公式意思是:假設當前滑屏偏移值即 scrollCurX 加上每個屏幕一半的寬度,除以每個屏幕的寬度就是
// 我們目標屏所在位置了。 假如每個屏幕寬度為320dip, 我們滑到了500dip處,很顯然我們應該到達第二屏,索引值為1
// 即(500 + 320/2)/320 = 1
final int screenWidth = getWidth();
final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
snapToScreen(destScreen);
}
public void snapToScreen(int whichScreen) {
// get the valid layout page
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
if (getScrollX() != (whichScreen * getWidth())) {
final int delta = whichScreen * getWidth() - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0,
Math.abs(delta) * 5);
mCurScreen = whichScreen;
onScreenChangeListener.onScreenChange(mCurScreen);
invalidate(); // Redraw the layout
}
}
public void setToScreen(int whichScreen) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
mCurScreen = whichScreen;
scrollTo(whichScreen * getWidth(), 0);
}
public int getCurScreen() {
return mCurScreen;
}
/**
* 控制view跟隨手指滑動 由父視圖調用用來請求子視圖根據偏移值 mScrollX,mScrollY重新繪制
*/
@Override
public void computeScroll() {
// 如果返回true,表示動畫還沒有結束
// 因為前面startScroll,所以只有在startScroll完成時 才會為false
if (mScroller.computeScrollOffset()) {
// 產生了動畫效果,根據當前值 每次滾動一點
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
/*
* 其中:onInterceptTouchEvent()主要功能是控制觸摸事件的分發,例如是子視圖的點擊事件還是滑動事件。
* 其他所有處理過程均在onTouchEvent()方法里實現了。 1、屏幕的滑動要根據手指的移動而移動 ----
* 主要實現在onTouchEvent()方法中
*
『貳』 Android中Scroller的使用及原理解析
1.1 構造Scroller
可以看到Scroller有2個構造器,其中第二個構造器需要傳入Interpolator,它是一個插值器,決定了view在滑動中的效果。第一種構造會默認使用ViscousFluidInterpolator這個插值器。
1.2 重寫View的computeScroll()
1.3 開始滑動
以上就是Scroller的簡單使用,需要注意的是Scroller移動的是View的內容而不是View本身,如一個LinearLayout里有2個Button,移動的是這2個Button而不是LinearLayout自己(原因在源碼解析里)。
另外Scroller還有個快速滑過的方法:
2.1 先看下startScroll():
可以看到startScroll只是記錄了傳入了參數,並沒有做什麼實際操作。
2.2 前面說過移動是通過scrollTo()完成的,那麼看看它的源碼
這里可以看到scrollTo更新了mScrollX,和mScrollY的值,最終會觸發重繪調用invalidate(),最終會調用到viewGroup的drawChild():
最終調用到了view的3個參數的draw(),再繼續看
在draw()中調用了computeScroll(),然後把最新的mScrollX,mScrollY賦值給sx,sy,最後完成移動。
那麼就是這樣一個流程:
startScroll()->invalidate()->draw()->computeScroll()-scrollTo()->invalidate()->draw()->computeScroll()-scrollTo()...
並一直循環,直到scroller.computeScrollOffset()返回false,滑動完成。
3.1 看一下OverScroller擴展的方法
『叄』 android scroller fling 滑多遠
Scroller主要用於平滑滾動,主要使用的滾動方法有:startScroll、fling。
startScroll(int startX, int startY, int dx, int dy, int ration):
指定起點(startX,startY),從起點平滑變化(dx,dy),耗時ration,通常用於:知道起點與需要改變的距離的平滑滾動等。
『肆』 如何優雅地在Android上實現iOS的圖片預覽
原文博客鏈接
用過 iOS 的都知道,擬物理的回彈效果在上面非常普遍,因為這是 iOS 系統支持的一套 UI 框架,但是 Android 就沒有了,就拿圖片查看器來講,iOS 的效果就是感覺一張圖片被綁定在了彈簧裝置上,滑動很自然,Android 沒有自帶的圖片查看器,需要自己實現
市面上主流的圖片查看器都沒有回彈的效果,一部分原因是沒有這個需求,還有一部分是實現麻煩,這里講述一個個人認為最好的方案
一個圖片查看器,要求可以滑動 Fling,觸碰到邊界的時候回彈,有越界孝喚回彈的效果,支持雙指縮放,雙擊縮放
咋一看需求,應該好寫,滾動的時候用 Scroller 來解巧態凱決,回彈效果直接用 ValueAnimator ,設置插值器為減速插值器來解決。看似簡單,但是因為是仿物理效果,中間牽扯到從滾動到回彈的時候( Scroller 動畫切換到 ValueAnimator 動畫)的閉老速度銜接問題,要看上去從滾動到開始回彈至結束沒有突兀,中間的特判邊界處理是很麻煩的,還要牽扯到縮放,所以不考慮這種方案
既然是要模擬現實中的物理效果,為何不在每一幀根據當前的狀態得到對用的加速度,然後去計算下一幀的狀態位置,這樣只要模擬現實中的物理加速度不就可以實現了嗎,那些邊界特判之類的就可以去見閻王了
方案確定完畢,接下來就是選定加速度的方程,要模擬彈簧的效果,拉力很簡單,用胡克定律嘛! F = k * dx ,摩擦力呢? Ff = μ*FN ? 這里推薦一個更加好的方案,借鑒自 Rebound 庫,這是 Facebook 的一個彈簧動畫庫,設定一個目的數值,它會根據當前的拉力,摩擦力,速度然後變化到目標值,加速度方程為
其中 tension 為彈性系數, friction 為摩擦力系數,為什麼讓摩擦力和速度成正比呢?如果摩擦力和速度成正比,那麼就不存在靜摩擦力,也就是不存在物體靜止情況下拉力小於摩擦力的情況(因為速度為0的時候,阻力為0,除非拉力為0),物體肯定會向目標地點靠近,遏制了物體摩擦力過大而無法達到目的地情況
為了方便接入各種 View ,設計一個 ZoomableGestureHelper 類
設計目的,我只需要知道視圖的大小邊界 (bounds) 和內部可滾動回彈的邊界 (innerBounds),就可以通過計算得到一個新的轉換矩陣
對於物理狀態,需要一個類 SpringPhysicsState 來做存儲,裡麵包含了速度、拉力系數、摩擦力系數,不保存位置,因為位置是通過 getBounds 動態計算得到的
速度分解成水平方向和垂直方向,因為處理方法一樣,下面只講述垂直方向的計算
狀態1 :其中一邊有越界
分析一下上圖中的位置,藍色部分為內部圖片,它被拖動越界了,此時的合力應該為 tension * dx - friction * v , v 為圖片在 y 軸方向上的速度,( dx 和 v 都是矢量,我暫且設置向右和向下為正),之後就直接調用 invalidate(); ,就可以播放動畫了。
狀態2:兩邊都沒越界
此時因為兩邊都沒有越界,所以應該不存在拉力,可以認為此時 dx 為0,摩擦力需要注意下,因為可以支持滑動( Fling ),所以此時的摩擦力要比之前越界回彈時候的摩擦力小,至於具體數值,文末會給出
狀態3:兩邊都超出
此時兩邊都超出邊界,藍色區域應該和紅色區域中心綁定,所以此時的 dx 為 dxBottom - dxTop (注意符號,因為 dx 為矢量,所以不能是 dxTop - dxBottom )
縮放的方法和移動一致,設定 tension 和 friction ,邊界設定為外面紅色的框框,藍色區域無法某一邊充滿紅色區域的時候,有拉力,否則沒拉力,摩擦力一直存在,至於雙擊放大和放小,只需要在雙擊的時候給縮放狀態設置一個初速度,然後 invalidate(); ,搞定!是不是很簡單啊
時間這一個參數在計算中是非常重要的,這關繫到當前微分狀態的數值變化,假如用歐拉方法模擬速度和位置的變化, x' = x + v * dt , v' = v + a * dt ,公式可以看出時間決定了動畫的快慢,為了接近現實物理時間,這里採用的時間單位為秒(計算機中常用的是毫秒)
確定了單位,還需要控制一下時間間隔的數值范圍,我們不能讓兩次 computeScroll 的時間間隔過於短或者過於長,這里採用的策略為固定每次計算時候的時間間隔,如果兩次 computeScroll 的時間間隔小於此時間間隔,那麼保存累計時間間隔,等待下一次 computeScroll ,直到大於等於固定的時間間隔,再用 while 循環一步一步的計算
結束判定是唯一的一個坑,因為計算機只是在 dt 時間內模擬速度和位移的變化,不是通過微積分計算的,存在誤差,比如歐拉方法 x' = x + v * dt 和 v' = v + a * dt 計算得到的 x' 和 v' 都是近似數值,把 dt 這段時間內的變化看成了勻變速運動
所以結束判定還需要設置一個閾值,當速度和偏移量小於此數值的時候,可以認定為達到了目的地
對於 ViewPager 的適配有些問題,如果在 Down 的時候 requestDisallow true 移動過程中到了左右邊界又 requestDisallow false ,此時 ViewPager 會有一個突變( 突變可恥但有用 ),而且多指頭的時候可能會崩潰,這是 ViewPager 的 Bug,具體細節請看源碼
『伍』 怎麼在手機桌面設置可以循環滾動的文字,可以自定義的
分析如下:
下載個LED顯示屏,手機應用中心搜索LED字母就會出現很多個三方軟體,然後你下載就可以使用了。
例如下載個文字鎖屏或者LED Scroller。
拓展資料
文字鎖屏,Android平台上最有趣的、最美的DIY鎖屏,DIY各種紋字鎖屏方式,自定義文字圖片,搭配編輯精心推薦壁紙,添加各類趣味插件(時間、文字、正計時倒計時等),最簡單的DIY方式打造最個性的手機鎖屏精靈,真正做到實惠到家的鎖屏軟體。
(資料來源:網路:文字鎖屏)