博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 弧形ViewPager 和弧形HeaderView(升级版)
阅读量:5786 次
发布时间:2019-06-18

本文共 6275 字,大约阅读时间需要 20 分钟。

前段时间写了一篇项目总结的文章,总结了项目中使用的弧形View 和弧形ViewPager 效果,采用的是自定义View 的方法,然后绘制弧形采用的是二阶贝塞尔曲线,具体的思路和详情请看文章 ,最后效果如下:

虽然效果还不错,但是有瑕疵,有两个明显的缺陷:

  • 底部的圆弧不是正圆弧:如上图所示,弧形有点歪,特别是在小屏幕手机上表现尤为明显,因为是用二阶贝塞尔曲线绘制的圆弧,不管怎么调整控制点,都不会是一个正圆弧,如下图:

  • 圆弧不能设置图片背景:前面的这个版本,弧形背景只能设置颜色,不能设置背景图

1. 升级版ArcView实现思路

既然有了上面说的2个缺点,我们就要想办法解决它,2个问题我们逐个分析一下:

1. 圆弧问题:

版本1的弧形使用二阶贝塞尔曲线绘制,既然这种方式不能绘制一个正圆弧,那么我们不妨换个思路,哪些图形有正圆弧?首先就想到了圆,我们可以绘制一个很大的圆,然后用手机的屏幕去截取,重叠的部分就是我们想要View了,画了一个草图,看得比较直观:

如上图所示,圆形和屏幕的重叠区域就是我们的View区域,圆形重叠之外的区域在屏幕外。这样截取出来的弧形肯定是正圆弧。

2 . 弧形View设置图片背景

我们采用的是自定义View,显示图片还是很简单的,canvasdrawBitmap 就能实现,但是有一个点,图片要显示成我们定义的弧形,这就需要用到 PorterDuffXfermode,PorterDuff.Mode,关于PorterDuffXfermode这里不过多的讲,网上讲它的博客很多,看一下这张经典的图就行白了:

具体实现:先绘制圆,再绘制图片,设置 PorterDuffXfermodePorterDuff.Mode.SRC_IN 就ok了。

2. 具体实现

前面说了思路,那么代码就很简单了,看一下实现的代码:

@Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mHeight = getHeight();        int width = getWidth();        mWidth = width;        // 半径        mRadius = width * 2;        // 矩形        mRect.left = 0;        mRect.top = 0;        mRect.right = width;        mRect.bottom = mHeight;        // 圆心坐标        mCircleCenter.x = width / 2;        mCircleCenter.y = mHeight - width * 2;        // 绘制渐变色        mLinearGradient = new LinearGradient(width / 2, 0, width / 2, mHeight, mStartColor, mEndColor, Shader.TileMode.MIRROR);    }复制代码

解释:圆的半径为屏幕宽度2倍,矩形的高度就是整个自定义View的高度,圆心坐标的y 为 mHeight - mRadius 。

@Override    protected void onDraw(Canvas canvas) {        int canvasWidth = canvas.getWidth();        int canvasHeight = canvas.getHeight();        int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);        canvas.drawCircle(mCircleCenter.x, mCircleCenter.y, mRadius, mPaint);        //设置PorterDuffXfermode        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));        // 通过变量mIsShowImage 来控制是显示图片还是颜色        if (mIsShowImage) {            if (mBitmap != null) {                canvas.drawBitmap(mBitmap, null, mRect, mPaint);            }        } else {            mPaint.setShader(mLinearGradient);//绘制渐变色            canvas.drawRect(mRect, mPaint);        }        mPaint.setXfermode(null);        canvas.restoreToCount(layerId);    }复制代码

就是这么简单,最后效果如下:

效果是不是好了很多?

3. 完整源码

PerfectArcView.java

