One, foreword

I haven’t updated my blog for a month or so. I am really busy working on a new project recently. I have entered 996 mode and don’t have much spare time to write blog. Although starting a new project is very busy and tiring, it is indeed an opportunity to improve myself. As I said in the previous article “The Road to Progress for Android Developers”, in fact, the best progress comes from actual combat. These new technologies and new frameworks learned are applied to the project, constantly digging holes and filling holes. In this way we can gain more and grow faster.

The precipitation of technology comes from summary. I feel that there are many things to summarize in the project. I will summarize some technologies used in the project and share them with you. Ok, without further ado, today I will start the first article about arc HeaderView.

Second, the demand for

This kind of curved Header interface has become very popular recently and can be seen in several apps. Let’s take a look at two images.

Palm’s Profile page:

Partial pages in LifeSum:

Arc view

Ok, these are the two effects above:

Static arc header 2. Arc ViewPager

Ideas:

How to make the above two effects? There are actually two effects, but there’s only one effect, and that’s the static arc Header View, and once you’ve done that, the ViewPager effect is easy.

The arc ViewPager, we can split it into 2 parts:

1. Arc Header View as the background. 2. Transparent plain ViewPager.

But what’s important is, how do I do the Header View of this arc? Let’s do this by customizing the View. Let’s take a closer look at the View:

Composed of two parts: rectangle + arc, the above rectangle is very simple to draw, not to say more. The following arc can be plotted by a second-order Bezier curve.

Customize ArcHeaderView

From the above analysis, we know that it is a rectangle + an arc, we can use Path to combine the two shapes. Finally, we draw the shape we want.

Second-order Bezier curve:

First let’s look at a second order Bezier curve. Second order Bezier curves

The second-order curve consists of two data points (A and C) and one control point (B) to describe the curve state, which is roughly as follows:

The red curve in the figure above is the legendary second-order Bezier curve, so how is this red curve generated? Let’s analyze it in one of the states:

Join AB BC and take points D on AB and E on BC so that it satisfies the condition:

Connect DE, take point F, such that:

The obtained point F is a point on the Bezier curve, and the dynamic process is as follows:

Drawing a second-order Bezier curve requires three points: a starting point, an end point and a control point. The different position of control point determines the different degree of curve bending. The method of drawing second-order Bezier curve is as follows:quadTo(float x1, float y1, float x2, float y2)

The drawing code is as follows:

 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mWidth = w;
        mHeight = h;

        mPath.reset();
        // The upper part of the rectangle
        mPath.moveTo(0.0);
        mPath.addRect(0.0, mWidth, mHeight - mArcHeight, Path.Direction.CCW);

        / / starting point
        mStartPoint.x = 0;
        mStartPoint.y = mHeight - mArcHeight;
        / / the end
        mEndPoint.x = mWidth;
        mEndPoint.y = mHeight - mArcHeight;
        / / control points
        mControlPoint.x = mWidth / 2 - 50;
        mControlPoint.y = mHeight + 100;

        // Initialize shader
        mLinearGradient = new LinearGradient(mWidth / 2.0, mWidth / 2, mHeight, mStartColor, mEndColor, Shader.TileMode.MIRROR);


        invalidate();

    }Copy the code

Finally, in the onDraw method, just call the method draw.

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setShader(mLinearGradient);

        mPath.moveTo(mStartPoint.x, mStartPoint.y);
        // Add bezier curves
        mPath.quadTo(mControlPoint.x, mControlPoint.y, mEndPoint.x, mEndPoint.y);
        / / to draw
        canvas.drawPath(mPath, mPaint);
    }Copy the code

The final effect is as follows:

ArcHeaderView code is as follows:


public class ArcHeaderView extends View {
    private Paint mPaint;
    private PointF mStartPoint, mEndPoint, mControlPoint;
    private int mWidth;
    private int mHeight;
    private Path mPath = new Path();
    private int mArcHeight = 100;// Arc height
    private int mStartColor;
    private int mEndColor;
    private LinearGradient mLinearGradient;

    public ArcHeaderView(Context context) {
        super(context);
        init();
    }

    public ArcHeaderView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ArcHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public ArcHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init(a) {
        mPaint = new Paint();
        // mPaint.setColor(Color.parseColor("#37B99F"));
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(10);
        mPaint.setStyle(Paint.Style.FILL);

        mStartPoint = new PointF(0.0);
        mEndPoint = new PointF(0.0);
        mControlPoint = new PointF(0.0);

        mStartColor = Color.parseColor("#FF3A80");
        mEndColor = Color.parseColor("#FF3745");

    }


