如下圖是側滑的效果圖 實現的功能主要是用ViewDragHelper,用ViewDragHelper來自定義一個側滑面板來實現側滑 如下是自定義的側滑面板 1 package com.demo.sb.widget; 2 3 import com.nineoldandroids.view.ViewHe
如下圖是側滑的效果圖
實現的功能主要是用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 met