Androd自定義控件(三)飛翔的小火箭

在前面的自定義控件概述中已經跟大家分享了Android開發當中自定義控件的種類。今天跟大家分享一個非主流的組合控件。
我們在開發當中,難免需要在不同的場合中重複使用一些控件的組合。而Java的最高目標呢,是消滅所有重複代碼。這個時候怎麼辦呢?辦法之一就是創建一個囊括邏輯和佈局的視圖,以便可以重複使用而不用在不同的場合中寫重複的代碼。代碼複用的同時我們還把邏輯包裝到了控件內部,做到更好的解耦。比如我們App頁面中的頂欄等等。
今天呢,跟大家分享一個我前一陣子在項目中遇到的實例。先看下效果圖:

需求:

  1. 該控件可以左右滑動。
  2. 底部積分是一個等差數列,可以自己定義。積分初始爲半透明,小紅旗下方顯示設定的最大值。小火箭會飛到當前用戶對應的積分位置,用戶得到的積分在小火箭動畫之後會顯示爲白色,同時當前積分位置出現一條標識線。
  3. 動畫開始的時候小火箭會從0開始移動,直到當前積分位置,在移動過程中小火箭會有一個噴射火焰的效果。
  4. 背景會隨着火箭的移動而移動,當動畫結束的時候,保證小火箭在屏幕中心。

實現方式:

自己寫一個類繼承HorizontalScrollView,HorizontalScrollView會幫我們處理左右滑動的事件,否則還要重寫ontouchEvent自己處理滑動。然後加載一個佈局文件,給小火箭加一個幀動畫和位移屬性動畫,實現小火箭的移動和噴火動畫。同時自定義一個動畫,來處理控件本身的滑動。

需要的技能點:

1.Android的view動畫和屬性動畫,以及簡單的自定義動畫。
2.view的繪製流程,詳情參照Androd自定義控件(一)概述
3.Activity中view的加載機制。
4.Android中dp,px等單位的概念。
5.用代碼創建控件。
6.LayoutParams的使用方法。

具體實現:

初始化,在這裏我們讓一個參數的構造方法調用兩個參數的構造方法,兩個參數的構造方法調用三個參數的構造方法,把初始化的方法放到三個參數的構造方法當中。在初始化方法中加載佈局文件。

public PointView(Context context) {
        this(context, null);
    }

    public PointView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PointView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        LayoutInflater.from(context).inflate(R.layout.point_view, this);
        initView();

        this.context = context;

        bottomLeftMargin = UIUtil.dip2px(context, 65);
    }

    private void initView() {
        //頂部內容區域
        content = (FrameLayout) findViewById(R.id.point_content);
        rocket = (ImageView) findViewById(R.id.point_rocket);

        //底部標註
        one = (TextView) findViewById(R.id.point_one);
        two = (TextView) findViewById(R.id.point_two);
        three = (TextView) findViewById(R.id.point_three);
        four = (TextView) findViewById(R.id.point_four);
        five = (TextView) findViewById(R.id.point_five);
        six = (TextView) findViewById(R.id.point_six);
        seven = (TextView) findViewById(R.id.point_seven);
        pointMax = (TextView) findViewById(R.id.point_max);

        mark = (LinearLayout) findViewById(R.id.point_mark);
        bottom = (FrameLayout) findViewById(R.id.point_bottom);
    }

佈局文件