    / * * * *@param startColor
     * @param endColor
     */
    public void setColor(@ColorInt int startColor, @ColorInt int endColor) {
        mStartColor = startColor;
        mEndColor = endColor;
        mLinearGradient = new LinearGradient(mWidth / 2.0, mWidth / 2, mHeight, mStartColor, mEndColor, Shader.TileMode.MIRROR);
        invalidate();
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mWidth = w;
        mHeight = h;

        mPath.reset();

        mPath.moveTo(0.0);
        mPath.addRect(0.0, mWidth, mHeight - mArcHeight, Path.Direction.CCW);

        mStartPoint.x = 0;
        mStartPoint.y = mHeight - mArcHeight;

        mEndPoint.x = mWidth;
        mEndPoint.y = mHeight - mArcHeight;

        mControlPoint.x = mWidth / 2 - 50;
        mControlPoint.y = mHeight + 100;

        // Initialize shader
        mLinearGradient = new LinearGradient(mWidth / 2.0, mWidth / 2, mHeight, mStartColor, mEndColor, Shader.TileMode.MIRROR);

        ///SweepGradient sweepGradient = new SweepGradient(mEndPoint.x / 2,mEndPoint.y / 2,mStartColor,mEndColor);
        //mPaint.setShader(sweepGradient);


        invalidate();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas); mPaint.setShader(mLinearGradient); mPath.moveTo(mStartPoint.x, mStartPoint.y); mPath.quadTo(mControlPoint.x, mControlPoint.y, mEndPoint.x, mEndPoint.y); canvas.drawPath(mPath, mPaint); }}Copy the code

4, arc ViewPager effect

With the custom ArcHeaderView above, it’s easy to do this ViewPager effect. The previous analysis has not been repeated here. Directly on the code:

The layout file is as follows:

<? The XML version = "1.0" encoding = "utf-8"? > <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <com.zhouwei.androidtrainingsimples.arc_header_view.ArcHeaderView android:id="@+id/header_view" android:layout_width="match_parent" android:layout_height="300dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginTop="50dp" > <com.zhouwei.mzbanner.MZBannerView android:id="@+id/arc_view_pager" android:layout_width="match_parent" android:layout_height="250dp" app:canLoop="false" app:open_mz_mode="false" > </com.zhouwei.mzbanner.MZBannerView> </LinearLayout> </FrameLayout>Copy the code

The Activity code is as follows:

public class ArcHeaderViewPagerActivity extends AppCompatActivity {
    private ArcHeaderView mArcHeaderView;
    private MZBannerView mMZBannerView;
    public int mStartColor[] ;
    public int mEndColor[] ;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.arc_header_viewpager_layout_activity);
        StatusBarUtils.setColor(this,getResources().getColor(R.color.start_color),0);

        initView();
    }

    private void initView(a) {
        mStartColor = new int[]{getResources().getColor(R.color.start_color)
                ,getResources().getColor(R.color.page1_start_color)
                ,getResources().getColor(R.color.page2_start_color)
                ,getResources().getColor(R.color.page3_start_color)};
        mEndColor = new int[]{getResources().getColor(R.color.end_color)
                ,getResources().getColor(R.color.page1_end_color)
                ,getResources().getColor(R.color.page2_end_color)
                ,getResources().getColor(R.color.page3_end_color)};
        mMZBannerView = (MZBannerView) findViewById(R.id.arc_view_pager);
        mArcHeaderView = (ArcHeaderView) findViewById(R.id.header_view);
        mArcHeaderView.setColor(getResources().getColor(R.color.start_color),getResources().getColor(R.color.end_color));

        mMZBannerView.addPageChangeLisnter(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Override
            public void onPageSelected(int position) {
                StatusBarUtils.setColor(ArcHeaderViewPagerActivity.this,mStartColor[position],0);
                mArcHeaderView.setColor(mStartColor[position],mEndColor[position]);
            }

            @Override
            public void onPageScrollStateChanged(int state) {}}); mMZBannerView.setPages(mockData(),new MZHolderCreator() {
            @Override
            public MZViewHolder createViewHolder(a) {
                return newBannerViewHolder(); }}); }public static class BannerViewHolder implements MZViewHolder<MyData> {
        private TextView text1,text2;
        @Override
        public View createView(Context context) {
            // Return to the page layout
            View view = LayoutInflater.from(context).inflate(R.layout.arc_view_pager_item,null);
            text1 = (TextView) view.findViewById(R.id.arc_title);
            text2 = (TextView) view.findViewById(R.id.arc_content);
            return view;
        }

        @Override
        public void onBind(Context context, int position, MyData data) {
            // Data binding
           // mImageView.setImageResource(data);text1.setText(data.title); text2.setText(data.content); }}public List<MyData> mockData(a){
        List<MyData> datas = new ArrayList<>();
        for(int i=0; i<4; i++){ MyData myData =new MyData();
            myData.title = "Public account: Android technology grocery store";
            myData.content = "Find your\n"+"Plan"+i;
            datas.add(myData);
        }
        return datas;
    }

    public static class MyData{
        public  String title;
        publicString content; }}Copy the code

The final effect is as follows:

Arc viewpager

The last

This is all about the summary of this issue, Demo code has been uploaded to Github:github.com/pinguo-zhou… Welcome to the discussion.

More Android dry goods articles, pay attention to the public number [Android technology grocery store]