Android自定义view (UI组件)和三个绘图工具类详解

1. android自定义view UI组件)

1.1 自定义view的简介

1.1.1 为什么要自定义view                                         

Android 开发中有很多业务场景,原生的控件是无法满足应用,并且经常也会遇到一个 UI 在多处
重复使用情况,那么就需要通过自定义 View 的方式来实现这些 UI 效果。
作为一个 Android 开发工程师自定义 View 属于一个必备技能。

1.1.2 android自定义view几种方式

自定义 View 的实现方式有以下几种: 组合控件,继承控件,自绘控件
详细可分为: 自定义组合控件,继承系统 View 控件,继承系统 ViewGroup ,自绘 View 控件,自绘
ViewGroup 控件

 1.2 自定义组合控件

组合控件就是将多个控件组合成一个新的控件,可以重复使用。
应用场景:在项目中经常会遇到一些比较复杂的 UI 块需要用在 多处使用 ,那么我们就可以通过五大布局 和基本控件组合成一个新的布局View ,这样就可以方便的将该 UI 用在项目的不同页面中,比如一个标题 栏。这种方式比较简单,只要通过布局文件实现相应的UI ,然后将该 UI 加到适合的五大布局中即可。

 1.2.1 自定义组合控件的使用步骤

1. 编写布局文件
2. 实现构造方法
3. 初始化 UI
4. 提供对外的方法
5. 在布局当中引用该控件
6. activity 中使用

 

1.2.2 示例

中间是 title 的文字,左边是返回按钮
1. 编写布局文件 view_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:background="#8E8D8D"
    android:id="@+id/rl">

    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginStart="8dp"
        android:visibility="visible"
        app:srcCompat="@drawable/ic_baseline_arrow_back_24" />

    <TextView
        android:layout_centerInParent="true"
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:textColor="@color/black"
        android:text="聊天室" />
</RelativeLayout>
2. 实现构造方法
3. 初始化 UI
4. 提供对外的方法
package com.hp.demo;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

//因为我们的布局采用RelativeLayout,所以这里继承RelativeLayout。
public class HeadView extends RelativeLayout {
    private RelativeLayout rl;
    private ImageView iv;
    private TextView tv;

    public HeadView(Context context) {
        super(context);
    }

    public HeadView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }
    //初始化UI,可根据业务需求设置默认值。
    private void initView(Context context) {
        LayoutInflater.from(context).inflate(R.layout.head_view,this,true);
    }
    public HeadView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public HeadView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}
5. 在布局当中引用该控件
<?xml version="1.0" encoding="utf-8"?>
<com.hp.demo.ForegroundLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.hp.demo.HeadView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</com.hp.demo.ForegroundLinearLayout>
6. activity 中使用
package com.hp.demo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

1.3 继承系统控件

通过继承系统控件(View子类控件或ViewGroup子类控件)来完成自定义View,一般是希望在原 有系统控件基础上做一些修饰性的修改,而不会做大幅度的改动,如在TextView的文字下方添加下 划线,在LinearLayout布局中加一个蒙板等。这种方式往往都会复用系统控件的onMeasure和 onLayout方法,而只需要重写onDraw方法,在其中绘制一些需要的内容。

1.3.1 继承View类系统控件

1.3.1.1 继承 View 类系统控件使用步骤
        1. 继承 View 控件,并重写 onDraw 方法
        2. 在布局文件中调用
示例:
1. 继承 View 控件,并重写 onDraw 方法
package com.hp.demo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class UnderlineTextView extends androidx.appcompat.widget.AppCompatTextView {
    public UnderlineTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint=new Paint();
        paint.setColor(Color.BLUE);
        paint.setStrokeWidth(5);
        int width=getWidth();
        int height=getBaseline();
        canvas.drawLine(0,height,width,height,paint);
    }
}
2. 在布局文件中调用
        就像使用一个普通TextView 一样使用 UnderlineTextView
