Android自定义View基本使用

https://www.jianshu.com/p/705a6cb6bfee
https://zhuanlan.zhihu.com/p/162945366

自定义view步骤:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性:引入 xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"我们的命名空间
3、重写onMesure
4、重写onDraw

https://blog.csdn.net/guolin_blog/article/details/16330267
自定义控件的实现有三种方式
(一)组合控件
组合控件,顾名思义就是将一些小的控件组合起来形成一个新的控件,这些小的控件多是系统自带的控件。
(二)自绘控件
自绘控件的内容都是自己绘制出来的,在View的onDraw方法中完成绘制。
(三)继承控件
就是继承已有的控件,创建新控件,保留继承的父控件的特性,并且还可以引入新特性。下面就以支持横向滑动删除列表项的自定义ListView的实现来介绍。

实例:关于自定义录音声音分贝波纹
1.自定义View的属性 :vaules/attrs.xml,在自定义View的初始化使用此值

<declare-styleable name="LineWaveVoiceView">
        <attr name="voiceLineColor" format="color" />
        <attr name="voiceLineWidth" format="color" />
        <attr name="voiceTextSize" format="dimension" />
        <attr name="voiceTextColor" format="dimension" />
        <attr name="updateSpeed" format="color" />
 </declare-styleable>

2.自定义LineWaveVoiceView集成自View,重写onDraw方法

package com.example.demo.view;

/**
 * 语音录制的动画效果
 */
public class LineWaveVoiceView extends View {
    private static final String DEFAULT_TEXT = " 请录音 ";
    private static final int LINE_WIDTH = 9;//默认矩形波纹的宽度,9像素, 原则上从layout的attr获得
    private Paint paint = new Paint();
    private Runnable task;
    private ExecutorService executorService = Executors.newCachedThreadPool();
    private RectF rectRight = new RectF();//右边波纹矩形的数据,10个矩形复用一个rectF
    private RectF rectLeft = new RectF();//左边波纹矩形的数据
    private String text = DEFAULT_TEXT;
    private int updateSpeed;
    private int lineColor;
    private int textColor;
    private float lineWidth;
    private float textSize;


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

    public LineWaveVoiceView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LineWaveVoiceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(attrs, context);
        resetView(mWaveList, DEFAULT_WAVE_HEIGHT);
        task = new LineJitterTask();
    }

    private void initView(AttributeSet attrs, Context context) {
        //获取布局属性里的值
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.LineWaveVoiceView);
        lineColor = mTypedArray.getColor(R.styleable.LineWaveVoiceView_voiceLineColor, context.getColor(R.color.color_accent));
        lineWidth = mTypedArray.getDimension(R.styleable.LineWaveVoiceView_voiceLineWidth, LINE_WIDTH);
        textSize = mTypedArray.getDimension(R.styleable.LineWaveVoiceView_voiceTextSize, 42);
        textColor = mTypedArray.getColor(R.styleable.LineWaveVoiceView_voiceTextColor, context.getColor(R.color.color_accent));
        updateSpeed = mTypedArray.getColor(R.styleable.LineWaveVoiceView_updateSpeed, UPDATE_INTERVAL_TIME);
        mTypedArray.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //获取实际宽高的一半
        int widthCentre = getWidth() / 2;
        int heightCentre = getHeight() / 2;
        paint.setStrokeWidth(0);
        paint.setColor(textColor);
        paint.setTextSize(textSize);
        float textWidth = paint.measureText(text);
        canvas.drawText(text, widthCentre - textWidth / 2, heightCentre - (paint.ascent() + paint.descent()) / 2, paint);

        //设置颜色
        paint.setColor(lineColor);
        //填充内部
        paint.setStyle(Paint.Style.FILL);
        //设置抗锯齿
        paint.setAntiAlias(true);
        for (int i = 0; i < 10; i++) {
            rectRight.left = widthCentre + textWidth / 2 + (1 + 2 * i) * lineWidth;
            rectRight.top = heightCentre - lineWidth * mWaveList.get(i) / 2;
            rectRight.right = widthCentre + textWidth / 2 + (2 + 2 * i) * lineWidth;
            rectRight.bottom = heightCentre + lineWidth * mWaveList.get(i) / 2;

            //左边矩形
            rectLeft.left = widthCentre - textWidth / 2 - (2 + 2 * i) * lineWidth;
            rectLeft.top = heightCentre - mWaveList.get(i) * lineWidth / 2;
            rectLeft.right = widthCentre - textWidth / 2 - (1 + 2 * i) * lineWidth;
            rectLeft.bottom = heightCentre + mWaveList.get(i) * lineWidth / 2;

            canvas.drawRoundRect(rectRight, 6, 6, paint);
            canvas.drawRoundRect(rectLeft, 6, 6, paint);
        }
    }

    private static final int MIN_WAVE_HEIGHT = 2;//矩形线最小高
    private static final int MAX_WAVE_HEIGHT = 12;//矩形线最大高
    private static final int[] DEFAULT_WAVE_HEIGHT = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
    private static final int UPDATE_INTERVAL_TIME = 100;//100ms更新一次
    private LinkedList<Integer> mWaveList = new LinkedList<>();
    private float maxDb;

    private void resetView(List<Integer> list, int[] array) {
        list.clear();
        for (int anArray : array) {
            list.add(anArray);
        }
    }

    private synchronized void refreshElement() {
        Random random = new Random();
        maxDb = random.nextInt(5) + 2;
        int waveH = MIN_WAVE_HEIGHT + Math.round(maxDb * (MAX_WAVE_HEIGHT - MIN_WAVE_HEIGHT));
        mWaveList.add(0, waveH);
        mWaveList.removeLast();
    }

    public boolean isStart = false;

    private class LineJitterTask implements Runnable {
        @Override
        public void run() {
            while (isStart) {
                refreshElement();
                try {
                    Thread.sleep(updateSpeed);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                postInvalidate();
            }
        }
    }

    public synchronized void startRecord() {
        isStart = true;
        executorService.execute(task);
    }

    public synchronized void stopRecord() {
        isStart = false;
        mWaveList.clear();
        resetView(mWaveList, DEFAULT_WAVE_HEIGHT);
        postInvalidate();
    }


    public synchronized void setText(String text) {
        this.text = text;
        postInvalidate();
    }

    public void setUpdateSpeed(int updateSpeed) {
        this.updateSpeed = updateSpeed;
    }
}

