关闭

Android:绘制自定义视图

146人阅读 评论(0) 收藏 举报
分类:

介绍

每天我们使用各种应用程序,尽管他们的不同意图,大多数是非常相似,甚至相似的设计。这就是为什么很多客户要求特定的,自定义的布局和外观,没有其他应用程序还没有体现,以使Android应用程序的独特性和与其他人的对比。
如果一个特定的功能需要一个非常定制的功能,不能创建android内置视图? - ?然后来自定义视图绘制。在大多数情况下,这意味着需要很长时间才能完成它。但它的确意味着我们不应该这样做,此外,它是非常令人兴奋和有趣的实施。
我最近遇到了类似的情况:我的任务是为Android ViewPager创建一个页面指示器。不像iOS,Android不提供这样的视图,所以我不得不实现它作为一个自定义。
我花了相当多的时间试图实现它。幸运的是,视图在现在的项目中是可重复使用的,所以为了节省个人时间和其他开发人员的时间,我决定根据该视图创建一个公共库。如果你有类似的功能,没有时间自己实现它,然后在github repo找到它。


使用PageIndicatorView的示例


画!

好吧,由于大多数自定义视图比常规视图更耗时,您应该只有在没有更简单的方法来实现特定功能或您有以下问题,自定义视图可以解决:
性能。如果在布局中有很多视图,并且想要通过绘制单个自定义视图以使其更轻,进行优化。
大视图层次结构,操作和支持复杂。
需要手动绘制的完整自定义视图。
如果你没有尝试制定一个自定义视图,那么这篇文章是一个很好的机会,以更接近绘制自己的平面自定义视图。它将显示整体视图结构,如何实现具体的事情,如何避免常见的错误,甚至如何动画你的观点!
我们需要做的第一件事是跳转到View生命周期。由于某种原因,Google没有提供视图生命周期的官方图表,因此开发人员之间相当普遍的误解,导致意想不到的错误和问题,因此我们关注它!


构造函数

每个视图开始它的生命从构造函数。它给了我们,是一个很好的机会来准备一个初始绘图,进行各种计算,设置默认值或任何我们需要的。
但是为了使我们的视图易于使用和设置,有一些有用的AttributeSet接口。它很容易实现,绝对值得花时间,因为它将帮助您(和您的团队)在更多的屏幕上使用一些静态参数设置您的视图。
首先,创建一个新文件并将其命名为attrs.xml。在该文件中可以是不同自定义视图的所有属性。正如你可以看到在这个例子中,我们有一个视图称为PageIndicatorView和单一属性piv_count。


自定义属性示例
在你的View构造函数中,你需要获取属性并使用它,如下所示。
public PageIndicatorView(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.PageIndicatorView);
    int count = typedArray.getInt(R.styleable.PageIndicatorView_piv_count,0);
    typedArray.recycle();
}

自定义视图检索属性


注意:
创建自定义属性时会创建一个简单的前缀,以避免具有类似属性名称的其他视图之间的名称冲突。主要是视图名称的缩写,就像我们有piv_。

如果你使用Android Studio,Lint会建议你调用recycle()方法,只要你完成你的属性。原因是为了摆脱无效的绑定数据,不会再次使用。


onAttachedToWindow

父视图调用addView(View)后,该视图将附加到一个窗口。在这个阶段,我们的观点将知道其它观点被它包围。如果你的视图使用位于同一layout.xml中的用户的其他视图,它是通过id(你可以通过属性设置)和保存为全局引用(如果需要)找到它们的好地方。

onMeasure

意味着我们的自定义视图是在舞台上,以找出它自己的大小。这是非常重要的方法,因为在大多数情况下,您将需要您的视图具有适合您的布局的特定大小。
在覆盖此方法时,您需要执行此操作是设置setMeasuredDimension(int width,int height)。


