编程知识 cdmana.com

Android custom view Tour (2) inheriting view to realize customization

Inherit View It's the implementation of customization View Important ways , Through custom properties to achieve xml Easy to use in , By rewriting onMeasure and onDraw Method customization View Drawing process of , Accomplish specific actions by intercepting event responses , Make the idea a reality .

Last article Android Customize View The journey ( One ) Customize View In several ways

This article will talk about how to inherit through actual combat View Implement customization , Take a closer look at the requirements first , A sound wave control , Continuous animation effect , The simple implementation above is impossible , The desired effects are as follows :
 Insert picture description here

1、 Inherit View Implement customization View

In the last article , We give a brief introduction to “ Customize ” How to achieve , Inherited the native control of Android system , On this basis, we can complete the additional function style or change the original function style , Can quickly achieve the desired effect , The disadvantage is that it is restricted by the parent class 、 Unable to fulfill complex requirements .

For this need to achieve the sound waveform control , We need to create a new class VoiceLineView, Inherited from View

/**
 *  Custom sound vibration curve view
 */
public class VoiceLineView extends View {

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

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

    public VoiceLineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

}

2、 Provide some custom properties

Through custom properties to achieve xml Easy to use in , It's like we're using TextView When , Set up android:text="Hello World!".

First , Need to be in src/main/res/values Under folder attrs.xml file ( If this file does not exist, create a new one ) Add the following code in :

<?xml version="1.0" encoding="utf-8"?>
<resources>
	······
	<!--name To get properties, use , Cannot be repeated with custom properties of other controls -->
    <declare-styleable name="voiceView">
        <!-- The color of the middle line , It's the waveform time , You can see , There is a straight line in the middle , That's it. -->
        <attr name="middleLine" format="color" />
        <!-- The height of the middle line , The width of the middle line is full, no need to set -->
        <attr name="middleLineHeight" format="dimension" />
        <!-- The color of the undulating line -->
        <attr name="voiceLine" format="color" />
        <!-- The lateral velocity of the wave line , The inverse ratio of the speed of a line , That is, the smaller the value is , The faster the line moves horizontally , The bigger the line, the slower it moves , Default 90-->
        <attr name="lineSpeed" format="integer" />
        <!-- Maximum volume entered , The default is 100-->
        <attr name="maxVolume" format="float" />
        <!-- sensitivity , The default value is 4-->
        <attr name="sensibility">
            <enum name="one" value="1" />
            <enum name="two" value="2" />
            <enum name="three" value="3" />
            <enum name="four" value="4" />
            <enum name="five" value="5" />
        </attr>
        <!-- Fineness , When you draw a curve , Every few pixels , The default is 1, commonly , The smaller the value , The smoother the curve , But on some old phones , The frame rate will be too low , You can turn it up a little bit , Make a trade-off between smoothness and frame rate -->
        <attr name="fineness">
            <enum name="one" value="1" />
            <enum name="two" value="2" />
            <enum name="three" value="3" />
        </attr>
    </declare-styleable>
    ······
</resources>

Add a new one declare-styleable Tags declare a property set , According to need , stay declare-styleable Add more than one to the label attr Tags declare properties ,attr The tag is configured according to the settable properties of the control . About attr You can see the article type of user-defined Android in attr Type of attribute .

Later on VoiceLineView Class to modify the code as follows :

public class VoiceLineView extends View {

    private int middleLineColor = Color.BLACK;
    private int voiceLineColor = Color.BLACK;
    private float middleLineHeight = 4;
    private int sensibility = 4;
    private float maxVolume = 100;
    private int fineness = 1;
    private int lineSpeed = 90;

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

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

    public VoiceLineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAtts(context, attrs);
    }

    private void initAtts(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.voiceView);
        voiceLineColor = typedArray.getColor(R.styleable.voiceView_voiceLine, Color.BLACK);
        maxVolume = typedArray.getFloat(R.styleable.voiceView_maxVolume, 100);
        sensibility = typedArray.getInt(R.styleable.voiceView_sensibility, 4);
        middleLineColor = typedArray.getColor(R.styleable.voiceView_middleLine, Color.BLACK);
        middleLineHeight = typedArray.getDimension(R.styleable.voiceView_middleLineHeight, 4);
        lineSpeed = typedArray.getInt(R.styleable.voiceView_lineSpeed, 90);
        fineness = typedArray.getInt(R.styleable.voiceView_fineness, 1);
        typedArray.recycle();
    }

}

