Android 多屏滑動
最近寫應用,多次用到裡多屏滑動,參考了下一些資料,寫裡一個類,可以通用。目前用起來沒什麼問題,先存下來。
package com.android.systemui.recent.gk;
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.view.animation.Interpolator;
import android.widget.Scroller;
import android.widget.TextView;
public class ScreenView extends ViewGroup {
protected int mVisibleRange = 1;
private static final int MAX_VISIBLE_RANGE = 4;
private static final int TOUCH_STATE_RESET = 0;
private static final int TOUCH_STATE_SCROLLING = 1;
private static final int INVALIDATE_SCREEN = -1;
private int mTouchState = TOUCH_STATE_RESET;
private float mTouchDownX;
private float mTouchDownY;
private float mLastMotionX;
private float mTouchSlop;
private int mCurrentScreen;
private int mNextScreen;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private static final int OVER_SCROLL_BOUNDS = 0;
private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.3f;
private static final float RETURN_TO_ORIGINAL_SCREEN_THRESHOLD = 0.33f;
private static final int MIN_LENGTH_FOR_FLING = 25;
private static final int FLING_THRESHOLD_VELOCITY = 200;
private static final int MIN_SNAP_VELOCITY_BASE = 200;
private static final int SCREEN_SNAP_DURATION = 200;
private OvershootInterpolator mScrollInterpolator;
private int mFlingThresholdVelocity;
private int mMinSnapVelocity;
private int mMaximumVelocity;
private final float mBaseLineFlingVelocity = 2500.0f;
private final float mFlingVelocityInfluence = 0.4f;
private boolean mTouchMoveHorizion = false;
private double mTouchMoveOrientationSlop;
private int mScreenCount = 0;
private int mScreenWidth;
private int mMaxChildWidth;
private static final float mOverScrollRatio = 0.3f;
private static final String TAG = "ScreenView jalen";
private int mOverScrollBounds = 0;
public ScreenView(Context context) {
super(context);
init(context);
}
public ScreenView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ScreenView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
@Override
protected void onFinishInflate() {
// TODO Auto-generated method stub
super.onFinishInflate();
}
private void init(Context context){
ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
mMaximumVelocity = config.getScaledMaximumFlingVelocity();
mMinSnapVelocity = MIN_SNAP_VELOCITY_BASE * 2;
mFlingThresholdVelocity = FLING_THRESHOLD_VELOCITY * 2;
mScrollInterpolator = new OvershootInterpolator();
mScroller = new Scroller(context, mScrollInterpolator);
mTouchMoveOrientationSlop = getTouchMoveOrientationSlop();
}
private double getTouchMoveOrientationSlop(){
return Math.tan(45 * Math.PI / 180);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
int childCount = getChildCount();
int childGap;
if(childCount < mVisibleRange){
childGap = (mScreenWidth - childCount * mMaxChildWidth) / (childCount + 1);
}else{
childGap = (mScreenWidth - mVisibleRange * mMaxChildWidth) / (mVisibleRange + 1);
}
int left = getPaddingLeft() + childGap;
int top = getPaddingTop();
boolean isNewScreen;
for(int i=0; i<childCount; i++){
View v = getChildAt(i);
if(v.getVisibility() == GONE) continue;
v.layout(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
isNewScreen = ((i + 1) % mVisibleRange == 0);
if(isNewScreen){
int screenIndex = (i + mVisibleRange - 1) / mVisibleRange;
left = screenIndex * getWidth() + getPaddingLeft() + childGap;
}else{
left += v.getMeasuredWidth() + childGap;
}
}
Log.i(TAG,"onLayout childCount="+childCount + ", childGap="+childGap);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
int childCount = getChildCount();
for(int i=0; i<childCount; i++){
View v = getChildAt(i);
measureChild(v, widthMeasureSpec, heightMeasureSpec);
mMaxChildWidth = Math.max(mMaxChildWidth, v.getMeasuredWidth());
mScreenWidth = View.MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
mVisibleRange = Math.min(MAX_VISIBLE_RANGE, Math.max(1, mScreenWidth / mMaxChildWidth));
}
Log.i(TAG,"onMeasure childCount="+childCount + ", mMaxChildWidth="+mMaxChildWidth
+", mScreenWidth="+mScreenWidth
+", mVisibleRange="+mVisibleRange);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void addView(View child, int index, LayoutParams params) {
// TODO Auto-generated method stub
super.addView(child, index, params);
mScreenCount += 1;
}
@Override
public void removeAllViews() {
// TODO Auto-generated method stub
super.removeAllViews();
mScreenCount = 0;
}
@Override
public void removeViewsInLayout(int start, int count) {
// TODO Auto-generated method stub
super.removeViewsInLayout(start, count);
mScreenCount -= 1;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
aquireVelocityTrackerAndAddMotion(ev);
switch(ev.getActionMasked()){
case MotionEvent.ACTION_DOWN:
mLastMotionX = mTouchDownX = ev.getX();
mTouchDownY = ev.getY();
mTouchState = mScroller.isFinished() ? TOUCH_STATE_RESET : TOUCH_STATE_SCROLLING;
mTouchMoveHorizion = (mTouchState == TOUCH_STATE_SCROLLING);
break;
case MotionEvent.ACTION_MOVE:
if(mTouchState != TOUCH_STATE_SCROLLING){
float xDiff = Math.abs(ev.getX() - mTouchDownX);
float yDiff = Math.abs(ev.getY() - mTouchDownY);
if(xDiff > mTouchSlop){
if(yDiff == 0 || (xDiff / yDiff > mTouchMoveOrientationSlop)){
mTouchMoveHorizion = true;
}
if(mTouchMoveHorizion){
mTouchState = TOUCH_STATE_SCROLLING;
}
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
releaseVelocityTracker();
mTouchState = TOUCH_STATE_RESET;
break;
}
Log.i(TAG, " onInterceptTouchEvent "+ev.getActionMasked()+" return="+(mTouchState == TOUCH_STATE_SCROLLING));
return mTouchState == TOUCH_STATE_SCROLLING;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
aquireVelocityTrackerAndAddMotion(event);
switch(event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
mLastMotionX = mTouchDownX = event.getX();
mTouchDownY = event.getY();
mTouchState = mScroller.isFinished() ? TOUCH_STATE_RESET : TOUCH_STATE_SCROLLING;
mTouchMoveHorizion = (mTouchState == TOUCH_STATE_SCROLLING);
break;
case MotionEvent.ACTION_MOVE:
if(mTouchState == TOUCH_STATE_SCROLLING){
float deltaX = mLastMotionX - event.getX();
mLastMotionX = event.getX();
float nextScrollX = getScrollX() + deltaX;
if(nextScrollX < (getScreenCount() - 1) * getWidth() + OVER_SCROLL_BOUNDS
&& nextScrollX > -OVER_SCROLL_BOUNDS){
scrollBy((int)deltaX, 0);
}
}else{
float xDiff = Math.abs(event.getX() - mTouchDownX);
float yDiff = Math.abs(event.getY() - mTouchDownY);
if(xDiff > mTouchSlop){
if(yDiff == 0 || (xDiff / yDiff > mTouchMoveOrientationSlop)){
mTouchMoveHorizion = true;
}
if(mTouchMoveHorizion){
mTouchState = TOUCH_STATE_SCROLLING;
}
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if(mTouchState == TOUCH_STATE_SCROLLING){
final int screenWidth = getWidth();
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
final int velocityX = (int) mVelocityTracker.getXVelocity();
int delataX = (int)(event.getX() - mTouchDownX);
boolean isSignificationMove = Math.abs(delataX) > screenWidth * SIGNIFICANT_MOVE_THRESHOLD;
boolean isFling = (Math.abs(velocityX) > mFlingThresholdVelocity)
&& (Math.abs(delataX) > MIN_LENGTH_FOR_FLING);
boolean returnToOriginalScreen = (Math.signum(velocityX) != Math.signum(delataX))
&& (Math.abs(delataX) > screenWidth * RETURN_TO_ORIGINAL_SCREEN_THRESHOLD)
&& isFling;
int finalScreen;
if(((isSignificationMove && delataX > 0 && !isFling) || (isFling && velocityX > 0))
&& mCurrentScreen > 0){
finalScreen = returnToOriginalScreen ? mCurrentScreen : mCurrentScreen - 1;
snapToScreen(finalScreen, velocityX);
}else if(((isSignificationMove && delataX < 0 && ! isFling) || (isFling && velocityX < 0))
&& mCurrentScreen < getScreenCount() - 1){
finalScreen = returnToOriginalScreen ? mCurrentScreen : mCurrentScreen + 1;
snapToScreen(finalScreen, velocityX);
}else{
finalScreen = (getScrollX() + screenWidth / 2) / screenWidth;
snapToScreen(finalScreen, velocityX);
}
mTouchState = TOUCH_STATE_RESET;
}
releaseVelocityTracker();
break;
}
return true;
}
@Override
public void computeScroll() {
// TODO Auto-generated method stub
//super.computeScroll();
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}else if(mNextScreen != INVALIDATE_SCREEN){
mCurrentScreen = Math.max(0, Math.min(mNextScreen, getScreenCount() - 1));
scrollTo(mCurrentScreen * getWidth(), 0);
mNextScreen = INVALIDATE_SCREEN;
}
}
private void snapToScreen(int whichScreen, int velocity){
whichScreen = Math.max(0, Math.min(whichScreen, getScreenCount() - 1));
int screenWidth = getWidth();
final int screenDelta = Math.abs(mCurrentScreen - whichScreen);
final int newX = whichScreen * screenWidth;
final int deltaX = newX - getScrollX();
int duration = 0;
if(!mScroller.isFinished()){
mScroller.abortAnimation();
}
velocity = Math.max(mMinSnapVelocity, Math.abs(velocity));
int tmpDuration = Math.abs(deltaX) * SCREEN_SNAP_DURATION / screenWidth;
if(velocity > 0){
tmpDuration += tmpDuration / (velocity / mBaseLineFlingVelocity) * mFlingVelocityInfluence;
}
duration = Math.max(SCREEN_SNAP_DURATION, tmpDuration);
if(screenDelta <= 1) duration = Math.min(duration, SCREEN_SNAP_DURATION * 2);
boolean changeScreen = whichScreen != mCurrentScreen;
View focusedView = getFocusedChild();
if(changeScreen && focusedView != null && focusedView == getChildAt(mCurrentScreen)){
clearChildFocus(focusedView);
}
Log.i(TAG,"snapToScreen "
+", screenDelta="+screenDelta
+", newX="+newX
+", deltaX="+deltaX
+", whichScreen="+whichScreen
+", velocity="+velocity
+", duration="+duration);
mNextScreen = whichScreen;
mScroller.startScroll(getScrollX(), 0, deltaX, 0, duration);
invalidate();
}
public void snapToScreen(int screenId){
if(screenId < 0 || screenId > getScreenCount() - 1){
return;
}
snapToScreen(screenId, 0);
}
private void aquireVelocityTrackerAndAddMotion(MotionEvent ev){
if(mVelocityTracker == null){
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
}
private void releaseVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
public int getScreenCount(){
//return mScreenCount;
return (int)((getChildCount() + (mVisibleRange - 1)) / mVisibleRange);
}
public static class OvershootInterpolator implements Interpolator {
private static final float DEFAULT_TENSION = 1.3f;
private float mTension;
public OvershootInterpolator() {
mTension = 0.0f;//DEFAULT_TENSION;
}
public void setDistance(int distance) {
mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
}
public void setTension(float tension){
mTension = tension;
}
public void disableSettle() {
mTension = 0.f;
}
public float getInterpolation(float t) {
// _o(t) = t * t * ((tension + 1) * t + tension)
// o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * ((mTension + 1) * t + mTension) + 1.0f;
}
}
}
相關文章
- Android 設定TextView滑動滾動條和滑動效果AndroidTextView
- Android 禁止ViewPager左右滑動AndroidViewpager
- Android滑動返回-swipebacklayout解析Android
- 互動滑軌屏的特點及功能特性
- 互動滑軌屏的幾種實現形式
- 說說android端實現Airplay多屏互動(二)AndroidAI
- Android:巢狀滑動總結Android巢狀
- android: slide 滑動動畫效果AndroidIDE動畫
- 多屏互動和投屏該如何區別
- Android事件傳遞、多點觸控及滑動衝突的處理Android事件
- 互動滑軌屏在多媒體數字展廳應用中所帶來的影響
- Android自定義滑動刻度尺Android
- Android 自定義View 滑動解鎖AndroidView
- 互動滑軌屏在展廳應用的優勢
- Appium常用操作之「微信滑屏、觸屏操作」APP
- web 多屏互動顯示方案Web
- Android巢狀滑動邏輯淺析Android巢狀
- Android實現Activity的滑動返回效果Android
- Android 進出activity的滑動動畫效果Android動畫
- web前端仿手機左右滑動(手滑+自動滑動)Web前端
- Android 的滑動分析以及各種實現Android
- android可以無限迴圈滑動的ViewPagerAndroidViewpager
- Android 具有左右滑動功能的時間軸Android
- Android左右滑動效果的程式碼實現Android
- android 滑動刪除的listview(自定義view)AndroidView
- 【朝花夕拾】Android自定義View篇之(十一)View的滑動,彈性滑動與自定義PagerViewAndroidView
- android 自定義ImageView實現圖片手勢滑動,多點觸控放大縮小效果AndroidView
- 展廳設計中互動滑軌屏常見的應用形式
- [MAUI]模仿iOS多工切換卡片滑動的互動實現UIiOS
- Android View 滑動衝突解決方式以及原理AndroidView
- android處理單擊雙擊和滑動事件Android事件
- Android可滑動Tab的3種實現方法Android
- android 全域性頁面滑動返回聯動效果的實現Android
- 移動端左滑右滑元件元件
- uniapp---app滑動翻頁(上滑、下滑、左滑、右滑)APP
- WebSocket實現多屏互動的分析及方案Web
- Android去掉SrollView、GrdiView、RecycleView、ViewPager等可滑動控制元件滑動到邊緣的光暈效果AndroidViewpager控制元件
- Android ViewPager Fragments滑動只重新整理當前頁AndroidViewpagerFragment