Android 自定义View之边缘凹凸的优惠券效果的开发过程

作者:qijingwang 时间:2021-12-30 07:25:10 

本篇文章讲的是自定义View之边缘凹凸的优惠券效果,之前有见过很多优惠券的效果都是使用了边缘凹凸的样式。和往常一样,主要总结一下在自定义View的开发过程中需要注意的一些地方。

按照惯例,我们先来看看效果图

Android 自定义View之边缘凹凸的优惠券效果的开发过程

一、写代码之前,我们先弄清楚view的启动过程:

之所以想要弄清楚这个问题是因为代码里面用到了onSizeChanged()方法,一开始我有点犹豫onSizeChanged是在什么时候启动的呢,所以看看View的启动流程吧


package per.lijuan.coupondisplayviewdome;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;
/**
* 自定义边缘凹凸的优惠券效果view
* Created by lijuan on 2016/9/26.
*/
public class CouponDisplayView extends LinearLayout {
 public CouponDisplayView(Context context) {
   this(context, null);
   Log.d("mDebug", "CouponDisplayView:context");
 }
 public CouponDisplayView(Context context, AttributeSet attrs) {
   this(context, attrs, 0);
   Log.d("mDebug", "CouponDisplayView:context,attrs");
 }
 public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr) {
   super(context, attrs, defStyleAttr);
   Log.d("mDebug", "CouponDisplayView:context,attrs,defStyleAttr");
 }
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   super.onSizeChanged(w, h, oldw, oldh);
   Log.d("mDebug", "onSizeChanged:w=" + w + ",h=" + h + ",oldw=" + oldw + ",oldh=" + oldh);
 }
 @Override
 protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);
   Log.d("mDebug", "onDraw");
 }
}

输出如下:


09-27 15:29:31.957 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: CouponDisplayView:context,attrs,defStyleAttr
09-27 15:29:31.957 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: CouponDisplayView:context,attrs
09-27 15:29:32.050 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: onSizeChanged:w=984,h=361,oldw=0,oldh=0
09-27 15:29:32.083 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: onDraw

在这里可以看到,onSizeChanged()方法的启动是在onDraw之前

二、view的几个常用触发方法

1. onFinishInflate():当View中所有的子控件均被映射成xml后触发
2. onMeasure(int widthMeasureSpec, int heightMeasureSpec):确定所有子元素的大小
3. onLayout(boolean changed, int l, int t, int r, int b):当View分配所有的子元素的大小和位置时触发
4. onSizeChanged(int w, int h, int oldw, int oldh):当view的大小发生变化时触发
5. onDraw(Canvas canvas):负责将View绘制在屏幕上

三、View 的几个构造函数

1、public CouponDisplayView(Context context)
—>Java代码直接new一个CouponDisplayView实例的时候,会调用这个只有一个参数的构造函数;

2、public CouponDisplayView(Context context, AttributeSet attrs)
—>在默认的XML布局文件中创建的时候调用这个有两个参数的构造函数。AttributeSet类型的参数负责把XML布局文件中所自定义的属性通过AttributeSet带入到View内;

3、public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr)
—>构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或者Activity所用的Theme中的默认Style,且只有在明确调用的时候才会调用

4、public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
—>该构造函数是在API21的时候才添加上的

自定义View中,我们需要重写了3个构造方法,在上面的构造方法中说过默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造方法调用三个参数的构造方法,然后在三个参数的构造方法中获得自定义属性。
一开始一个参数的构造方法和两个参数的构造方法是这样的:


public CouponDisplayView(Context context) {
   super(context);
 }
 public CouponDisplayView(Context context, AttributeSet attrs) {
   super(context, attrs);
 }

我们需要注意的是super应该改成this,然后让一个参数的构造方法引用两个参数的构造方法,两个参数的构造方法引用三个参数的构造方法,代码如下:


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

四、分析具体的实现思路:

