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回调中调用,保证在控件加载完成之后再设置参数。

到这里这个控件就基本完成了。其实还有很多可以优化的地方,比如把一些属性抽离出来,写成自定义属性,还有下标根据传入数组动态生成等等,有兴趣的朋友可以交流一下。源码地址。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值