Android UI效果实现——Activity滑动退出效果

更新说明:

1、在QQ网友北京-旭的提醒下,在SlideFrame的initilize方法中添加了focusable、focusableInTouch、clickable的状态设置,否则会导致部分情况下无法滑动,感谢!

 

一、使用说明

使用方法很简单,只有一个类HorizontalActivity,继承自FragmentActivity类,实现了contentView的滑动事件触发和动画效果,要在自己的代码里实现,方法两种:

1、如果对Activity没特殊要求,直接继承HorizontalActivity即可

2、如果Activity的父类必须是某一特定类型的Activity子类,则可以仿照我的写法对该类进行继承

 

二、HorizontalActivity类的代码

  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 }

 

三、使用详细步骤

1、建立一个Android工程,项目最小api level必须在api level 11及以上

2、把本类考入任意代码包下

3、项目中想要实现Activity滑动退出效果的Activity继承本类

4、如果要在滑动过程中显示上一个Activity的话,将Activity的背景设置为透明,方法建议通过设置Activity的Style或者说theme来实现

5、如果滑动过程中需要加入一些特殊效果,可以复写slideTo(int)方法,记得别把super.slideTo(int)给漏了,否则滑动失效

6、如果滑动结束后不希望立即返回上一页,可以复写onSlideFinish()方法

 

四、某项目中使用的效果展示

Android UI效果实现——Activity滑动退出效果

 

PS、

很简单的实现方法,但离我觉得完美还太远,所以如果有更好的实现方法希望可以一起交流下:

联系方式QQ:811868948,备注加上Android交流,有好的想法一定要联系我,谢谢!

联系方式E-Mail:[email protected]

你可能感兴趣的:(android ui)