更新說明:android
一、在QQ網友北京-旭的提醒下,在SlideFrame的initilize方法中添加了focusable、focusableInTouch、clickable的狀態設置,不然會致使部分狀況下沒法滑動,感謝!api
1、效果動圖app
2、使用說明框架
使用方法很簡單,只有一個類HorizontalActivity,繼承自FragmentActivity類,實現了contentView的滑動事件觸發和動畫效果,要在本身的代碼裏實現,方法兩種:ide
一、若是對Activity沒特殊要求,直接繼承HorizontalActivity便可動畫
二、若是Activity的父類必須是某一特定類型的Activity子類,則能夠仿照個人寫法對該類進行繼承ui
3、HorizontalActivity類的代碼this
1 package com.beifeng.widget; 2 3 import android.content.Context; 4 import android.support.v4.app.FragmentActivity; 5 import android.util.AttributeSet; 6 import android.view.LayoutInflater; 7 import android.view.MotionEvent; 8 import android.view.View; 9 import android.view.ViewGroup.LayoutParams; 10 import android.view.animation.Animation; 11 import android.view.animation.Animation.AnimationListener; 12 import android.view.animation.DecelerateInterpolator; 13 import android.view.animation.Transformation; 14 import android.widget.FrameLayout; 15 16 /** 17 * HorizontalActivity:可滑動Activity 18 * 19 * 注意事項: 本Activity中與滑動方向相同的滑動操做會被攔截 20 * 21 * @author HalfmanG2 22 * @version 1.0.0 23 * @since JDK7 SDK19 24 */ 25 public class HorizontalActivity extends FragmentActivity { 26 27 /** 框架視圖 */ 28 protected SlideFrame frameView; 29 /** 內容視圖 */ 30 protected View contentView; 31 32 @Override 33 public void setContentView(int layoutResID) { 34 // 初始化frame 35 if (frameView == null) { 36 // 未初始化則初始化 37 frameView = new SlideFrame(this); 38 } else { 39 // 已經初始化則清空 40 frameView.removeAllViews(); 41 } 42 // 創造framelayout的填充參數 43 FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(-1, -1); 44 // 獲取layoutResId對應的contentView視圖並插入frameView 45 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 46 contentView = inflater.inflate(layoutResID, null); 47 frameView.addView(contentView, params); 48 // 設置frameview爲根視圖 49 super.setContentView(frameView); 50 } 51 52 @Override 53 public void setContentView(View view) { 54 // 初始化frame 55 if (frameView == null) { 56 // 未初始化則初始化 57 frameView = new SlideFrame(this); 58 } else { 59 // 已經初始化則清空 60 frameView.removeAllViews(); 61 } 62 // 創造framelayout的填充參數 63 FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(-1, -1); 64 // 獲取view爲contentView視圖並插入frameView 65 contentView = view; 66 frameView.addView(contentView, params); 67 // 設置frameview爲根視圖 68 super.setContentView(frameView); 69 } 70 71 @Override 72 public void setContentView(View view, LayoutParams params) { 73 // 初始化frame 74 if (frameView == null) { 75 // 未初始化則初始化 76 frameView = new SlideFrame(this); 77 } else { 78 // 已經初始化則清空 79 frameView.removeAllViews(); 80 } 81 // 創造framelayout的填充參數 82 FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams(-1, -1); 83 // 獲取view爲contentView視圖並插入frameView 84 contentView = view; 85 frameView.addView(contentView, fp); 86 // 設置frameview爲根視圖 87 super.setContentView(frameView, params); 88 } 89 90 /** 91 * 推出頁面 92 */ 93 protected void onSlideFinish() { 94 finish(); 95 } 96 97 /** 98 * 位移內容視圖到 99 * 100 * @param position 101 * 目標位置 102 */ 103 public void slideTo(int position) { 104 if (android.os.Build.VERSION.SDK_INT > 10) { 105 contentView.setX(position); 106 } else { 107 android.widget.FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) contentView 108 .getLayoutParams(); 109 params.setMargins(position, 0, -position, 0); 110 contentView.setLayoutParams(params); 111 } 112 } 113 114 /** 115 * 得到當前容器位移 116 * 117 * @return 當前容器位移 118 */ 119 public int getSlide() { 120 if (android.os.Build.VERSION.SDK_INT > 10) { 121 return (int) contentView.getX(); 122 } else { 123 return ((android.widget.FrameLayout.LayoutParams) contentView 124 .getLayoutParams()).leftMargin; 125 } 126 } 127 128 /** 129 * 滑動框架 130 * 131 * @author HalfmanG2 132 * @version 1.0.0 133 * @since JDK7 SDK19 134 */ 135 public class SlideFrame extends FrameLayout { 136 /** 默認滑動閥值 */ 137 private final static int DEFAULT_SLIDE_DUMPING = 8; 138 /** 默認狀態改變閥值 */ 139 private final static int DEFAULT_DO_DUMPING = 100; 140 /** 滑動起始位置與當前位置 */ 141 private int startX, currentX, startY, currentY; 142 /** 是否攔截事件,是否已經完成滑動檢查 */ 143 private boolean doNotIntercept, hasChecked; 144 /** 滑動閥值 */ 145 private int slideDumping; 146 /** 操做閥值 */ 147 private int doDumping; 148 /** 滑屏動畫 */ 149 protected SlideAnimation slideAnimation; 150 151 @Override 152 public boolean onInterceptTouchEvent(MotionEvent ev) { 153 super.onInterceptTouchEvent(ev); 154 // 若當前處在側滑狀態中,則攔截信號 155 if ((!doNotIntercept) && hasChecked) { 156 return true; 157 } 158 // 不然使用默認 159 return false; 160 } 161 162 @Override 163 public boolean dispatchTouchEvent(MotionEvent ev) { 164 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 165 // 得到起始滑動座標 166 startX = (int) ev.getX(); 167 startY = (int) ev.getY(); 168 // 初始化狀態 169 doNotIntercept = false; 170 hasChecked = false; 171 } else if (!doNotIntercept) { 172 // 得到當前滑動座標 173 currentX = (int) ev.getX(); 174 currentY = (int) ev.getY(); 175 // 根據滑動類型區分 176 switch (ev.getAction()) { 177 case MotionEvent.ACTION_MOVE: // 移動狀態 178 if (hasChecked) { 179 doSlide(); 180 } else { 181 doCheck(); 182 } 183 break; 184 case MotionEvent.ACTION_CANCEL: // 取消狀態 185 case MotionEvent.ACTION_UP: // 擡起狀態 186 // 初始化狀態 187 doNotIntercept = false; 188 hasChecked = false; 189 if (Math.abs(currentX - startX) > doDumping) { 190 if (currentX > startX) { 191 // 右滑 192 slideAnimation = new SlideAnimation(getSlide(), 193 contentView.getWidth(), 0); 194 slideAnimation 195 .setAnimationListener(new AnimationListener() { 196 @Override 197 public void onAnimationStart( 198 Animation animation) { 199 } 200 201 @Override 202 public void onAnimationRepeat( 203 Animation animation) { 204 } 205 206 @Override 207 public void onAnimationEnd( 208 Animation animation) { 209 onSlideFinish(); 210 } 211 }); 212 startAnimation(slideAnimation); 213 } 214 } else { 215 // 返回0位置 216 slideAnimation = new SlideAnimation(getSlide(), 0, 0); 217 startAnimation(slideAnimation); 218 } 219 break; 220 default: 221 break; 222 } 223 } 224 return super.dispatchTouchEvent(ev); 225 } 226 227 /** 228 * 檢查是否超過滑動閥值開啓滑動狀態 229 */ 230 private void doCheck() { 231 if (Math.abs(startY - currentY) > slideDumping) { 232 hasChecked = true; 233 doNotIntercept = true; 234 slideTo(0); 235 } else if (currentX - startX > slideDumping) { 236 hasChecked = true; 237 doNotIntercept = false; 238 } 239 } 240 241 /** 242 * 進行滑動 243 */ 244 private void doSlide() { 245 if (currentX > startX) { 246 slideTo(currentX - startX); 247 } else { 248 slideTo(0); 249 } 250 } 251 252 /** 253 * 設置滑動閥值 254 * 255 * @param dpValue 256 */ 257 public void setSlideDumping(int dpValue) { 258 slideDumping = dip2px(dpValue); 259 } 260 261 /** 262 * 設置狀態改變閥值 263 * 264 * @param dpValue 265 */ 266 public void setDoDumping(int dpValue) { 267 doDumping = dip2px(dpValue); 268 } 269 270 /** 271 * 二級構造方法 272 */ 273 private void initilize() { 274 setSlideDumping(DEFAULT_SLIDE_DUMPING); 275 setDoDumping(DEFAULT_DO_DUMPING); 276 doNotIntercept = false; 277 hasChecked = false; 278 setClickable(true); 279 setFocusable(true); 280 setFocusableInTouchMode(true); 281 } 282 283 /** 284 * 構造方法 285 * 286 * @param context 287 * @param attrs 288 * @param defStyle 289 */ 290 public SlideFrame(Context context, AttributeSet attrs, int defStyle) { 291 super(context, attrs, defStyle); 292 initilize(); 293 } 294 295 /** 296 * 構造方法 297 * 298 * @param context 299 * @param attrs 300 */ 301 public SlideFrame(Context context, AttributeSet attrs) { 302 super(context, attrs); 303 initilize(); 304 } 305 306 /** 307 * 構造方法 308 * 309 * @param context 310 */ 311 public SlideFrame(Context context) { 312 super(context); 313 initilize(); 314 } 315 316 /** 317 * 講dip值轉換爲px值,像素密度距離轉像素距離 318 * 319 * @param dipValue dp值 320 * @return px值 321 */ 322 private int dip2px(float dipValue) { 323 // 得到像素密度 324 final float scale = getContext().getResources().getDisplayMetrics().density; 325 // 四捨五入dp值乘像素密度 326 return (int) (dipValue * scale + 0.5f); 327 } 328 } 329 330 /** 331 * 滑動動畫類 332 * 333 * @author HalfmanG2 334 */ 335 public class SlideAnimation extends Animation { 336 /** 起始位置,目標位置 */ 337 private float from, to; 338 /** 339 * 構造方法 340 * @param from 起始位置 341 * @param to 目標位置 342 * @param startOffset 起始延遲 343 */ 344 public SlideAnimation(int from, int to, int startOffset) { 345 this.from = from; 346 this.to = to; 347 setFillEnabled(false); 348 setDuration(200); 349 setRepeatCount(0); 350 setStartOffset(startOffset); 351 setInterpolator(new DecelerateInterpolator()); 352 } 353 @Override 354 protected void applyTransformation(float interpolatedTime, 355 Transformation t) { 356 float current = from + (to - from) * interpolatedTime; 357 slideTo((int) current); 358 super.applyTransformation(interpolatedTime, t); 359 } 360 } 361 }
4、使用詳細步驟spa
一、創建一個Android工程,項目最小api level必須在api level 11及以上code
二、把本類考入任意代碼包下
三、項目中想要實現Activity滑動退出效果的Activity繼承本類
四、若是要在滑動過程當中顯示上一個Activity的話,將Activity的背景設置爲透明,方法建議經過設置Activity的Style或者說theme來實現
五、若是滑動過程當中須要加入一些特殊效果,能夠複寫slideTo(int)方法,記得別把super.slideTo(int)給漏了,不然滑動失效
六、若是滑動結束後不但願當即返回上一頁,能夠複寫onSlideFinish()方法
PS、
很簡單的實現方法,但離我以爲完美還太遠,因此若是有更好的實現方法但願能夠一塊兒交流下:
聯繫方式QQ:811868948,備註加上Android交流,有好的想法必定要聯繫我,謝謝!
聯繫方式E-Mail:halfmanhuang@gmail.com