Android View(五)——自定义View

本文详细介绍了如何自定义Android View,包括继承View重写onDraw、继承ViewGroup实现特殊Layout、继承特定View和ViewGroup。讨论了自定义View须知,如支持wrap_content、padding、多线程处理和避免内存泄漏。通过实例展示了如何解决wrap_content和padding问题,以及如何添加自定义属性。最后,给出了一个实现水平滑动的自定义布局示例。
摘要由CSDN通过智能技术生成

一.前言

自定义View的实现需要我们对View的层次结构,事件分发机制和View的工作原理等知识有较好的掌握,具体的可参看如下的这些博客。

Android View(一)——View的基础知识
Android View(二)——View的事件分发机制
Android View(三)——View的滑动冲突
Android View(四)——View的工作原理
  

二.自定义View的分类

1.继承View重写onDraw方法

主要用来实现一些不规则的效果,这种效果不方便用布局组合的方式达到,通常需要静态或者动态的显示一些不规则的图形,这种需要绘制的需要自己支持wrap_content,并支持padding

2.继承ViewGroup派生出特殊的Layout

这种方式主要用于实现自定义布局,当某种效果看起来很像几种Veiw组合在一起的时候,可以采取这种方式来实现。这种方式复杂些,需要合适的处理ViewGroup的测量布局这两个过程,并同时处理子元素的测量和布局过程。

3.继承特定的View(比如TextView)

这种方法比较常见,一般用于扩展已有的View功能,比如TextView,这种方法比较容易实现,这种方法不需要自己支持wrap_content和padding。

4.继承特定的ViewGroup(比如LinearLayout)

这种方发也比较常见,当某种效果看起来很想几种View组合在一起的时候,可以采用这种方法实现,这种方法不需要自己处理测量和布局两个过程,一般来说方法2介意实现的效果方法4也可以实现,区别在于方法2更接近低层。

  
我们需要去找到一种代价最小,最高效的方法去实现。
  

三.自定义View须知

  1. 让View支持wrap_content(对于直接继承自View或者ViewGroup的控件,如果不在onMeasure中对wrap_content作特殊处理,那么wrap_content属性将失效)
  2. 支持padding和margin
    直接继承View的控件,如果不在draw方法中处理padding,padding属性是无法起作用的。直接继承自ViewGroup的控件需要在onMeasure和onLayout方法中考虑padding和子元素的margin对其造成的影响。
  3. 多线程应直接使用post方式
    View的内部本身提供了post系列的方法,完全可以替代Handler的作用,使用起来更加方便、直接。
  4. 避免内存泄漏
    主要针对View中含有线程或动画的情况:当View退出或不可见时,记得及时停止该View包含的线程和动画,否则会造成内存泄露问题。

启动或停止线程/ 动画的方式:
启动线程/动画:使用view.onAttachedToWindow(),因为该方法调用的时机是当包含View的Activity启动的时刻
停止线程/动画:使用view.onDetachedFromWindow(),因为该方法调用的时机是当包含View的Activity退出或当前View被remove的时刻

  1. 处理好滑动冲突

  

四.自定义View示例

1.继承View重写onDraw方法

自定义View实现圆的绘制,它会在自己的中心点以宽/高的最小值为直径绘制一个实心圆

public class CircleView extends View {
   
    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context) {
   
        super(context);
        init();
    }
    public CircleView(Context context, @Nullable AttributeSet attrs) {
   
        super(context, attrs);
        init();
    }

    public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
   
        super(context, attrs, defStyleAttr);
        init();
    }
    private void init() {
   
        int mColor = Color.RED;
        mPaint.setColor(mColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
   
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        int radius = Math.min(width,height) / 2;
        canvas.drawCircle(width / 2, height / 2,radius,mPaint ); //画圆
    }    
}

布局代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="#ffffff">
    <com.example.four_view_workingprincipletest.CircleView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#000000"
        android:id="@+id/circleView1"
        
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

运行效果图:
在这里插入图片描述

  • 问题1:此时,当我们把layout_width改为wrap_content,效果和上面完全一样。
    这是上面提到的让View支持wrap_content问题,具体的解决在Android View(四)——View的工作原理这里给出
  • 问题2:当在布局文件中加上android:padding="20dp"不起作用
    针对padding问题,需要我们在绘制时考虑一下padding,修改onDraw方法如下
@Override
    protected void onDraw(Canvas canvas) {
   
        super.onDraw(canvas);
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值