在前面的自己定义控件概述中已经跟大家分享了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回调中调用。保证在控件载入完毕之后再设置參数。

到这里这个控件就基本完毕了。事实上还有非常多能够优化的地方,比方把一些属性抽离出来,写成自己定义属性,还有下标依据传入数组动态生成等等,有兴趣的朋友能够交流一下。

源代码地址。

Androd自己定义控件(三)飞翔的小火箭的更多相关文章

  1. Android自己定义控件系列三:自己定义开关button(二)

    接上一篇自己定义开关button(一)的内容继续.上一次实现了一个开关button的基本功能.即自己定义了一个控件.开关button,实现了点击切换开关状态的功能.今天我们想在此基础之上.进一步实现触 ...

  2. Android自己定义控件(状态提示图表)

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重分享成果] 1 背景 前面分析那么多系统源代码了.也该暂停下来歇息一下,趁昨晚闲着看见一个有意思的需求就操 ...

  3. Android自己定义控件系列五:自己定义绚丽水波纹效果

    尊重原创!转载请注明出处:http://blog.csdn.net/cyp331203/article/details/41114551 今天我们来利用Android自己定义控件实现一个比較有趣的效果 ...

  4. Android自己定义控件系列二:自己定义开关button(一)

    这一次我们将会实现一个完整纯粹的自己定义控件,而不是像之前的组合控件一样.拿系统的控件来实现.计划分为三部分:自己定义控件的基本部分,自己定义控件的触摸事件的处理和自己定义控件的自己定义属性: 以下就 ...

  5. Android自己定义控件:进度条的四种实现方式

    前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源代码下载)http://down ...

  6. 【Asp.net之旅】--因自己定义控件注冊而引发的思考

    前言 近期在开发远洋的SOA系统平台,开发使用的是.NET平台.对于Asp.net并不困难,但该系统的开发并非全然依靠Asp.net.而是自身封装好的框架.这套框架是远洋地产购买的微软的开发平台,项目 ...

  7. 自己定义控件事实上非常easy1/6

    尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 上一节我们粗略地讲了下怎样去实现我 ...

  8. android 自己定义控件

    Android自己定义View实现非常easy 继承View,重写构造函数.onDraw.(onMeasure)等函数. 假设自己定义的View须要有自己定义的属性.须要在values下建立attrs ...

  9. Android 实现形态各异的双向側滑菜单 自己定义控件来袭

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935.本文出自:[张鸿洋的博客] 1.概述 关于自己定义控件側滑已经写了 ...

随机推荐

  1. 一种高兼容性的JavaBean序列化方案

    在对JavaBean做序列化时,我们可能在某些场景希望前后兼容性好一些.比如所有的javaBean都序列化后保存在数据库,用的时候需要反序列化创建.随着业务的发展,数据模型可能会进行变更,那么原来的数 ...

  2. JavaScript--Date 日期对象

    日期对象可以储存任意一个日期,并且可以精确到毫秒数(1/1000 秒). 定义一个时间对象 : var Udate=new Date(); 注意:使用关键字new,Date()的首字母必须大写. 使 ...

  3. 使用HBuilder新建项目

    依次点击文件→新建→选择Web项目(按下Ctrl+N,W可以触发快速新建(MacOS请使用Command+N,然后左键点击Web项目)) 如上图,请在A处填写新建项目的名称,B处填写(或选择)项目保存 ...

  4. 【转】基于linux下的变量声明declare的用法

    转自:http://techcurtman.iteye.com/blog/1249512 declare 功能介绍:声明变量的属性,如果使用declare,后面没有任何参数,那么bash就会主动将所有 ...

  5. 9-4前端vue面试的问题

    就没有什么顺序了,肖师傅的一些提问: 1- 配置文件中proxyTable的作用 2-@import '~styles/mixins.styl'  ~的作用 3-vue模拟的本地中访问地址的url带有 ...

  6. UIWebview 截获html并修改内容。

    需求:混合应用UIWebView打开html后,UIWebView有左右滚动条,要去掉左右滚动效果: 方法:通过js截获UIWebView中的html,然后修改html标签内容:  实例代码:  服务 ...

  7. java aop面向切面编程

    最近一直在学java的spring boot,一直没有弄明白aop面向切面编程是什么意思.看到一篇文章写得很清楚,终于弄明白了,原来跟python的装饰器一样的效果.http://www.cnblog ...

  8. CentOS6.9下NFS配置说明(转载)

    NFS是Network File System的缩写,即网络文件系统.它的主要功能是通过网络(一般是局域网)让不同的主机系统之间可以共享文件或目录.NFS客户端可以通过挂载(mount)的方式将NFS ...

  9. C#工具帮助类

    md5帮助类 .引入库 using System.Security.Cryptography;//引用Md5转换功能 .计算字符串的Md5值 public static string GetMD5Wi ...

  10. forcedirectories和CreateDirectory

    forcedirectories和CreateDirectory都能创建文件ForceDirectories可以创建多层目录. 如果你创建一个目录为c:\mymusic\music 如果你的C盘不存在 ...