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的开发技巧都能极大地提升应用的灵活性和用户体验。

相关推荐
张拭心11 分钟前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心22 分钟前
Android 17 来了!新特性介绍与适配建议
android·前端
Kapaseker3 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴3 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android