从上面的效果图来看,这个自定义View和普通的Linearlayout,RelativeLayout一样,只是上下两边多了类似于半圆锯齿的形状,我们需要在上下两条线上画一个个白色的小圆来实现这种效果。

假如我们上下线的半圆以及半圆与半圆之间的间距是固定的,那么不同尺寸的屏幕肯定会画出不同数量的半圆,那么我们只需要根据控件的宽度来获取能画的半圆数。

我们观察效果图会发现,圆的数量总是圆间距数量-1,也就是说,假设圆的数量是circleNum,那么圆间距就是circleNum+1,所以我们可以根据这个计算出circleNum:


circleNum = (int) ((w-gap)/(2*radius+gap));

这里gap就是圆间距,radius是圆半径,w是view的宽。

五、下面我们就开始来看看代码啦

1、自定义View的属性,首先在res/values/ 下建立一个attr.xml , 在里面定义我们的需要用到的属性以及声明相对应属性的取值类型


<?xml version="1.0" encoding="utf-8"?>
<resources>
 <!--圆间距-->
 <attr name="radius" format="dimension" />
 <!--半径-->
 <attr name="gap" format="dimension" />
 <declare-styleable name="CouponDisplayView">
   <attr name="radius" />
   <attr name="gap" />
 </declare-styleable>
</resources>

我们定义了圆间距和半径2个属性,format是值该属性的取值类型,format取值类型总共有10种,包括:string,color,demension,integer,enum,reference,float,boolean,fraction和flag。

2、然后在XML布局中声明我们的自定义View


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:custom="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_margin="16dp">
 <per.lijuan.coupondisplayviewdome.CouponDisplayView
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:background="#FBB039"
   android:orientation="horizontal"
   android:padding="16dp"
   custom:gap="8dp"
   custom:radius="5dp">
   <ImageView
     android:layout_width="90dp"
     android:layout_height="match_parent"
     android:scaleType="centerCrop"
     android:src="@mipmap/ic_launcher" />
   <LinearLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginLeft="5dp"
     android:orientation="vertical">
     <TextView
       android:id="@+id/name"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="电影新客代金劵"
       android:textSize="18dp" />
     <TextView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:paddingTop="5dp"
       android:text="编号:525451122312431"
       android:textSize="12dp" />
     <TextView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:paddingTop="5dp"
       android:text="满200元可用、限最新版本客户端使用"
       android:textSize="12dp" />
     <TextView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:paddingTop="5dp"
       android:text="截止日期:2016-11-07"
       android:textSize="12dp" />
   </LinearLayout>
 </per.lijuan.coupondisplayviewdome.CouponDisplayView>
</LinearLayout>

一定要引入xmlns:custom=”http://schemas.android.com/apk/res-auto”,Android Studio中我们可以使用res-atuo命名空间,就不用添加自定义View全类名。

3、在View的构造方法中,获得我们的xml布局文件中定义的圆的半径和圆间距


private Paint mPaint;
 /**
  * 半径
  */
 private float radius=10;
 /**
  * 圆间距
  */
 private float gap=8;
 /**
  * 圆数量
  */
 private int circleNum;
 private float remain;
 public CouponDisplayView(Context context) {
   this(context, null);
   Log.d("mDebug", "CouponDisplayView context");
 }
 public CouponDisplayView(Context context, AttributeSet attrs) {
   this(context, attrs, 0);
   Log.d("mDebug", "CouponDisplayView context, attrs");
 }
 public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr) {
   super(context, attrs, defStyleAttr);
   Log.d("mDebug", "CouponDisplayView context,attrs,defStyleAttr");
   /**
    * 获得我们所定义的自定义样式属性
    */
   TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CouponDisplayView, defStyleAttr, 0);
   for (int i = 0; i < a.getIndexCount(); i++) {
     int attr = a.getIndex(i);
     switch (attr) {
       case R.styleable.CouponDisplayView_radius:
         radius = a.getDimensionPixelSize(R.styleable.CouponDisplayView_radius, 10);
         break;
       case R.styleable.CouponDisplayView_gap:
         gap = a.getDimensionPixelSize(R.styleable.CouponDisplayView_radius, 8);
         break;
     }
   }
   a.recycle();
   mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   mPaint.setDither(true);
   mPaint.setColor(Color.WHITE);
   mPaint.setStyle(Paint.Style.FILL);
 }