<?xml version="1.0" encoding="utf-8"?>
<com.hp.demo.ForegroundLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.hp.demo.UnderlineTextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:text="继承TextView"
        android:textSize="24sp"/>

</com.hp.demo.ForegroundLinearLayout>

1.3.2 继承ViewGroup类系统控件

1.3.2.1 继承ViewGroup类系统控件使用步骤

        1. 继承 ViewGroup 类系统控件
        2. 在布局文件中调用
示例:
1. 继承 ViewGroup 类系统控件
package com.hp.demo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;

public class ForegroundLinearLayout extends LinearLayout {
    public ForegroundLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        canvas.drawColor(Color.parseColor("#70E1DFDF"));
    }
}
2. 在布局文件中调用
        对ForegroundLinearLayout 的使用,就和使用其父类 LinearLayout 一样。
<?xml version="1.0" encoding="utf-8"?>
<com.hp.demo.ForegroundLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.hp.demo.HeadView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <com.hp.demo.UnderlineTextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:text="继承TextView"
        android:textSize="24sp"/>

</com.hp.demo.ForegroundLinearLayout>

1.3.3 继承系统控件小结

从上面两个例子可见,继承系统原有的控件来实现自定义 View ,步骤非常简单,比组合控件简单多了。
但是这一节需要对 Canvas paint Path 等绘制方面的知识有一定的了解,且还需要对 ViewGroup 的中
内容的绘制顺序有一定的了解,才能在原生控件的基础上做出想要的效果来。

 

2.三个绘图工具类详解

绘图相关的 一些 API ,他们分别是 Canvas( 画布 ) , Paint( 画笔 ) , Path( 路径 ) ,同时也是自定义View 的基础。

2.1 相关方法详解

 2.1.1 Paint(画笔):

2.1.2 Canvas(画布):  

 2.1.3 Path(路径)

 

2.2 示例

第一步:

package com.hp.demo;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

import java.util.Random;

public class MyView extends View {
    private Paint paint;

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

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

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }
    private void init(){
        paint=new Paint();
        paint.setAntiAlias(true);//抗锯齿
        paint.setColor(getResources().getColor(R.color.red));//画笔颜色
        paint.setStyle(Paint.Style.FILL);//画笔风格  FILL表示实心   STROKE表示空心
        paint.setTextSize(36);//绘制文字大小,单位px
        paint.setStrokeWidth(5);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(getResources().getColor(R.color.white)); //设置画布背景颜色

        canvas.drawCircle(getWidth()/2, getHeight()/2, 100, paint); //画实心圆

        //canvas.drawRect(100, 50, 300, 150, paint); //画矩形

        //绘制Bitmap
        canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.zuan),700,500,paint);//放图片

        Paint paint1=new Paint();
        paint1.setColor(getResources().getColor(R.color.black));
        paint1.setTextSize(36);//绘制文字大小,单位px
        paint1.setTextAlign(Paint.Align.CENTER);
        float baseLineY=getHeight()/2+Math.abs(paint.ascent()+paint.descent())/2;
        canvas.drawText("小日子岛",getWidth()/2,baseLineY,paint1); //绘制文字

        //canvas.drawRoundRect(new RectF(60,200,260,310),50,50,paint); //画圆角矩形

        //绘制多边形
        Path path=new Path();
//        path.moveTo(10,10);
//        path.lineTo(100,50);
//        path.lineTo(200,40);
//        path.lineTo(300,20);
//        path.lineTo(200,10);
//        path.lineTo(100,70);
//        path.lineTo(50,40);
//        path.close();
//        canvas.drawPath(path,paint);

        //绘制文字
        path.moveTo(0,100);
        path.lineTo(100, 0);
        path.lineTo(200, 100);
        path.lineTo(300, 200);
        path.lineTo(400, 100);
        path.close();
        canvas.drawTextOnPath("ABCDEFGHIJKLMNOPQRSTUVWXYZ", path, 25, 25, paint);
    }
}

第二步: 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#838181"
    tools:context=".MainActivity2">

    <com.hp.demo.MyView
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值