public class PerfectArcView extends View implements Target {    private Paint mPaint;    private Bitmap mBitmap;    private int mHeight;    private int mWidth;    private RectF mRect = new RectF();    private Point mCircleCenter;    private float mRadius;    private int mStartColor;    private int mEndColor;    private LinearGradient mLinearGradient;    /**     * 显示图片还是显示色值     */    private boolean mIsShowImage = true;    public PerfectArcView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        readAttr(attrs);        init();    }    private void init() {        setLayerType(View.LAYER_TYPE_SOFTWARE, null);        mPaint = new Paint();        mPaint.setColor(Color.WHITE);        mPaint.setStyle(Paint.Style.FILL);        mPaint.setAntiAlias(true);        //  mBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.splash);        mCircleCenter = new Point();    }    private void readAttr(AttributeSet set) {        TypedArray typedArray = getContext().obtainStyledAttributes(set, R.styleable.PerfectArcView);        mStartColor = typedArray.getColor(R.styleable.PerfectArcView_p_arc_startColor, Color.parseColor("#FF3A80"));        mEndColor = typedArray.getColor(R.styleable.PerfectArcView_p_arc_endColor, Color.parseColor("#FF3745"));        mIsShowImage = typedArray.getBoolean(R.styleable.PerfectArcView_p_arc_showImage, false);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mHeight = getHeight();        int width = getWidth();        mWidth = width;        // 半径        mRadius = width * 2;        // 矩形        mRect.left = 0;        mRect.top = 0;        mRect.right = width;        mRect.bottom = mHeight;        // 圆心坐标        mCircleCenter.x = width / 2;        mCircleCenter.y = mHeight - width * 2;        mLinearGradient = new LinearGradient(width / 2, 0, width / 2, mHeight, mStartColor, mEndColor, Shader.TileMode.MIRROR);    }    /**     * 加载网络图片     *     * @param url     */    public void setImageUrl(String url) {        Picasso.with(getContext()).load(url).into(this);    }    /**     * @param startColor     * @param endColor     */    public void setColor(@ColorInt int startColor, @ColorInt int endColor) {        mStartColor = startColor;        mEndColor = endColor;        mIsShowImage = false;        mLinearGradient = new LinearGradient(mWidth / 2, 0, mWidth / 2, mHeight, mStartColor, mEndColor, Shader.TileMode.MIRROR);        invalidate();    }    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)    @Override    protected void onDraw(Canvas canvas) {        int canvasWidth = canvas.getWidth();        int canvasHeight = canvas.getHeight();        int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);        canvas.drawCircle(mCircleCenter.x, mCircleCenter.y, mRadius, mPaint);        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));        if (mIsShowImage) {            if (mBitmap != null) {                canvas.drawBitmap(mBitmap, null, mRect, mPaint);            }        } else {            mPaint.setShader(mLinearGradient);//绘制渐变色            canvas.drawRect(mRect, mPaint);        }        mPaint.setXfermode(null);        canvas.restoreToCount(layerId);    }    @Override    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {        Log.e("TAG", "onBitmapLoaded....");        mBitmap = bitmap;        invalidate();    }    @Override    public void onBitmapFailed(Drawable errorDrawable) {        Log.e("TAG", "onBitmapFailed....");    }    @Override    public void onPrepareLoad(Drawable placeHolderDrawable) {        Log.e("TAG", "onPrepareLoad....");    }}复制代码

4. 最后

条条大路通罗马,本文讲了弧形View的另一种实现思路,当然了,可能还有很多种实现方法,上一篇文章的留言区里,有人同学提到可以在矩形区域的地步覆盖一个白色的弧形图片,这个白色的可以找UI设计师切图,这种应该也是可以实现效果的,但是扩展性不是很强,如果项目中有多个地方用到,还是挺麻烦的。如果你还有其他方法,欢迎交流。 源码访问Github:https://github.com/pinguo-zhouwei/AndroidTrainingSimples

转载地址:http://yutyx.baihongyu.com/

你可能感兴趣的文章
iframe刷新问题
查看>>
数据解码互联网行业职位
查看>>
我所见的讲的最容易理解,逻辑最强的五层网络模型,来自大神阮一峰
查看>>
vue-cli项目打包需要修改的路径问题
查看>>
js实现复选框的操作-------Day41
查看>>
数据结构化与保存
查看>>
[SpringBoot] - 配置文件的多种形式及优先级
查看>>
chrome浏览器开发者工具之同步修改至本地
查看>>
debian7 + wheezy + chromium + flashplayer
查看>>
AOP
查看>>
进阶开发——文档,缓存,ip限速
查看>>
vue中子组件需调用父组件通过异步获取的数据
查看>>
uva 11468 - Substring(AC自己主动机+概率)
查看>>
Mysql 数据备份与恢复,用户创建,授权
查看>>
沉思录
查看>>
Angular.js中的$injector服务
查看>>
构建之法读书笔记01
查看>>
linux - lsof 命令最佳实践
查看>>
kafka性能测试
查看>>
现实世界的Windows Azure:h.e.t软件使用Windows Azure削减50%的成本
查看>>