編輯:關於android開發
如下圖是側滑的效果圖

實現的功能主要是用ViewDragHelper,用ViewDragHelper來自定義一個側滑面板來實現側滑
如下是自定義的側滑面板
1 package com.demo.sb.widget;
2
3 import com.nineoldandroids.view.ViewHelper;
4
5 import android.content.Context;
6 import android.graphics.Color;
7 import android.graphics.PorterDuff.Mode;
8 import android.support.v4.view.ViewCompat;
9 import android.support.v4.widget.ViewDragHelper;
10 import android.util.AttributeSet;
11 import android.util.Log;
12 import android.view.MotionEvent;
13 import android.view.View;
14 import android.view.ViewGroup;
15 import android.widget.FrameLayout;
16
17 /**
18 * 側滑面板
19 *
20 * @author Administrator
21 *
22 */
23 public class DragLayout extends FrameLayout {
24
25 private ViewDragHelper mDragHelper;
26 private ViewGroup mLeftContent;
27 private ViewGroup mMainContent;
28
29 private Status mStatus = Status.Close;
30 private OnDragStatusChangeListener mListener;
31
32 /**
33 * 狀態枚舉
34 */
35 public static enum Status {
36 Close, Open, Draging;
37 }
38
39 public interface OnDragStatusChangeListener {
40 void onClose();
41
42 void onOpen();
43
44 void onDraging(float percent);
45 }
46
47 public Status getStatus() {
48 return mStatus;
49 }
50
51 public void setStatus(Status mStatus) {
52 this.mStatus = mStatus;
53 }
54
55 public void setDragStatusListener(OnDragStatusChangeListener mListener) {
56 this.mListener = mListener;
57 }
58
59 public DragLayout(Context context) {
60 this(context, null);
61 // TODO Auto-generated constructor stub
62 }
63
64 public DragLayout(Context context, AttributeSet attrs) {
65 this(context, attrs, 0);
66 // TODO Auto-generated constructor stub
67 }
68
69 public DragLayout(Context context, AttributeSet attrs, int defStyle) {
70 super(context, attrs, defStyle);
71 // TODO Auto-generated constructor stub
72 /**
73 * a . 初始化(通過靜態的方法)
74 */
75 mDragHelper = ViewDragHelper.create(this, mCallback);
76 }
77
78 /**
79 * c. 重寫Callback的事件
80 */
81 ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
82 /**
83 * 1. 根據返回結果決定當前的child是否可以拖拽 child 當前被拖拽的View pointerId 區分多點觸摸的id
84 */
85 @Override
86 public boolean tryCaptureView(View child, int pointerId) {
87 Log.d("jiejie", "tryCaptureView: " + child);
88 return true;// 都可以嘗試被拖拽
89 // return child == mMainContent;//只有主程序的View可以被拖拽
90 }
91
92 public void onViewCaptured(View capturedChild, int activePointerId) {
93 // 當capturedChild被捕獲時,調用
94 Log.d("jiejie", "onViewCaptured: " + capturedChild);
95 }
96
97 public int getViewHorizontalDragRange(View child) {
98 // 返回拖拽的范圍,不對拖拽進行真正的限制。僅僅決定了動畫的執行速度
99 return mRange;
100 }
101
102 /**
103 * 2. 根據建議值 修正將要移動到的(橫向)位置(重要) 此時還沒有真正移動
104 */
105 public int clampViewPositionHorizontal(View child, int left, int dx) {
106 Log.d("jiejie", "clampViewPositionHorizontal: " + left);
107 if (child == mMainContent) {
108 left = fixLeft(left);
109 }
110 return left;
111 }
112
113 /**
114 * 3. 當View位置改變的時候,處理要做的事情(更新狀態, 伴隨動畫, 重繪界面) 此時,View已經發生了位置的改變
115 * changedView 改變位置的View left 新的左邊值 dx 水平方向變化量
116 */
117 public void onViewPositionChanged(View changedView, int left, int top,
118 int dx, int dy) {
119 super.onViewPositionChanged(changedView, left, top, dx, dy);
120 Log.d("jiejie", "onViewPositionChanged: " + "left:" + left
121 + " dx: " + dx);
122 int newLeft = left;
123 if (changedView == mLeftContent) {
124 // 把當前變化量專遞給mMainContent
125 newLeft = mMainContent.getLeft() + dx;
126 }
127 // 進行修正
128 newLeft = fixLeft(newLeft);
129
130 if (changedView == mLeftContent) {
131 // 當左面板移動之後,再強制放回去
132 mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);
133 mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight);
134 }
135
136 /**
137 * 更新狀態時設置動畫(也可以不設置,不設置的話則默認平滑的狀態,也可以省很多代碼) 設置左面板和主面板,背景的動畫
138 */
139 dispatchDragEvent(newLeft);
140
141 // 為了兼容低版本,每次修改值之後,進行重繪
142 invalidate();
143 }
144
145 /**
146 * 4. 當View被釋放的時候,處理的事情(執行動畫) View releasedChild 被釋放的子View float xvel
147 * 水平方向的速度,向右為正 float yvel 豎直方向的速度, 向下為正
148 */
149 public void onViewReleased(View releasedChild, float xvel, float yvel) {
150 Log.d("jiejie", "onViewReleased: " + "xvel: " + xvel + "yvel: "
151 + yvel);
152 super.onViewReleased(releasedChild, xvel, yvel);
153 // 判斷執行 關閉/開啟
154 // 先考慮所有開啟的情況,剩下的就是都是關閉的情況
155 if (xvel == 0 && mMainContent.getLeft() > (mRange / 2.0f)) {
156 open();
157 } else if (xvel > 0) {
158 open();
159 } else {
160 chose();
161 }
162 }
163
164 public void onViewDragStateChanged(int state) {
165 super.onViewDragStateChanged(state);
166 }
167 };
168
169 /**
170 * 根據范圍修正左邊值
171 *
172 * @param left
173 * @return
174 */
175 private int fixLeft(int left) {
176 if (left < 0) {
177 return 0;
178 } else if (left > mRange) {
179 return mRange;
180 }
181 return left;
182 }
183
184 /**
185 * 執行動畫
186 *
187 * @param newLeft
188 */
189 protected void dispatchDragEvent(int newLeft) {
190 // TODO Auto-generated method stub
191 float percent = newLeft * 1.0f / mRange;
192 Log.d("jiejie", "percent: " + percent);
193 if (mListener != null) {
194 mListener.onDraging(percent);
195 }
196
197 // 更新狀態,執行回調
198 Status preStatus = mStatus;
199 mStatus = updateStatus(percent);
200
201 if (mStatus != preStatus) {
202 // 狀態發生變化
203 if (mStatus == Status.Close) {
204 // 當前變為關閉狀態
205 if (mListener != null) {
206 mListener.onClose();
207 }
208 } else if (mStatus == Status.Open) {
209 if (mListener != null) {
210 mListener.onOpen();
211 }
212 }
213 }
214
215 // 伴隨動畫
216 animViews(percent);
217 }
218
219 private Status updateStatus(float percent) {
220 if (percent == 0f) {
221 return Status.Close;
222 } else if (percent == 1.0f) {
223 return Status.Open;
224 }
225 return Status.Draging;
226 }
227
228 private void animViews(float percent) {
229 // TODO Auto-generated method stub
230 /**
231 * >1.左面板:縮放動畫,平移動畫,透明度動畫 縮放動畫0.0 - > 1.0 >> 0.5f -> 1.0f >>>
232 * 0.5f*percent+0.5f mLeftContent.setScaleX(0.5f + 0.5f * percent);
233 * mLeftContent.setScaleY(0.5f + 0.5f * percent);
234 */
235 // mLeftContent.setScaleY(0.5f + 0.5f * percent);
236 // mLeftContent.setScaleX(0.5f + 0.5f * percent);
237 ViewHelper.setScaleX(mLeftContent, evaluate(percent, 0.5f, 1.0f));
238 ViewHelper.setScaleY(mLeftContent, 0.5f + 0.5f * percent);
239 // 平移動畫:-mWidth / 2.0 f - > 0.0f
240 ViewHelper.setTranslationX(mLeftContent,
241 evaluate(percent, -mWidth / 2.0f, 0));
242 // 透明度: 0.5 -> 1.0f
243 ViewHelper.setAlpha(mLeftContent, evaluate(percent, 0.5f, 1.0f));
244
245 /**
246 * >2. 主面板:縮放動畫
247 */
248 // 1.0f -> 0.8f
249 ViewHelper.setScaleX(mMainContent, evaluate(percent, 1.0f, 0.8f));
250 ViewHelper.setScaleY(mMainContent, evaluate(percent, 1.0f, 0.8f));
251
252 /**
253 * >3. 背景動畫: 亮度變化(顏色變化)
254 */
255 getBackground()
256 .setColorFilter(
257 (Integer) evaluateColor(percent, Color.BLACK,
258 Color.TRANSPARENT), Mode.SRC_OVER);
259 }
260
261 /**
262 * 估值器
263 *
264 * @param fraction
265 * @param startValue
266 * @param endValue
267 * @return
268 */
269 public Float evaluate(float fraction, Number startValue, Number endValue) {
270 float startFloat = startValue.floatValue();
271 return startFloat + fraction * (endValue.floatValue() - startFloat);
272 }
273
274 /**
275 * 顏色變化過度
276 *
277 * @param fraction
278 * @param startValue
279 * @param endValue
280 * @return
281 */
282 public Object evaluateColor(float fraction, Object startValue,
283 Object endValue) {
284 int startInt = (Integer) startValue;
285 int startA = (startInt >> 24) & 0xff;
286 int startR = (startInt >> 16) & 0xff;
287 int startG = (startInt >> 8) & 0xff;
288 int startB = startInt & 0xff;
289
290 int endInt = (Integer) endValue;
291 int endA = (endInt >> 24) & 0xff;
292 int endR = (endInt >> 16) & 0xff;
293 int endG = (endInt >> 8) & 0xff;
294 int endB = endInt & 0xff;
295
296 return (int) ((startA + (int) (fraction * (endA - startA))) << 24)
297 | (int) ((startR + (int) (fraction * (endR - startR))) << 16)
298 | (int) ((startG + (int) (fraction * (endG - startG))) << 8)
299 | (int) ((startB + (int) (fraction * (endB - startB))));
300 }
301
302 @Override
303 public void computeScroll() {
304 // TODO Auto-generated method stub
305 super.computeScroll();
306 // 2. 持續平滑動畫(高頻率調用)
307 if (mDragHelper.continueSettling(true)) {
308 // 如果返回true,動畫還需要繼續執行
309 ViewCompat.postInvalidateOnAnimation(this);
310 }
311 }
312
313 // public void chose() {
314 // // TODO Auto-generated method stub
315 // chose(true);
316 // }
317 public void chose() {
318 // TODO Auto-generated method stub
319 chose(true);
320 }
321
322 /**
323 * 關閉
324 *
325 * @param b
326 */
327 public void chose(boolean isSmooth) {
328 // TODO Auto-generated method stub
329 int finalLeft = 0;
330 // 1. 觸發一個平滑動畫
331 if (isSmooth) {
332 if (mDragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)) {
333 // 返回true代表還沒有移動到指定位置,需要刷新界面
334 // 參數傳this(child所在的ViewGroup)
335 ViewCompat.postInvalidateOnAnimation(this);
336 }
337 } else {
338 mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight);
339 }
340 }
341
342 public void open() {
343 // TODO Auto-generated method stub
344 open(true);
345 }
346
347 /**
348 * 開啟
349 *
350 * @param b
351 */
352 public void open(boolean isSmooth) {
353 // TODO Auto-generated method stub
354 int finalLeft = mRange;
355 if (isSmooth) {
356 // 1. 觸發一個平滑動畫
357 if (mDragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)) {
358 // 返回true代表還沒有移動到指定位置,需要刷新界面
359 // 參數this(child所在的ViewGroup)
360 ViewCompat.postInvalidateOnAnimation(this);
361 }
362 } else {
363 mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight);
364 }
365 }
366
367 /**
368 * b. 專遞觸摸事件
369 */
370 public boolean onInterceptTouchEvent(MotionEvent ev) {
371 // 傳遞給mDragHandler
372 return mDragHelper.shouldInterceptTouchEvent(ev);
373 };
374
375 @Override
376 public boolean onTouchEvent(MotionEvent event) {
377 // TODO Auto-generated method stub
378 try {
379 mDragHelper.processTouchEvent(event);
380 } catch (Exception e) {
381 e.printStackTrace();
382 }
383 // 返回true,持續接收事件
384 return true;
385 }
386
387 /**
388 * 當View中所有的子控件 均被映射成xml後觸發
389 */
390 @Override
391 protected void onFinishInflate() {
392 // TODO Auto-generated method stub
393 super.onFinishInflate();
394 if (getChildCount() < 2) {
395 throw new IllegalAccessError(
396 "布局至少有2個孩子. Your ViewGroup must have 2 children at least");
397 }
398 if (!(getChildAt(0) instanceof ViewGroup && getChildAt(1) instanceof ViewGroup)) {
399 throw new IllegalArgumentException(
400 "子View必須是ViewGroup的子類. Your children must be an instance of ViewGroup");
401 }
402 mLeftContent = (ViewGroup) getChildAt(0);
403 mMainContent = (ViewGroup) getChildAt(1);
404 }
405
406 /**
407 * 當view的大小發生變化時觸發
408 */
409 private int mRange;
410 private int mHeight;
411 private int mWidth;
412
413 @Override
414 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
415 // 當尺寸有變化的時候調用
416 super.onSizeChanged(w, h, oldw, oldh);
417 mHeight = getMeasuredHeight();
418 mWidth = getMeasuredWidth();
419 mRange = (int) (mWidth * 0.6f);
420 }
421
422 }
如下是主程序代碼
1 package com.demo.sb.main;
2
3 import com.demo.sb.mainfram.TentcentOneFragment;
4 import com.demo.sb.mainfram.TentcentThreeFragment;
5 import com.demo.sb.mainfram.TentcentTwoFragment;
6 import com.demo.sb.utils.Utils;
7 import com.demo.sb.widget.DragLayout;
8 import com.demo.sb.widget.DragLayout.OnDragStatusChangeListener;
9 import com.demo.sb.widget.MyListLinearLayout;
10 import com.demo.suibian.R;
11 import com.nineoldandroids.animation.ObjectAnimator;
12 import com.nineoldandroids.view.ViewHelper;
13
14 import android.os.Bundle;
15 import android.support.v4.app.FragmentActivity;
16 import android.support.v4.app.FragmentManager;
17 import android.support.v4.app.FragmentTransaction;
18 import android.view.View;
19 import android.view.Window;
20 import android.view.animation.CycleInterpolator;
21 import android.widget.ImageView;
22 import android.widget.RadioGroup;
23 import android.widget.RadioGroup.OnCheckedChangeListener;
24 import android.widget.TextView;
25
26 /**
27 * 側滑菜單
28 *
29 * @author Administrator
30 *
31 */
32 public class Activity_Tencent extends FragmentActivity {
33
34 private RadioGroup rg_tencent_mian;
35 // private FrameLayout fl_tencent_content;
36 private ImageView iv_header;
37 private TextView tv_header;
38
39 private TentcentOneFragment oneFragment;
40 private TentcentTwoFragment twoFragment;
41 private TentcentThreeFragment threeFragment;
42 private static DragLayout mDragLayout;
43
44 @Override
45 protected void onCreate(Bundle savedInstanceState) {
46 // TODO Auto-generated method stub
47 super.onCreate(savedInstanceState);
48 requestWindowFeature(Window.FEATURE_NO_TITLE);
49 setContentView(R.layout.activity_tencent);
50
51 initView();
52 initData();
53
54 }
55
56 private void initView() {
57 // TODO Auto-generated method stub
58 rg_tencent_mian = (RadioGroup) findViewById(R.id.rg_tencent_mian);
59 iv_header = (ImageView) findViewById(R.id.iv_header);
60 tv_header = (TextView) findViewById(R.id.tv_header);
61 MyListLinearLayout mLayout = (MyListLinearLayout) findViewById(R.id.mll);
62 mDragLayout = (DragLayout) findViewById(R.id.dl);
63 // 設置引用
64 mLayout.setDraglayout(mDragLayout);
65
66 mDragLayout.setDragStatusListener(new OnDragStatusChangeListener() {
67
68 @Override
69 public void onOpen() {
70 // TODO Auto-generated method stub
71 Utils.showToast(Activity_Tencent.this, "onOpen");
72 }
73
74 @Override
75 public void onDraging(float percent) {
76 // TODO Auto-generated method stub
77 // 跟新圖標的透明度
78 // 1.0 -> 0.0
79 ViewHelper.setAlpha(iv_header, 1 - percent);
80 }
81
82 @Override
83 public void onClose() {
84 // TODO Auto-generated method stub
85 Utils.showToast(Activity_Tencent.this, "onClose");
86 // 讓圖片開會晃動
87 ObjectAnimator mAnim = ObjectAnimator.ofFloat(iv_header,
88 "translationX", 15.0f);
89 mAnim.setInterpolator(new CycleInterpolator(4));
90 mAnim.setDuration(500);
91 mAnim.start();
92 }
93 });
94
95 iv_header.setOnClickListener(new View.OnClickListener() {
96
97 @Override
98 public void onClick(View arg0) {
99 // TODO Auto-generated method stub
100 mDragLayout.open(true);
101 }
102 });
103 }
104
105 private void initData() {
106 // TODO Auto-generated method stub
107 rg_tencent_mian.check(R.id.rb_tencent_one);
108
109 oneFragment = new TentcentOneFragment();
110 twoFragment = new TentcentTwoFragment();
111 threeFragment = new TentcentThreeFragment();
112 // 獲取fragment管理器
113 FragmentManager fragmentManager = getSupportFragmentManager();
114 // 打開事務
115 FragmentTransaction transaction = fragmentManager.beginTransaction();
116 // 把內容顯示到幀布局中
117 transaction.add(R.id.fl_tencent_content, oneFragment);
118 transaction.add(R.id.fl_tencent_content, twoFragment);
119 transaction.add(R.id.fl_tencent_content, threeFragment);
120 transaction.show(oneFragment).hide(twoFragment).hide(threeFragment);
121 // 提交
122 transaction.commit();
123 tv_header.setText("head");
124
125 rg_tencent_mian
126 .setOnCheckedChangeListener(new OnCheckedChangeListener() {
127
128 @Override
129 public void onCheckedChanged(RadioGroup arg0, int arg1) {
130 // TODO Auto-generated method stub
131 switch (arg1) {
132 case R.id.rb_tencent_one:
133 tv_header.setText("head");
134 getSupportFragmentManager().beginTransaction()
135 .show(oneFragment).hide(twoFragment)
136 .hide(threeFragment).commit();
137 break;
138 case R.id.rb_tencent_two:
139 tv_header.setText("聯系人");
140 getSupportFragmentManager().beginTransaction()
141 .show(twoFragment).hide(oneFragment)
142 .hide(threeFragment).commit();
143 break;
144 case R.id.rb_tencent_three:
145 tv_header.setText("動態");
146 getSupportFragmentManager().beginTransaction()
147 .show(threeFragment).hide(oneFragment)
148 .hide(twoFragment).commit();
149 break;
150 default:
151 break;
152 }
153 }
154 });
155 }
156
157 }
主程序的布局xml為