3.在layout中引入该自定义View的控件:该控件名为自定义View类路径

<com.example.demo.view.LineWaveVoiceView
      android:layout_width="300dp"
      android:layout_height="100dp"
      android:layout_gravity="center"
      android:layout_margin="8dp" />

若要引用自定义View的属性,需要根布局添加命名空间

xmlns:custom_android="http://schemas.android.com/apk/res-auto"

然后才可以添加自定义控件的属性:custom_android:voiceLineColor=“@color/color_accent”

<com.example.demo.view.LineWaveVoiceView
      android:layout_width="300dp"
      android:layout_height="100dp"
      android:layout_gravity="center"
      android:layout_margin="8dp"
      custom_android:voiceLineColor="@color/color_accent" />

附加

  1. 如果我们自定义属性,这个属性应该去我们的应用程序包中找,所以要引入我们应用包的命名空间xmlns:openxu="http://schemas.android.com/apk/res-auto”,res-auto表示自动查找

  2. 属性值的类型format

format支持的类型一共有11种:

(1). reference:参考某一资源ID
属性定义:
<declare-styleable name = "名称">
 <attr name = "background" format = "reference" />
</declare-styleable>
属性使用:
<ImageView android:background = "@drawable/图片ID"/>

(2). color:颜色值
属性定义:
<attr name = "textColor" format = "color" />
属性使用:
<TextView android:textColor = "#00FF00" />

(3). boolean:布尔值
属性定义:
<attr name = "focusable" format = "boolean" />
属性使用:
<Button android:focusable = "true"/>

(4). dimension:尺寸值**
属性定义:
<attr name = "layout_width" format = "dimension" />
属性使用:
<Button android:layout_width = "42dip"/>

(5). float:浮点值
属性定义:
<attr name = "fromAlpha" format = "float" />
属性使用:
<alpha android:fromAlpha = "1.0"/>

(6). integer:整型值**
属性定义:
<attr name = "framesCount" format="integer" />
属性使用:
<animated-rotate android:framesCount = "12"/>

(7). string:字符串
属性定义:
<attr name = "text" format = "string" />
属性使用:
<TextView android:text = "我是文本"/>

(8). fraction:百分数**
属性定义:
<attr name = "pivotX" format = "fraction" />
属性使用:
<rotate android:pivotX = "200%"/>

(9). enum:枚举值
属性定义:
<declare-styleable name="名称"> 
        <attr name="orientation"> 
                    <enum name="horizontal" value="0" />
                   <enum name="vertical" value="1" />
       </attr>
</declare-styleable>
属性使用:
<LinearLayout android:orientation = "vertical"></LinearLayout>
注意:枚举类型的属性在使用的过程中只能同时使用其中一个,不能 android:orientation = “horizontal|vertical"

(10). flag:位或运算
属性定义:
<declare-styleable name="名称"> 
      <attr name="gravity"> 
                <flag name="top" value="0x30" /> 
                <flag name="bottom" value="0x50" />
                <flag name="left" value="0x03" /> 
                <flag name="right" value="0x05" /> 
                <flag name="center_vertical" value="0x10" />
      </attr>
</declare-styleable>
属性使用:
<TextView android:gravity="bottom|left"/>
注意:位运算类型的属性在使用的过程中可以使用多个值

(11). 混合类型:属性定义时可以指定多种类型值
属性定义:
<declare-styleable name = "名称"> 
          <attr name = "background" format = "reference|color" />
</declare-styleable>
属性使用:
<ImageViewandroid:background = "@drawable/图片ID" />
或者:
<ImageViewandroid:background = "#00FF00" />

我们写好布局文件之后,要在控件中使用这些属性还需要将它解析出来。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值