在设置自定义视图的大小时,您应该处理大小写,该视图可以具有特定大小,用户将在layout.xml中或以编程方式设置。要正确计算,需要完成几个步骤。
计算您的视图内容所需的大小(宽度和高度)。
获取您的视图MeasureSpec(宽度和高度)的大小和模式。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    }
自定义视图MeasureSpec
3.检查MeasureSpec模式,用户设置和调整视图的大小(宽度和高度)。
int width;
if (widthMode == MeasureSpec.EXACTLY) {
  width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
  width = Math.min(desiredWidth, widthSize);
} else {
  width = desiredWidth;

}

自定义视图onMeasure - 获取正确的大小


注意:
看看MeasureSpec值:
MeasureSpec.EXACTLY表示用户硬编码大小的值,所以
 不管你的视图大小,你应该设置指定的宽度或高度。
MeasureSpec.AT_MOST用于使您的视图大到它想要达到指定的大小。
MeasureSpec.UNSPECIFIED实际上是一个视图的包裹大小。所以用
 此参数可以使用上面计算的所需大小。

在将最终值设置为setMeasuredDimension之前,请检查这些值是否为负值。这将避免布局预览中的问题。


onLayout

这种方法包括给每个孩子分配大小和位置。因此,我们正在寻找一个平面自定义视图(扩展一个简单的视图),没有任何孩子,所以没有理由重写此方法。


onDraw

这就是魔法发生的地方。拥有Canvas和Paint对象将允许你绘制任何你需要的东西。
Canvas实例来自onDraw参数,它基本上响应绘制不同的形状,而Paint对象定义了形状将获得的颜色。简单地说,Canvas响应绘制一个对象,而Paint则是对它进行造型。它几乎在任何地方使用,无论它将是一个线,圆或矩形。




onDraw()方法示例
在做自定义视图时,始终记住onDraw调用大量的时间,像真的很多。虽然有一些更改,滚动,刷卡将重绘。所以这就是为什么即使Android Studio建议在onDraw操作期间避免对象分配,而是创建一次并进一步重用。




onDraw() -绘制对象重新创建




onDraw() -绘制对象重用
注意:
在执行绘制时,始终记住重用对象,而不是创建新对象。不要依赖你的IDE来突出一个潜在的问题,但自己做,因为如果你在onDraw调用的方法中创建对象,IDE看不到它。

不要在绘制时硬编码视图大小。处理其他开发人员可能具有相同视图但大小不同的情况,因此绘制视图取决于它有什么大小。


查看更新

从视图生命周期图中,您可能会注意到有两种方法导致视图重绘本身。 invalidate()和requestLayout()方法将帮助您创建交互式自定义视图,这可能会改变其在运行时的外观。但为什么有两个?
invalidate()方法用于简单重绘视图。虽然您的视图例如更新其文本,颜色或触摸交互性。这意味着视图将仅再次调用onDraw()方法来更新其状态。
requestLayout()方法,你可以看到将产生视图更新通过其生命周期只是从onMeasure()方法。这意味着你需要它,而在视图更新后,它改变了它的大小,你需要再次测量它来绘制它取决于新的大小。

动画

自定义视图中的动画是逐帧过程。这意味着,如果你想让一个圆半径动画从小到大,你需要逐个增加它,并在每一步之后调用invalidate()来绘制它。
你最好的朋友在自定义视图动画是ValueAnimator。这个类将帮助你动画任何值从开始到结束甚至Interpolator支持(如果你需要)。

ValueAnimator animator = ValueAnimator.ofInt(0, 100);
animator.setDuration(1000);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  public void onAnimationUpdate(ValueAnimator animation) {
    int newRadius = (int) animation.getAnimatedValue();
  }
});
animator.start();

自定义视图ValueAnimator样本


注意:
不要忘记调用invalidate()每次新的动画值出来。
通过ValueAnimator的动画示例


1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1825988次
    • 积分:33465
    • 等级:
    • 排名:第142名
    • 原创:1330篇
    • 转载:590篇
    • 译文:83篇
    • 评论:269条
    博客专栏
    android

    文章:117篇

    阅读:37687
    文章分类