先说明一下本自定义view的主要内容:
1.本效果的实现就是在光圈内六边形六个角上分别绘制六个光圈叶片
2.根据不同的光圈值计算出内六边形的大小,从而计算每个六边形的顶点的位置
3.设计叶片。也可以让美工MM提供,本方案是自己用代码画的。注意预留叶片之间的间隔距离以及每个叶片的角度为60°
4.定义颜色、间隔等自定义属性
5.上下滑动可以调节光圈大小
6.提供光圈值变动的监听接口
代码
可以在GitHub上下载:https://github.com/willhua/CameraAperture.git
package com.example.cameraaperture;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PointF;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;/** * 上下滑动可以调节光圈大小; * 调用setApertureChangedListener设置光圈值变动监听接口; * 绘制的光圈最大直径将填满整个view * @author willhua http://www.cnblogs.com/willhua/ **/public class ApertureView extends View {public interface ApertureChanged {public void onApertureChanged(float newapert);}private static final float ROTATE_ANGLE = 30;private static final String TAG = "ApertureView";private static final float COS_30 = 0.866025f;private static final int WIDTH = 100; // 当设置为wrap_content时测量大小private static final int HEIGHT = 100;private int mCircleRadius;private int mBladeColor;private int mBackgroundColor;private int mSpace;private float mMaxApert = 1;private float mMinApert = 0.2f;private float mCurrentApert = 0.5f;//利用PointF而不是Point可以减少计算误差,以免叶片之间间隔由于计算误差而不均衡private PointF[] mPoints = new PointF[6]; private Bitmap mBlade;private Paint mPaint;private Path mPath;private ApertureChanged mApertureChanged;private float mPrevX;private float mPrevY;public ApertureView(Context context, AttributeSet attrs) {super(context, attrs);init(context, attrs);}private void init(Context context, AttributeSet attrs) {//读取自定义布局属性TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ApertureView);mSpace = (int)array.getDimension(R.styleable.ApertureView_blade_space, 5);mBladeColor = array.getColor(R.styleable.ApertureView_blade_color, 0xFF000000);mBackgroundColor = array.getColor(R.styleable.ApertureView_background_color, 0xFFFFFFFF);array.recycle();mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);mPaint.setAntiAlias(true);for (int i = 0; i < 6; i++) {mPoints[i] = new PointF();}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);int paddX = getPaddingLeft() + getPaddingRight();int paddY = getPaddingTop() + getPaddingBottom();//光圈的大小要考虑减去view的padding值mCircleRadius = widthSpecSize - paddX < heightSpecSize - paddY ? (widthSpecSize - paddX) / 2: (heightSpecSize - paddY) / 2;//对布局参数为wrap_content时的处理if (widthSpecMode == MeasureSpec.AT_MOST&& heightSpecMode == MeasureSpec.AT_MOST) {setMeasuredDimension(WIDTH, HEIGHT);mCircleRadius = (WIDTH - paddX) / 2;} else if (widthSpecMode == MeasureSpec.AT_MOST) {setMeasuredDimension(WIDTH, heightSpecSize);mCircleRadius = WIDTH - paddX < heightSpecSize - paddY ? (WIDTH - paddX) / 2: (heightSpecSize - paddY) / 2;} else if (heightSpecMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSpecSize, HEIGHT);mCircleRadius = widthSpecSize - paddX < HEIGHT - paddY ? (widthSpecSize - paddX) / 2: (HEIGHT - paddY) / 2;}if (mCircleRadius < 1) {mCircleRadius = 1;}//measure之后才能知道所需要绘制的光圈大小mPath = new Path();mPath.addCircle(0, 0, mCircleRadius, Path.Direction.CW);createBlade();}@Overridepublic void onDraw(Canvas canvas) {canvas.save();calculatePoints();//先把canbvas平移到view的中间canvas.translate(getWidth() / 2, getHeight() / 2);//让光圈的叶片整体旋转,更加贴合实际canvas.rotate(ROTATE_ANGLE * (mCurrentApert - mMinApert) / (mMaxApert - mMinApert));canvas.clipPath(mPath);canvas.drawColor(mBackgroundColor);for (int i = 0; i < 6; i++) {canvas.save();canvas.translate(mPoints[i].x, mPoints[i].y);canvas.rotate(-i * 60);canvas.drawBitmap(mBlade, 0, 0, mPaint);canvas.restore();}canvas.restore();}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (event.getPointerCount() > 1) {return false;}switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mPrevX = event.getX();mPrevY = event.getY();break;case MotionEvent.ACTION_MOVE:float diffx = Math.abs((event.getX() - mPrevX));float diffy = Math.abs((event.getY() - mPrevY));if (diffy > diffx) { // 竖直方向的滑动float diff = (float) Math.sqrt(diffx * diffx + diffy * diffy)/ mCircleRadius * mMaxApert;if (event.getY() > mPrevY) { //判断方向setCurrentApert(mCurrentApert - diff);} else {setCurrentApert(mCurrentApert + diff);}mPrevX = event.getX();mPrevY = event.getY();}break;default:break;}return true;}private void calculatePoints() {if (mCircleRadius - mSpace <= 0) {Log.e(TAG, "the size of view is too small and Space is too large");return;}//mCircleRadius - mSpace可以保证内嵌六边形在光圈内float curRadius = mCurrentApert / mMaxApert * (mCircleRadius - mSpace);//利用对称关系,减少计算mPoints[0].x = curRadius / 2;mPoints[0].y = -curRadius * COS_30;mPoints[1].x = -mPoints[0].x;mPoints[1].y = mPoints[0].y;mPoints[2].x = -curRadius;mPoints[2].y = 0;mPoints[3].x = mPoints[1].x;mPoints[3].y = -mPoints[1].y;mPoints[4].x = -mPoints[3].x;mPoints[4].y = mPoints[3].y;mPoints[5].x = curRadius;mPoints[5].y = 0;}//创建光圈叶片,让美工MM提供更好private void createBlade() {mBlade = Bitmap.createBitmap(mCircleRadius,(int) (mCircleRadius * 2 * COS_30), Config.ARGB_8888);Path path = new Path();Canvas canvas = new Canvas(mBlade);path.moveTo(mSpace / 2 / COS_30, mSpace);path.lineTo(mBlade.getWidth(), mBlade.getHeight());path.lineTo(mBlade.getWidth(), mSpace);path.close();canvas.clipPath(path);canvas.drawColor(mBladeColor);}/** * 设置光圈片的颜色 * @param bladeColor */public void setBladeColor(int bladeColor) {mBladeColor = bladeColor;}/** * 设置光圈背景色 */public void setBackgroundColor(int backgroundColor) {mBackgroundColor = backgroundColor;}/** * 设置光圈片之间的间隔 * @param space */public void setSpace(int space) {mSpace = space;}/** * 设置光圈最大值 * @param maxApert */public void setMaxApert(float maxApert) {mMaxApert = maxApert;}/** * 设置光圈最小值 * @param mMinApert */public void setMinApert(float mMinApert) {this.mMinApert = mMinApert;}public float getCurrentApert() {return mCurrentApert;}public void setCurrentApert(float currentApert) {if (currentApert > mMaxApert) {currentApert = mMaxApert;}if (currentApert < mMinApert) {currentApert = mMinApert;}if (mCurrentApert == currentApert) {return;}mCurrentApert = currentApert;invalidate();if (mApertureChanged != null) {mApertureChanged.onApertureChanged(currentApert);}}/** * 设置光圈值变动的监听 * @param listener */public void setApertureChangedListener(ApertureChanged listener) {mApertureChanged = listener;}}自定义属性的xml:
<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="ApertureView"><attr name="blade_color" format="color" /><attr name="background_color" format="color" /><attr name="blade_space" format="dimension" /></declare-styleable></resources>以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。