Android 弧形ViewPager 和弧形HeaderView

先来看效果:



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

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

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

1. 升级版ArcView实现思路

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

1. 圆弧问题:

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


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

2 . 弧形View设置图片背景

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

具体实现:先绘制圆,再绘制图片,设置  PorterDuffXfermode 为 PorterDuff.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....");
   }
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值