4、重写onSizeChanged()方法,根据上面的圆的半径和圆间距来计算需要画的圆数量circleNum


@Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   super.onSizeChanged(w, h, oldw, oldh);
   Log.d("mDebug", "onSizeChanged,w=" + w + ",h=" + h + ",oldw=" + oldw + ",oldh=" + oldh);
   if (remain == 0) {
     //计算不整除的剩余部分
     remain = (int) (w - gap) % (2 * radius + gap);
   }
   circleNum = (int) ((w - gap) / (2 * radius + gap));
 }

5、接下来只需要重写onDraw()方法,简单的根据circleNum的数量将一个一个的圆绘制在屏幕上就可以了


@Override
 protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);
   Log.d("mDebug", "onDraw");
   for (int i = 0; i < circleNum; i++) {
     float x = gap + radius + remain / 2 + ((gap + radius * 2) * i);
     canvas.drawCircle(x, 0, radius, mPaint);
     canvas.drawCircle(x, getHeight(), radius, mPaint);
   }
 }

这里remain/2是因为避免有一些情况:当计算出来的圆的数量不是整除时,这样就会出现右边最后一个间距会比其它的间距都要宽,所以我们在绘制第一个的时候加上了余下的间距的一半,即使是不整除的情况,至少也能保证第一个和最后一个间距宽度一致。

总结

以上所述是小编给大家介绍的Android 自定义View之边缘凹凸的优惠券效果的开发过程网站的支持!

来源:http://blog.csdn.net/qijingwang/article/details/79622538

标签:android,view,优惠券
0
投稿

猜你喜欢

  • 详解spring boot rest例子

    2021-06-02 21:54:46
  • Java 8中的18个常用日期处理(收藏)

    2023-03-02 02:01:54
  • 详解Spring-boot中读取config配置文件的两种方式

    2021-07-04 15:52:55
  • Android Studio finish()方法的使用与解决app点击“返回”(直接退出)

    2022-07-31 09:37:10
  • Java实现双保险线程的示例代码

    2023-09-21 07:59:46
  • Java非静态成员变量之死循环(详解)

    2022-04-15 09:18:20
  • Java 通过API操作GraphQL

    2021-12-24 03:21:49
  • Java超详细分析泛型与通配符

    2023-07-28 08:34:26
  • c#集合快速排序类实现代码分享

    2023-03-30 13:38:51
  • Android8.1原生系统网络感叹号消除的方法

    2022-09-29 18:38:52
  • 详解C++ bitset用法

    2022-10-30 08:57:16
  • 关于工厂方法模式的Java实现

    2021-12-20 18:06:13
  • Android个人中心的头像上传,图片编码及截取实例

    2021-10-24 11:56:40
  • java并发编程包JUC线程同步CyclicBarrier语法示例

    2023-01-18 07:44:58
  • SpringBoot资源文件的存放位置设置方式

    2023-02-26 10:37:00
  • Java通过反射,如何动态修改注解的某个属性值

    2022-08-22 04:10:51
  • 浅谈Spring Boot 开发REST接口最佳实践

    2021-10-08 12:24:35
  • SpringBoot获取yml和properties配置文件的内容

    2022-12-02 18:26:08
  • JFreeChart插件实现的折线图效果实例

    2023-09-21 02:20:03
  • Winform实现将网页生成图片的方法

    2022-09-06 13:39:31
  • asp之家 软件编程 m.aspxhome.com