屬性動畫:如何自定義View
道長今天說一下自定義View的實現,因為有很大一部分自定義View都帶有動畫,所以把自定義View放到屬性動畫裡聊聊,let’s go……
一、需求明確
首先明確需求,根據需求建立佈局,這裡道長就自己定需求了:
1.拖動紅色方塊,紅色沿著中心旋轉並且顏色便為綠色,藍色沿著Y軸旋轉
2.拖動藍色方塊,紅色沿著中心旋轉並且顏色便為綠色,藍色沿著Y軸旋轉
3.拖動綠色方塊,紅色沿著中心旋轉並且顏色便為綠色,藍色沿著Y軸旋轉
4.三種方塊只可在螢幕內拖動
這裡需求對佈局的要求不明顯,我們們把整個介面作為自定義View,程式碼如下:
<?xml version="1.0" encoding="utf-8"?>
<com.yushan.animdemo.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.yushan.animdemo.MainActivity">
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#ff0000" />
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#0000ff" />
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#00ff00" />
</com.yushan.animdemo.DragLayout>
效果如下:
二、實現自定義View
- 首先重寫構造方法:
public DragLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public DragLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DragLayout(Context context) {
super(context);
init();
}
private void init(){
scroller = new Scroller(getContext());
viewDragHelper = ViewDragHelper.create(this, callback);
}
- 獲取子View
/**
* 當載入完佈局xml的時候會執行該方法,所以執行該方法 的時候就能夠知道當前的ViewGroup
* 有多少個子View,但是此時並不知道子View的寬高是多少,因為還沒有測量
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
redView = getChildAt(0);
blueView = getChildAt(1);
yellowView = getChildAt(2);
}
- 測量自己和子控制元件的寬高(這個不是必須的)
/**
* 測量自己和子控制元件的寬高
* MeasureSpec: 測量規則,由size和mode組成
* size:表示的是具體的大小值
* mode:測量模式 封裝的是我們在佈局xml中的寬高引數
*
* MeasureSpec.AT_MOST: 對應的是wrap_content;
* MeasureSpec.EXACTLY: 對應的是具體的dp值,match_parent;
* MeasureSpec.UNSPECIFIED: 未定義的,一般只在adapter的測量中用到
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//構建測量規則
//測量紅孩子
int measureSpec = MeasureSpec.makeMeasureSpec(redView.getLayoutParams().width,MeasureSpec.EXACTLY);
// redView.measure(measureSpec, measureSpec);
// //測量藍精靈
// blueView.measure(measureSpec, measureSpec);
//更加簡單的測量子View的方法是這樣的:
measureChild(redView, widthMeasureSpec, heightMeasureSpec);
measureChild(blueView, widthMeasureSpec, heightMeasureSpec);
}
- 放置控制元件(這個是控制元件的初始位置,必須要有)
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = 0;
int top = 0;
redView.layout(left,top,left+redView.getMeasuredWidth(), top+redView.getMeasuredHeight());
blueView.layout(left,redView.getBottom(),left+blueView.getMeasuredWidth(), redView.getBottom()+blueView.getMeasuredHeight());
yellowView.layout(left,blueView.getBottom(),left+yellowView.getMeasuredWidth(), blueView.getBottom()+yellowView.getMeasuredHeight());
//將某個子View提到最上面
// bringChildToFront(redView);
}
- 攔截事件(這裡道長整合了NineOldAndroid並重寫了Callback類中的方法)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 讓viewDragHelper幫助我們判斷是否應該攔截
boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//將TouchEvent傳遞給viewDragHelper來處理
viewDragHelper.processTouchEvent(event);
return true;
}
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
/**
* 是否捕獲view的觸控
* child: 表示當前所觸控的子VIew
* return: true:會捕獲 false:不會捕獲,即忽略
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child==blueView || child==redView || child==yellowView;
}
/**
* 當View被捕獲的時候會回撥
*/
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
// Log.e("tag", "onViewCaptured");
}
/**
* 獲取view水平方向拖拽範圍,但是目前並不起作用,但是最好還要實現下,不要
* 返回0,它目前返回的值會用在計算view釋放移動的動畫時間計算上面
*/
@Override
public int getViewHorizontalDragRange(View child) {
return DragLayout.this.getMeasuredWidth()-blueView.getMeasuredWidth();
}
/**
* 控制child在水平方向的移動
* child:當前所觸控的子View
* left:表示ViewDragHelper幫你計算好的child的最終要變成的left值, left=child.getLeft()+dx
* dx:表示本次水平移動的距離
* return: 表示我們真正想讓child的left變成的值
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if(left<0){
left = 0;
}
return left;
}
/**
* 控制child在垂直方向的移動
* child:當前所觸控的子View
* top:表示ViewDragHelper幫你計算好的child的最終要變成的top值, top=child.getTop()+dy
* dy:表示本次垂直移動的距離
* return: 表示我們真正想讓child的top變成的值
*/
public int clampViewPositionVertical(View child, int top, int dy) {
if(top<0){
top = 0;
}
return top;
}
/**
* 當view位置改變的回撥,一般用來實現view的伴隨移動
* changedView:表示當前位置改變了的view
* left:changedView的最新的left
* top:changedView的最新的top
* dx:changedView本次水平移動距離
* dy:changedView本次垂直移動距離
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if(changedView==blueView){
//讓redView跟隨移動
redView.layout(redView.getLeft()+dx,redView.getTop()+dy, redView.getRight()+dx, redView.getBottom()+dy);
}else if (changedView==redView) {
//讓blueView跟隨移動
blueView.layout(blueView.getLeft()+dx,blueView.getTop()+dy, blueView.getRight()+dx, blueView.getBottom()+dy);
} else if (changedView==yellowView){
//讓redView跟隨移動
redView.layout(redView.getLeft()+dx,redView.getTop()+dy, redView.getRight()+dx, redView.getBottom()+dy);
//讓blueView跟隨移動
blueView.layout(blueView.getLeft()+dx,blueView.getTop()+dy, blueView.getRight()+dx, blueView.getBottom()+dy);
}
//1.計算移動的百分比
int maxLeft = DragLayout.this.getMeasuredWidth()-blueView.getMeasuredWidth();
float fraction = changedView.getLeft()*1f/maxLeft;
//2.根據移動的百分比執行很多的伴隨動畫
executeAnim(fraction);
}
/**
* 當View釋放的時候執行,就是touch_up
* releasedChild:當前抬起的子VIew
* xvel:x方向移動的速度
* yvel:y方向移動的速度
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
// Log.e("tag", "xvel:"+xvel + " yvel: "+yvel);
//首先算出在正中間的left
int centerLeft = getMeasuredWidth()/2-releasedChild.getMeasuredWidth()/2;
if(releasedChild.getLeft()<centerLeft){
//說明在左半邊
viewDragHelper.smoothSlideViewTo(releasedChild,0,releasedChild.getTop());
ViewCompat.postInvalidateOnAnimation(DragLayout.this);
// scroller.startScroll(startX, startY, dx, dy, duration);
// invalidate();
}else {
//說明在右半邊
int finalLeft = getMeasuredWidth()-releasedChild.getMeasuredWidth();
viewDragHelper.smoothSlideViewTo(releasedChild,finalLeft,releasedChild.getTop());
ViewCompat.postInvalidateOnAnimation(DragLayout.this);
}
}
};
- 執行動畫方法
/**
* 執行動畫
* @param fraction
*/
private void executeAnim(float fraction){
//旋轉
// blueView.setRotation(360*fraction);//設定旋轉的角度
// blueView.setRotationX(360*fraction);//設定圍繞x軸旋轉的角度
blueView.setRotationY(360*fraction);//設定圍繞Y軸旋轉的角度
//使用NineOldAndroid中的方法
ViewHelper.setRotation(redView, 360*fraction);//設定旋轉的角度
// ViewHelper.setScaleX(redView, 1+fraction*0.5f);
// ViewHelper.setScaleY(redView, 1+fraction*0.5f);
//進行顏色的過度變化
redView.setBackgroundColor((Integer) ColorUtil.evaluateColor(fraction, Color.RED,Color.GREEN));
}
到這裡就把View定義好了,當然這個View可以放到其他介面中,通過動畫自定義View的好處是很明顯的,就是實現簡單靈活。希望這篇部落格可以給你一些幫助。
原始碼下載
相關文章
- Android 自定義View:屬性動畫(六)AndroidView動畫
- 自定義View:自定義屬性(自定義按鈕實現)View
- Android自定義View 屬性新增AndroidView
- 給自定義View新增xml屬性ViewXML
- HenCoder Android 自定義 View 1-6: 屬性動畫(上手篇)AndroidView動畫
- Android 自定義View:深入理解自定義屬性(七)AndroidView
- 這可能是第二好的自定義 View 教程之屬性動畫View動畫
- 安卓Property Animator動畫詳解(二)-自定義屬性安卓動畫
- 自定義View:Paint的常用屬性介紹及使用ViewAI
- Android開發自定義View之滑動按鈕與自定義屬性AndroidView
- 【朝花夕拾】Android自定義View篇之(四)自定義View的三種實現方式及自定義屬性詳解AndroidView
- Android自定義View播放Gif動畫AndroidView動畫
- Android 自定義View之下雨動畫AndroidView動畫
- 【HenCoder Android 開發進階】自定義 View 1-7:屬性動畫(進階篇)AndroidView動畫
- Android 動畫詳解:屬性動畫、View 動畫和幀動畫Android動畫View
- Android View動畫和屬性動畫簡單解析:AndroidView動畫
- CSS 自定義屬性指北CSS
- data-* 自定義屬性
- Android自定義屬性Android
- objc系列譯文(12.2):Layer中自定義屬性的動畫OBJ動畫
- 自定義view 之多個引導層動畫效果View動畫
- 自定義View:側滑選單動畫實現View動畫
- Android自定義控制元件——自定義屬性Android控制元件
- 初識css自定義屬性CSS
- CSS 自定義屬性(變數)CSS變數
- easyui tree自定義屬性用法UI
- CSS自定義屬性Expression(轉)CSSExpress
- 4. 自定義控制元件(4) --- 自定義屬性控制元件
- Android自定義控制元件之自定義屬性Android控制元件
- 自定義VIEWView
- 再談屬性動畫——介紹以及自定義Interpolator插值器動畫
- Android 自定義view中的屬性,名稱空間,以及tools標籤AndroidView
- ubuntu下OpenLDAP新增自定義屬性UbuntuLDA
- 使用 CSS 自定義屬性(變數)CSS變數
- 如何寫好一個自定義ViewView
- 如何理解Android屬性動畫Android動畫
- 自定義View公式View公式
- Android自定義組合控制元件之自定義屬性Android控制元件