Android自定义View全解析:从基础绘制到复杂交互,实战多种自定义View实现

在Android开发中,自定义View的应用场景非常广泛。根据不同的需求,开发者可以创建各种类型的自定义View。以下是几种常见的自定义View类型及其实现思路和示例。
1. 自定义绘制View

自定义绘制View是最基础的自定义View类型,通常用于绘制图形、文本、图像等内容。开发者通过重写onDraw()方法,使用Canvas和Paint来实现绘制。

示例:自定义圆形View

复制代码
public class CircleView extends View {
    private Paint paint;

    public CircleView(Context context) {
        super(context);
        init();
    }

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

    private void init() {
        paint = new Paint();
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true); // 抗锯齿
    }

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

使用:

复制代码
<com.example.CircleView
    android:layout_width="100dp"
    android:layout_height="100dp"/>

2. 自定义组合View

自定义组合View是通过将多个现有的View组合在一起,形成一个新的复合View。这种方式通常用于创建复杂的UI组件。

示例:自定义标题栏

复制代码
public class CustomTitleBar extends RelativeLayout {
    private TextView titleText;
    private ImageView backButton;

    public CustomTitleBar(Context context) {
        super(context);
        init(context);
    }

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

    private void init(Context context) {
        LayoutInflater.from(context).inflate(R.layout.custom_title_bar, this, true);
        titleText = findViewById(R.id.titleText);
        backButton = findViewById(R.id.backButton);

        backButton.setOnClickListener(v -> {
            // 返回按钮点击事件
            if (context instanceof Activity) {
                ((Activity) context).finish();
            }
        });
    }

    public void setTitle(String title) {
        titleText.setText(title);
    }
}

布局文件:custom_title_bar.xml

复制代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@color/colorPrimary">

    <ImageView
        android:id="@+id/backButton"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_centerVertical="true"
        android:layout_marginStart="10dp"
        android:src="@drawable/ic_back" />

    <TextView
        android:id="@+id/titleText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textColor="@android:color/white"
        android:textSize="18sp" />
</RelativeLayout>

使用:

复制代码
<com.example.CustomTitleBar
    android:id="@+id/titleBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

3. 自定义动画View

自定义动画View通常用于实现复杂的动画效果。开发者可以通过ValueAnimator、ObjectAnimator或PropertyValuesHolder等工具来实现动画。

示例:自定义旋转进度条

复制代码
public class RotatingProgressBar extends View {
    private Paint paint;
    private float rotationAngle = 0;

    public RotatingProgressBar(Context context) {
        super(context);
        init();
    }

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

    private void init() {
        paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
        paint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        int radius = Math.min(centerX, centerY) - 10;

        canvas.save();
        canvas.rotate(rotationAngle, centerX, centerY);
        canvas.drawCircle(centerX, centerY, radius, paint);
        canvas.restore();
    }

    public void startRotation() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 360);
        animator.setDuration(1000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.addUpdateListener(animation -> {
            rotationAngle = (float) animation.getAnimatedValue();
            invalidate();
        });
        animator.start();
    }
}

使用:

复制代码
RotatingProgressBar progressBar = findViewById(R.id.rotatingProgressBar);
progressBar.startRotation();

4. 自定义触摸交互View

自定义触摸交互View通常用于处理用户的触摸事件,例如拖动、缩放、点击等。

示例:自定义可拖动View

复制代码
public class DraggableView extends View {
    private float lastX, lastY;

    public DraggableView(Context context) {
        super(context);
        init();
    }

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

    private void init() {
        setBackgroundColor(Color.GREEN);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = event.getX();
                lastY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = event.getX() - lastX;
                float dy = event.getY() - lastY;
                setX(getX() + dx);
                setY(getY() + dy);
                break;
        }
        return true;
    }
}

使用:

复制代码
<com.example.DraggableView
    android:layout_width="100dp"
    android:layout_height="100dp"/>

5. 自定义属性View

自定义属性View允许开发者在XML中定义和使用自定义属性,以增强View的灵活性。

示例:自定义带属性的TextView
在res/values/attrs.xml中定义属性

复制代码
<declare-styleable name="CustomTextView">
    <attr name="customTextColor" format="color" />
    <attr name="customTextSize" format="dimension" />
</declare-styleable>

在自定义View中解析属性:

复制代码
public class CustomTextView extends TextView {
    public CustomTextView(Context context) {
        super(context);
        init(null);
    }

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

    private void init(AttributeSet attrs) {
        if (attrs != null) {
            TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTextView);
            int textColor = a.getColor(R.styleable.CustomTextView_customTextColor, Color.BLACK);
            float textSize = a.getDimension(R.styleable.CustomTextView_customTextSize, 16);
            a.recycle();

            setTextColor(textColor);
            setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
        }
    }
}

在XML中使用:

复制代码
<com.example.CustomTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World"
    app:customTextColor="@color/red"
    app:customTextSize="20sp"/>

6. 自定义ViewGroup

自定义ViewGroup用于实现复杂的布局逻辑,例如瀑布流布局、环形布局等。

示例:自定义流式布局

复制代码
public class FlowLayout extends ViewGroup {
    public FlowLayout(Context context) {
        super(context);
    }

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

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        int width = r - l;
        int x = 0, y = 0;

        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (x + child.getMeasuredWidth() > width) {
                x = 0;
                y += child.getMeasuredHeight();
            }
            child.layout(x, y, x + child.getMeasuredWidth(), y + child.getMeasuredHeight());
            x += child.getMeasuredWidth();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

使用:

复制代码
<com.example.FlowLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <!-- 添加子View -->
</com.example.FlowLayout>

总结

自定义View的类型多种多样,开发者可以根据具体需求选择合适的实现方式。无论是简单的绘制、复杂的动画,还是自定义布局,掌握自定义View的开发技巧都能极大地提升应用的灵活性和用户体验。

相关推荐
xiangpanf11 分钟前
Laravel 10.x重磅升级:五大核心特性解析
android
robotx3 小时前
安卓线程相关
android
消失的旧时光-19433 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon4 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon5 小时前
VSYNC 信号完整流程2
android
dalancon5 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
用户69371750013846 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android6 小时前
Android 刷新一帧流程trace拆解
android
墨狂之逸才7 小时前
解决 Android/Gradle 编译报错:Comparison method violates its general contract!
android
阿明的小蝴蝶7 小时前
记一次Gradle环境的编译问题与解决
android·前端·gradle