Android 弧形ViewPager 和弧形HeaderView(升級版)

封面.png

前段時間寫了一篇項目總結的文章,總結了項目中使用的弧形View 和弧形ViewPager 效果互亮,采用的是自定義View 的方法,然后繪制弧形采用的是二階貝塞爾曲線余素,具體的思路和詳情請看文章Android 項目總結(一):弧形ViewPager 和弧形HeaderView ,最后效果如下:

image.png
弧形viewpager.gif

雖然效果還不錯豹休,但是有瑕疵,有兩個明顯的缺陷:

  • 底部的圓弧不是正圓弧:如上圖所示桨吊,弧形有點歪威根,特別是在小屏幕手機上表現(xiàn)尤為明顯凤巨,因為是用二階貝塞爾曲線繪制的圓弧,不管怎么調(diào)整控制點洛搀,都不會是一個正圓弧敢茁,如下圖:
image.png
  • 圓弧不能設置圖片背景:前面的這個版本,弧形背景只能設置顏色留美,不能設置背景圖

1. 升級版ArcView實現(xiàn)思路

既然有了上面說的2個缺點彰檬,我們就要想辦法解決它,2個問題我們逐個分析一下:

1. 圓弧問題:

版本1的弧形使用二階貝塞爾曲線繪制谎砾,既然這種方式不能繪制一個正圓弧僧叉,那么我們不妨換個思路,哪些圖形有正圓还桌啤?首先就想到了圓隘道,我們可以繪制一個很大的圓症歇,然后用手機的屏幕去截取,重疊的部分就是我們想要View了谭梗,畫了一個草圖忘晤,看得比較直觀:

image.png

如上圖所示,圓形和屏幕的重疊區(qū)域就是我們的View區(qū)域激捏,圓形重疊之外的區(qū)域在屏幕外设塔。這樣截取出來的弧形肯定是正圓弧。

2 . 弧形View設置圖片背景

我們采用的是自定義View,顯示圖片還是很簡單的,canvasdrawBitmap 就能實現(xiàn)远舅,但是有一個點闰蛔,圖片要顯示成我們定義的弧形,這就需要用到 PorterDuffXfermode,PorterDuff.Mode,關于PorterDuffXfermode這里不過多的講图柏,網(wǎng)上講它的博客很多序六,看一下這張經(jīng)典的圖就行白了:

image.png

具體實現(xiàn):先繪制圓,再繪制圖片蚤吹,設置 PorterDuffXfermodePorterDuff.Mode.SRC_IN 就ok了例诀。

2. 具體實現(xiàn)

前面說了思路,那么代碼就很簡單了裁着,看一下實現(xiàn)的代碼:

 @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);
    }

就是這么簡單扔罪,最后效果如下:

image.png

效果是不是好了很多?

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);
    }

    /**
     * 加載網(wǎng)絡圖片
     *
     * @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的另一種實現(xiàn)思路步势,當然了氧猬,可能還有很多種實現(xiàn)方法,上一篇文章的留言區(qū)里坏瘩,有人同學提到可以在矩形區(qū)域的地步覆蓋一個白色的弧形圖片柜某,這個白色的可以找UI設計師切圖,這種應該也是可以實現(xiàn)效果的嚼酝,但是擴展性不是很強毙驯,如果項目中有多個地方用到,還是挺麻煩的哪自。如果你還有其他方法丰包,歡迎交流。
源碼訪問Github:https://github.com/pinguo-zhouwei/AndroidTrainingSimples

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壤巷,一起剝皮案震驚了整個濱河市邑彪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胧华,老刑警劉巖寄症,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異矩动,居然都是意外死亡有巧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門悲没,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篮迎,“玉大人,你說我怎么就攤上這事示姿√鸪鳎” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵峻凫,是天一觀的道長渗鬼。 經(jīng)常有香客問我,道長荧琼,這世上最難降的妖魔是什么譬胎? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮命锄,結果婚禮上堰乔,老公的妹妹穿的比我還像新娘。我一直安慰自己脐恩,他們只是感情好镐侯,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般苟翻。 火紅的嫁衣襯著肌膚如雪韵卤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天崇猫,我揣著相機與錄音沈条,去河邊找鬼。 笑死诅炉,一個胖子當著我的面吹牛蜡歹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涕烧,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼月而,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了议纯?” 一聲冷哼從身側(cè)響起父款,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞻凤,沒想到半個月后铛漓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡鲫构,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了玫坛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片结笨。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖湿镀,靈堂內(nèi)的尸體忽然破棺而出炕吸,到底是詐尸還是另有隱情,我是刑警寧澤勉痴,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布赫模,位于F島的核電站,受9級特大地震影響蒸矛,放射性物質(zhì)發(fā)生泄漏瀑罗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一雏掠、第九天 我趴在偏房一處隱蔽的房頂上張望斩祭。 院中可真熱鬧,春花似錦乡话、人聲如沸摧玫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诬像。三九已至屋群,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坏挠,已是汗流浹背芍躏。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留癞揉,地道東北人纸肉。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像喊熟,于是被迫代替她去往敵國和親柏肪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容