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

本篇文章讲的是自定义View之边缘凹凸的优惠券效果,之前有见过很多优惠券的效果都是使用了边缘凹凸的样式。和往常一样,主要总结一下在自定义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之边缘凹凸的优惠券效果的开发过程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android自定义View 仿QQ侧滑菜单的实现代码

    先看看QQ的侧滑效果 分析一下 先上原理图(不知道能否表达的清楚 ==) -首先这里使用了 Android 的HorizontalScrollView 水平滑动布局作为容器,当然我们需要继承它自定义一个侧滑视图 - 这个容器里面有一个父布局(一般用LinerLayout,本demo用的是),这个父布局里面有且只有两个子控件(布局),初始状态菜单页的位置在Y轴上存在偏移这样可以就可以形成主页叠在菜单页的上方的视觉效果:然后在滑动的过程程中 逐渐修正偏移,最后菜单页和主页并排排列.原理搞清了实现起来

  • Android自定义View圆形和拖动圆、跟随手指拖动效果

    单纯的自定义一个圆非常简单 只需要几步就完成 拖动圆添加实现触摸事件即可 我在第一次自定义View圆遇到的几个Bug: 1.拖动圆的话在xml里面设置的自定义圆的宽和高是它能活动的空间的大小 不是圆控件的大小 如果你定义了100dp 拖动它的时候超过100dp这个距离这个圆就会看不见 就像下面这样 如果想活动于整个屏幕直接给宽和高match_parent属性就好了 2.我在定义充满属性match_parent的时候运行会报错,什么方法都用了就是不行,耐心等待过一会就好了-有可能是studio没来

  • Android 自定义TextView去除paddingTop和paddingBottom

    Android 自定义TextView去除paddingTop和paddingBottom 最近项目中需要用libgdx渲染一个Android的TextView, 但是绘制出来的TextView总是默认带有paddingTop和paddingBottom, 如下图所示: 网上有很多解决方案,例如在xml中设置如下属性: android:lineSpacingMultiplier="0.8" android:includeFontPadding="false" 或者设

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

    本篇文章讲的是自定义View之边缘凹凸的优惠券效果,之前有见过很多优惠券的效果都是使用了边缘凹凸的样式.和往常一样,主要总结一下在自定义View的开发过程中需要注意的一些地方. 按照惯例,我们先来看看效果图 一.写代码之前,我们先弄清楚view的启动过程: 之所以想要弄清楚这个问题是因为代码里面用到了onSizeChanged()方法,一开始我有点犹豫onSizeChanged是在什么时候启动的呢,所以看看View的启动流程吧 package per.lijuan.coupondisplayvi

  • Android自定义View实现loading动画加载效果

    项目开发中对Loading的处理是比较常见的,安卓系统提供的不太美观,引入第三发又太麻烦,这时候自己定义View来实现这个效果,并且进行封装抽取给项目提供统一的loading样式是最好的解决方式了. 先自定义一个View,继承自LinearLayout,在Layout中,添加布局控件 /** * Created by xiedong on 2017/3/7. */ public class Loading_view extends LinearLayout { private Context m

  • Android自定义View实现简单的圆形Progress效果

    先给大家展示下效果图,如果感觉不错,请参考实现思路: 我们要实现一个自定义的再一个圆形中绘制一个弧形的自定义View,思路是这样的: 先要创建一个类ProgressView,继承自View类,然后重写其中的两个构造方法,一个是一个参数的,一个是两个参数的,因为我们要在xml文件中使用该自定义控件,所以必须要定义这个两个参数的构造函数.创建完了这个类后,我们先不去管它,先考虑我们实现的这个自定义View,我们想让它的哪些部分可以由使用者自己指定,比如说这个Demo中我们让他的外面圆的外边框颜色和宽

  • Android 自定义view实现进度条加载效果实例代码

    这个其实很简单,思路是这样的,就是拿view的宽度,除以点的点的宽度+二个点 之间的间距,就可以算出大概能画出几个点出来,然后就通过canvas画出点,再然后就是每隔多少时间把上面移动的点不断的去改变它的坐标就可以, 效果如下: 分析图: 代码如下: package com.example.dotloadview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bit

  • Android 自定义View实现任意布局的RadioGroup效果

    前言 RadioGroup是继承LinearLayout,只支持横向或者竖向两种布局.所以在某些情况,比如多行多列布局,RadioGroup就并不适用 . 本篇文章通过继承RelativeLayout实现自定义RadioGroup,实现RadioButton的任意布局.效果图如下: 代码(RelativeRadioGroup) /** * Author : BlackHao * Time : 2018/10/26 10:46 * Description : 自定义 RadioGroup */ p

  • Android自定义view实现仿抖音点赞效果

    前言 学习自定义view,想找点东西耍一下,刚好看到抖音的点赞效果不错,尝试一下. 抖音效果: 话不多说,先上代码: public class Love extends RelativeLayout { private Context mContext; float[] num = {-30, -20, 0, 20, 30};//随机心形图片角度 public Love(Context context) { super(context); initView(context); } public

  • Android自定义View实现抖音飘动红心效果

    本文实例为大家分享了Android自定义View实现抖音飘动红心效果的具体代码,供大家参考,具体内容如下 自定义View--抖音飘动红心 效果展示 动画效果 使用自定义view完成红心飘动效果 View实现 动画:属性动画(位移+缩放+透明度+旋转) + 随机数:(属性动画参数+颜色选取) View /** * 飘心效果 * 1.创建ImageView * 2.ImageView执行组合动画 * 3.动画执行完成后销毁View */ public class FlyHeartView exten

  • Android自定义View实现APP启动页倒计时效果

    Android自定义View实现APP启动页倒计时效果,供大家参考,具体内容如下 之前也是做过APP启动页的倒计时效果,但是只有文字变化,没有动画效果,这次通过使用自定义View控件来制作一个带有动画效果的倒计时. 倒计时效果的基本思路如下: Canvas提供了绘制弧形的方法,drawArc(),使用这个方法通过定时刷新计算当前弧形的角度,就可以模拟出倒计时的动画效果,同时借助drawText()方法可以实现倒计时文字.(1)继承View(2)使用canvas的drawArc()来绘制弧形,模拟

  • Android自定义View模仿即刻点赞数字切换效果实例

    目录 即刻点赞展示 自己如何实现这种数字切换呢? 效果展示 源码 总结 即刻点赞展示 点赞的数字增加和减少并不是整个替换,而是差异化替换.再加上动画效果就看的很舒服. 自己如何实现这种数字切换呢? 下面用一张图来展示我的思路: 现在只需要根据这张图,写出对应的动画即可. 分为2种场景: 数字+1: 差异化的数字从3号区域由渐变动画(透明度 0- 255) + 偏移动画 (3号区域绘制文字的基线,2号区域绘制文字的基线),将数字移动到2号位置处 差异化的数字从2号区域由渐变动画(透明度 255-

  • Android自定义view仿iOS弹出框效果

    本文实例为大家分享了Android自定义view仿iOS弹出框的具体代码,供大家参考,具体内容如下 运行效果图 自定义对话框的使用,仿照ios.从底部弹出,类似pop窗口.包括消息.图片.列表及对话框. 好了,用法都会,直接贴上代码 1.layout布局文件 view_actionsheet.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="ht

随机推荐