<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:scrollbars="none">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@mipmap/point_view_bg"
        android:orientation="vertical">

        <!-- 內容區域 -->
        <FrameLayout
            android:id="@+id/point_content"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">

            <ImageView
                android:id="@+id/point_rocket"
                android:layout_width="80dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginTop="85dp"
                android:src="@mipmap/rocket_four" />

        </FrameLayout>

        <!-- 底部標註 -->
        <FrameLayout
            android:id="@+id/point_bottom"
            android:layout_width="match_parent"
            android:layout_height="57dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="30dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:layout_marginTop="7dp"
                    android:text="積分"
                    android:textColor="#fff"
                    android:textSize="14sp" />

                <LinearLayout
                    android:id="@+id/point_mark"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1">

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="vertical">

                        <LinearLayout
                            android:layout_width="50dp"
                            android:layout_height="match_parent"
                            android:gravity="center_horizontal"
                            android:orientation="vertical">

                            <ImageView
                                android:layout_width="1px"
                                android:layout_height="5dp"
                                android:background="@color/light_red" />

                            <TextView
                                android:id="@+id/point_one"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginTop="3dp"
                                android:text="0"
                                android:textColor="@color/white"
                                android:textSize="12sp" />
                        </LinearLayout>

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="vertical">

                        <LinearLayout
                            android:layout_width="50dp"
                            android:layout_height="match_parent"
                            android:gravity="center_horizontal"
                            android:orientation="vertical">

                            <ImageView
                                android:layout_width="1px"
                                android:layout_height="5dp"
                                android:background="@color/light_red" />

                            <TextView
                                android:id="@+id/point_two"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginTop="3dp"
                                android:text="300"
                                android:textColor="@color/zhuce"
                                android:textSize="12sp" />
                        </LinearLayout>

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="vertical">

                        <LinearLayout
                            android:layout_width="50dp"
                            android:layout_height="match_parent"
                            android:gravity="center_horizontal"
                            android:orientation="vertical">

                            <ImageView
                                android:layout_width="1px"
                                android:layout_height="5dp"
                                android:background="@color/light_red" />

                            <TextView
                                android:id="@+id/point_three"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginTop="3dp"
                                android:text="600"
                                android:textColor="@color/zhuce"
                                android:textSize="12sp" />
                        </LinearLayout>

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="vertical">

                        <LinearLayout
                            android:layout_width="50dp"
                            android:layout_height="match_parent"
                            android:gravity="center_horizontal"
                            android:orientation="vertical">

                            <ImageView
                                android:layout_width="1px"
                                android:layout_height="5dp"
                                android:background="@color/light_red" />

                            <TextView
                                android:id="@+id/point_four"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginTop="3dp"
                                android:text="900"
                                android:textColor="@color/zhuce"
                                android:textSize="12sp" />
                        </LinearLayout>

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="vertical">

                        <LinearLayout
                            android:layout_width="50dp"
                            android:layout_height="match_parent"
                            android:gravity="center_horizontal"
                            android:orientation="vertical">

                            <ImageView
                                android:layout_width="1px"
                                android:layout_height="5dp"
                                android:background="@color/light_red" />

                            <TextView
                                android:id="@+id/point_five"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginTop="3dp"
                                android:text="1200"
                                android:textColor="@color/zhuce"
                                android:textSize="12sp" />
                        </LinearLayout>

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="vertical">

                        <LinearLayout
                            android:layout_width="50dp"
                            android:layout_height="match_parent"
                            android:gravity="center_horizontal"
                            android:orientation="vertical">

                            <ImageView
                                android:layout_width="1px"
                                android:layout_height="5dp"
                                android:background="@color/light_red" />

                            <TextView
                                android:id="@+id/point_six"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginTop="3dp"
                                android:text="1500"
                                android:textColor="@color/zhuce"
                                android:textSize="12sp" />
                        </LinearLayout>

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="vertical">

                        <LinearLayout
                            android:layout_width="50dp"
                            android:layout_height="match_parent"
                            android:gravity="center_horizontal"
                            android:orientation="vertical">

                            <ImageView
                                android:layout_width="1px"
                                android:layout_height="5dp"
                                android:background="@color/light_red" />

                            <TextView
                                android:id="@+id/point_seven"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginTop="3dp"
                                android:text="1800"
                                android:textColor="@color/zhuce"
                                android:textSize="12sp" />
                        </LinearLayout>

                    </LinearLayout>

                </LinearLayout>

                <TextView
                    android:id="@+id/point_max"
                    android:layout_width="58dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:layout_marginTop="7dp"
                    android:gravity="center"
                    android:textColor="@color/zhuce"
                    android:textSize="12sp" />

                <View
                    android:layout_width="33dp"
                    android:layout_height="match_parent" />
            </LinearLayout>
        </FrameLayout>

    </LinearLayout>