Use the following in the layout :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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="match_parent"
    android:orientation="vertical">

    <VoiceLineView
        android:id="@+id/voice_line"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        app:middleLine="@color/colorPrimary"
        app:middleLineHeight="1dp"
        app:voiceLine="@color/colorPrimary"
        app:lineSpeed="90"
        app:maxVolume="100"
        app:sensibility="four"
        app:fineness="one" />

</LinearLayout>

3、 rewrite onDraw Method , Keep drawing sound waves

Don't talk much , Go straight to the code :

public class VoiceLineView extends View {
	······

    private Paint paint;
    private Paint paintVoicLine;
    private float translateX = 0;
    private boolean isSet = false;
    private float amplitude = 1;
    private float volume = 10;
    private long lastTime = 0;
    private int lineSpeed = 90;
    List<Path> paths = null;

	······

    public void setVolume(int volume) {
        if (volume > maxVolume * sensibility / 25) {
            isSet = true;
            this.targetVolume = getHeight() * volume / 2 / maxVolume;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        drawMiddleLine(canvas);
        drawVoiceLine(canvas);
        run();
    }

    private void drawMiddleLine(Canvas canvas) {
        if (paint == null) {
            paint = new Paint();
            paint.setColor(middleLineColor);
            paint.setAntiAlias(true);
        }
        canvas.save();
        canvas.drawRect(0, getHeight() / 2 - middleLineHeight / 2, getWidth(), getHeight() / 2 + middleLineHeight / 2, paint);
        canvas.restore();
    }

    private void drawVoiceLine(Canvas canvas) {
        lineChange();
        if (paintVoicLine == null) {
            paintVoicLine = new Paint();
            paintVoicLine.setColor(voiceLineColor);
            paintVoicLine.setAntiAlias(true);
            paintVoicLine.setStyle(Paint.Style.STROKE);
            paintVoicLine.setStrokeWidth(2);
        }
        canvas.save();
        if (paths == null) {
            paths = new ArrayList<>(20);
            for (int i = 0; i < 20; i++) {
                paths.add(new Path());
            }
        }
        int moveY = getHeight() / 2;
        for (int i = 0; i < paths.size(); i++) {
            paths.get(i).reset();
            paths.get(i).moveTo(getWidth(), getHeight() / 2);
        }
        for (float i = getWidth() - 1; i >= 0; i -= fineness) {
            amplitude = 4 * volume * i / getWidth() - 4 * volume * i * i / getWidth() / getWidth();
            for (int n = 1; n <= paths.size(); n++) {
                float sin = amplitude * (float) Math.sin((i - Math.pow(1.22, n)) * Math.PI / 180 - translateX);
                paths.get(n - 1).lineTo(i, (2 * n * sin / paths.size() - 15 * sin / paths.size() + moveY));
            }
        }
        for (int n = 0; n < paths.size(); n++) {
            if (n == paths.size() - 1) {
                paintVoicLine.setAlpha(255);
            } else {
                paintVoicLine.setAlpha(n * 130 / paths.size());
            }
            if (paintVoicLine.getAlpha() > 0) {
                canvas.drawPath(paths.get(n), paintVoicLine);
            }
        }
        canvas.restore();
    }

    private void lineChange() {
        if (lastTime == 0) {
            lastTime = System.currentTimeMillis();
            translateX += 1.5;
        } else {
            if (System.currentTimeMillis() - lastTime > lineSpeed) {
                lastTime = System.currentTimeMillis();
                translateX += 1.5;
            } else {
                return;
            }
        }
        if (volume < targetVolume && isSet) {
            volume += getHeight() / 30;
        } else {
            isSet = false;
            if (volume <= 10) {
                volume = 10;
            } else {
                if (volume < getHeight() / 30) {
                    volume -= getHeight() / 60;
                } else {
                    volume -= getHeight() / 30;
                }
            }
        }
    }

    public void run() {
        invalidate();
    }

}

Here we are , The demand has been completed , When using, press 2 Point join layout , The code uses :

VoiceLineView voiceLine = findViewById(R.id.voice_line);

//  Set the volume cycle to draw a sonic map 
voiceLine.setVolume(volume);

版权声明
本文为[coder-ice]所创,转载请带上原文链接,感谢

Scroll to Top