很久以前,發現QQ有一個很有趣的功能,就是未讀消息的紅點是可以拖拽的,而且在任何地方都可以隨意拖拽,并且有一個彈性的動畫,非常有趣,而且也是一個非常方便的功能,于是總想仿制一個,雖說仿制,但也只是他的拖拽功能,彈性效果還是能力有限。
不多說 先上效果
一個自定義的view 使用方式也很簡單
<com.weizhenbin.show.widget.VanishView android:layout_width="30dp" android:layout_height="30dp" android:text="5" android:layout_alignParentBottom="true" android:gravity="center" android:textColor="#fff" android:id="@+id/vv" android:layout_marginBottom="35dp" android:layout_marginLeft="80dp" android:background="@drawable/shape_red_bg"/>
然后先看下源碼
** * Created by weizhenbin on 16/6/1. * <p/> * 一個可以隨意拖動的view */public class VanishView extends TextView { private Context context; /**窗口管理器*/ private WindowManager windowManager; /**用來存儲鏡像的imageview*/ private ImageView iv; /** 狀態欄高度*/ private int statusHeight = 0; /**按下的坐標x 相對于view自身*/ private int dx = 0; /**按下的坐標y 相對于view自身*/ private int dy = 0; /**鏡像bitmap*/ private Bitmap tmp; /**按下的坐標x 相對于屏幕*/ private float downX = 0; /**按下的坐標y 相對于屏幕*/ private float downY = 0; /**屬性動畫 用于回彈效果*/ private ValueAnimator animator; /**窗口參數*/ private WindowManager.LayoutParams mWindowLayoutParams; /**接口對象*/ private OnListener listener; public VanishView(Context context) { super(context); init(context); } public VanishView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public VanishView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { this.context = context; windowManager = ((Activity) context).getWindowManager(); statusHeight = getStatusHeight(context); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: dx = (int) event.getX(); dy = (int) event.getY(); downX = event.getRawX(); downY = event.getRawY(); addWindow(context, event.getRawX(), event.getRawY()); setVisibility(INVISIBLE); break; case MotionEvent.ACTION_MOVE: mWindowLayoutParams.x = (int) (event.getRawX() - dx); mWindowLayoutParams.y = (int) (event.getRawY() - statusHeight - dy); windowManager.updateViewLayout(iv, mWindowLayoutParams); break; case MotionEvent.ACTION_UP: int distance=distance(new MyPoint(event.getRawX(), event.getRawY()), new MyPoint(downX, downY)); if(distance<400) { scroll(new MyPoint(event.getRawX(), event.getRawY()), new MyPoint(downX, downY)); }else { if(listener!=null){ listener.onDismiss(); } windowManager.removeView(iv); } break; } return true; } /** * 構建一個窗口 用于存放和移動鏡像 * */ private void addWindow(Context context, float downX, float dowmY) { mWindowLayoutParams = new WindowManager.LayoutParams(); mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; iv = new ImageView(context); mWindowLayoutParams.format = PixelFormat.RGBA_8888; mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; mWindowLayoutParams.x = (int) (downX - dx); mWindowLayoutParams.y = (int) (dowmY - statusHeight - dy); //獲取view的鏡像bitmap this.setDrawingCacheEnabled(true); tmp = Bitmap.createBitmap(this.getDrawingCache()); //釋放緩存 this.destroyDrawingCache(); iv.setImageBitmap(tmp); windowManager.addView(iv, mWindowLayoutParams); } /** * 使用屬性動畫 實現緩慢回彈效果 * */ private void scroll(MyPoint start, MyPoint end) { animator = ValueAnimator.ofObject(new MyTypeEvaluator(), start, end); animator.setDuration(200); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { MyPoint point = (MyPoint) animation.getAnimatedValue(); mWindowLayoutParams.x = (int) (point.x - dx); mWindowLayoutParams.y = (int) (point.y - statusHeight - dy); windowManager.updateViewLayout(iv, mWindowLayoutParams); } }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); windowManager.removeView(iv); setVisibility(VISIBLE); } }); animator.start(); } /** * 計算兩點的距離 */ private int distance(MyPoint point1, MyPoint point2) { int distance = 0; if (point1 != null && point2 != null) { float dx = point1.x - point2.x; float dy = point1.y - point2.y; distance = (int) Math.sqrt(dx * dx + dy * dy); } return distance; } /** * 獲取狀態欄的高度 */ private static int getStatusHeight(Context context) { int statusHeight = 0; Rect localRect = new Rect(); ((Activity) context).getWindow().getDecorView().getWindowVisibleDisplayFrame(localRect); statusHeight = localRect.top; if (0 == statusHeight) { Class<?> localClass; try { localClass = Class.forName("com.android.internal.R$dimen"); Object localObject = localClass.newInstance(); int i5 = Integer.parseInt(localClass.getField("status_bar_height").get(localObject).toString()); statusHeight = context.getResources().getDimensionPixelSize(i5); } catch (Exception e) { e.printStackTrace(); } } return statusHeight; } class MyPoint { float x; float y; public MyPoint(float x, float y) { this.x = x; this.y = y; } @Override public String toString() { return "MyPoint{" + "x=" + x + ", y=" + y + '}'; } } class MyTypeEvaluator implements TypeEvaluator<MyPoint> { @Override public MyPoint evaluate(float fraction, MyPoint startValue, MyPoint endValue) { MyPoint point = startValue; point.x = startValue.x + fraction * (endValue.x - startValue.x); point.y = startValue.y + fraction * (endValue.y - startValue.y); return point; } } /**事件回調借口*/ public interface OnListener{ void onDismiss(); } public void setListener(OnListener listener) { this.listener = listener; }
實現這一功能其實也不難,這個功能涉及到以下幾個知識點
使用WindowManager添加一個view
使用ValueAnimator屬性動畫實現回彈效果
getX和getRawX,getY和getRawY的區別
1.使用WindowManager添加一個view
/** * 構建一個窗口 用于存放和移動鏡像 * */ private void addWindow(Context context, float downX, float dowmY) { mWindowLayoutParams = new WindowManager.LayoutParams(); mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; iv = new ImageView(context); mWindowLayoutParams.format = PixelFormat.RGBA_8888; mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; mWindowLayoutParams.x = (int) (downX - dx); mWindowLayoutParams.y = (int) (dowmY - statusHeight - dy); //獲取view的鏡像bitmap this.setDrawingCacheEnabled(true); tmp = Bitmap.createBitmap(this.getDrawingCache()); //釋放緩存 this.destroyDrawingCache(); iv.setImageBitmap(tmp); windowManager.addView(iv, mWindowLayoutParams); }
這一步是為了投影一個鏡像來達到拖動view的一個假像效果,使用imageview來顯示。這里為了使投影沒用偏移需要了解getX getRawX getY getRawY的區別
getX和getY 是相對于view自身的,getRawX和getRawY是像對屏幕的,這里還要扣除掉狀態欄的高度。
2.移動
windowManager.updateViewLayout(iv, mWindowLayoutParams);
3.使用ValueAnimator屬性動畫實現回彈效果
這里自定義了TypeEvaluator實現點的位移動畫
class MyTypeEvaluator implements TypeEvaluator<MyPoint> { @Override public MyPoint evaluate(float fraction, MyPoint startValue, MyPoint endValue) { MyPoint point = startValue; point.x = startValue.x + fraction * (endValue.x - startValue.x); point.y = startValue.y + fraction * (endValue.y - startValue.y); return point; } } /** * 使用屬性動畫 實現緩慢回彈效果 * */ private void scroll(MyPoint start, MyPoint end) { animator = ValueAnimator.ofObject(new MyTypeEvaluator(), start, end); animator.setDuration(200); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { MyPoint point = (MyPoint) animation.getAnimatedValue(); mWindowLayoutParams.x = (int) (point.x - dx); mWindowLayoutParams.y = (int) (point.y - statusHeight - dy); windowManager.updateViewLayout(iv, mWindowLayoutParams); } }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); windowManager.removeView(iv); setVisibility(VISIBLE); } }); animator.start(); }
通過屬性動畫實現一個回彈效果
4.觸發消失的時機
/** * 計算兩點的距離 */ private int distance(MyPoint point1, MyPoint point2) { int distance = 0; if (point1 != null && point2 != null) { float dx = point1.x - point2.x; float dy = point1.y - point2.y; distance = (int) Math.sqrt(dx * dx + dy * dy); } return distance; }
計算兩點之間的距離來觸發一個回調事件。
int distance=distance(new MyPoint(event.getRawX(), event.getRawY()), new MyPoint(downX, downY)); if(distance<400) { scroll(new MyPoint(event.getRawX(), event.getRawY()), new MyPoint(downX, downY)); }else { if(listener!=null){ listener.onDismiss(); } windowManager.removeView(iv); }
代碼分析就到這里,實現這個功能的核心代碼都在這里。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
新聞熱點
疑難解答