主界面是用繼承的FragmentActivity來實現下面3個按鈕的點擊事件,其中第一個Fragment(TentcentOneFragment)內只有個ListView
當停在側滑界面時,ListView不能進行上下滑動
TentcentOneFragment的代碼和xml布局


第二個Fragment(TentcentTwoFragment)內只有個ViewPager可以進行左右滑動不過還是有點問題


下面是2個工具類
第一個是自定義的線性布局可以當側滑界面打開時,讓ListView不能進行滑動

第二個是dp和px 的轉化還有自定義Toast的設置比較常用
1 package com.demo.sb.utils;
2
3 import android.content.Context;
4 import android.widget.Toast;
5
6 public class DensityUtil {
7
8 /**
9 * 根據手機的分辨率從dip的單位轉為px(像素)
10 */
11 public static int dip2px(Context context, float dp) {
12 float density = context.getResources().getDisplayMetrics().density;
13 int px = (int) (dp * density + 0.5f);// 4.9->5 4.4->4
14 return px;
15 }
16 /**
17 * 根據手機的分辨率從px(像素)的單位轉化成dip
18 * @param ctx
19 * @param px
20 * @return
21 */
22 public static float px2dip(Context ctx, int px) {
23 float density = ctx.getResources().getDisplayMetrics().density;
24 float dp = px / (density + 0.5f);
25
26 return dp;
27 }
28
29 /**
30 * 自定義吐司
31 *
32 */
33 public static Toast mToast;
34 public static void showToast(Context mContext, String msg) {
35 if (mToast == null) {
36 mToast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT);
37 }
38 mToast.setText(msg);
39 mToast.show();
40 }
41 }
Android制作粒子爆炸特效
Android制作粒子爆炸特效 簡介 最近在閒逛的時候,發現了一款粒子爆炸特效的控件,覺得比較有意思,效果也不錯。 但是代碼不好擴展,也就是說如果要提供不同的爆炸效果
UI-初識君面之理論篇,ui-初識理論篇
UI-初識君面之理論篇,ui-初識理論篇 一個好的app不光要用好的功能,還要有好的界面,這樣內外兼修才算得上
ArrayAdapter適配器的用法,模擬QQ發消息界面。,arrayadapter適配器
ArrayAdapter適配器的用法,模擬QQ發消息界面。,arrayadapter適配器 1 import java.util.ArrayList; 2 3 im
Android 手機衛士--平移動畫實現,android衛士
Android 手機衛士--平移動畫實現,android衛士本文實現如下幾個界面之間的平移動畫實現 本文地址:http://www.cnblogs.com/wuyudo