</HorizontalScrollView>

然後在onlayout方法中拿到我們需要的底部標註的長度,用來計算小火箭和view動畫的位移。

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        markLength = mark.getMeasuredWidth();
//        L.e(TAG, "markLength---" + markLength);
    }

設置顯示標註線

/**
     * 設置當前分數
     *
     * @param point
     */
    public void setCurrentPoint(int point) {
        int location;
        if (point < MAX_POINT) {
            location = (int) ((point / MAX_POINT) * markLength + bottomLeftMargin);//算出當前分數顯示位置的偏移量
        } else {
            location = markLength + bottomLeftMargin + UIUtil.dip2px(context, 12);
        }

        //標註當前位置,
        ImageView line = new ImageView(context);
        line.setImageDrawable(getResources().getDrawable(R.color.point_line));
        bottom.addView(line);
        LayoutParams linePa = (LayoutParams) line.getLayoutParams();
        linePa.leftMargin = location;
        linePa.width = UIUtil.dip2px(context, 1);
        linePa.height = UIUtil.dip2px(context, 58);
        line.setLayoutParams(linePa);

//        L.e(TAG, "location---" + location + ";bottomLeftMargin---" + bottomLeftMargin);
    }

火箭的動畫

//火箭平移動畫
        ObjectAnimator rocketAni = ObjectAnimator.ofFloat(rocket, "translationX", rocketX);
        DecelerateInterpolator interpolator = new DecelerateInterpolator();
        rocketAni.setInterpolator(interpolator);
        rocketAni.setDuration(DEFAULT_DURATION);
        rocketAni.start();

        //火箭切換動畫
        rocket.setImageResource(R.drawable.rocket_frame);
        final AnimationDrawable animationDrawable = (AnimationDrawable) rocket.getDrawable();
        animationDrawable.start();

        rocketAni.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                //停止幀動畫
                animationDrawable.stop();
                rocket.setImageResource(R.mipmap.rocket_three);
                //設置當前積分標註線
                setCurrentPoint(point);
                //設置已經到達積分爲白色
                setMarkColor(point, 300);
            }
        });

因爲scroller自帶的滾動插值器與火箭動畫插值器不同步,所以使用自定義動畫實現控件的平滑滾動

/**
     * 自定義動畫,控制scrollview滾動
     */
    public class ViewAnimation extends Animation {

        private int viewX;

        public ViewAnimation(int viewX) {
            this.viewX = viewX;
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            smoothScrollTo((int) (viewX * interpolatedTime), 0);
        }
    }
    //view滾動動畫
        /**
         * scroller自帶的滾動插值器與火箭動畫插值器不同步,所以使用自定義動畫實現平滑滾動
         */
        ViewAnimation viewAnimation = new ViewAnimation(finalViewX);
        viewAnimation.setDuration(DEFAULT_DURATION);
        viewAnimation.setInterpolator(interpolator);
        this.setAnimation(viewAnimation);
        viewAnimation.start();

調用

@Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        mPoinView.setMaxPoint(2500);
        mPoinView.startAni(600);
    }

這裏我們在onWindowFocusChanged回調中調用,保證在控件加載完成之後再設置參數。

到這裏這個控件就基本完成了。其實還有很多可以優化的地方,比如把一些屬性抽離出來,寫成自定義屬性,還有下標根據傳入數組動態生成等等,有興趣的朋友可以交流一下。源碼地址。

發佈了40 篇原創文章 · 獲贊 143 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章