自定义一个背景可以滚动的tab栏

原创文章

先上一个效果图,GIF录制的图片有严重的掉帧现象,其实效果还是很流畅的.

此文要说的是上方的切换的时候的白色背景滚动效果.其他内容本文没有说明.

可以看到上方有一个长方形的tab,有两个选项,分别为"美丽店","技术达人",

点击技术达人的时候,后面的白色背景会滚动到右边,点击美丽店的时候,又滚动到左边.

写这个空间的原因是因为要配合fragment的切换动画,这样看起来感觉效果好一点

此效果是通过自定义ViewGroup实现的,并不是通过动画实现,实现起来还是比较简单,本文最后会加上完整代码


首先先说下实现方式,继承一个ViewGroup,这里继承的是LinearLayout,(命名我命名为tabLinearLayout,不知道对不对,将就着用就行了)

然后根据大小画一个白色圆角描边,然后在左半边画纯白背景,右半边透明.

在点击的时候,只要改变一下白色背景的位置,还有上方的文字颜色和图标,其他不用动


1.定义四种状态,代码有注释

/**
 * 背景状态
 */
protected enum StateBG {
    /**
     * 静止在左边
     */
    LEFT_STATIC,
    /**
     * 静止在右边
     */
    RIGHT_STATIC,
    /**
     * 从左边滚动到右边
     */
    LEFT_TO_RIGHT,
    /**
     * 从右边滚动到左边
     */
    RIGHT_TO_LEFT;
}

后面的onDraw()开始写自己的逻辑

2.绘制白色描边

/**
 * 画圆角背景,画白色描边
 *
 * @param canvas
 * @param height
 * @param width
 */
private void drawBg(Canvas canvas, int height, int width) {

    mPaint.setStyle(Paint.Style.STROKE);//设置空心
    // 设置个新的长方形
    if (lineBgRectF == null) {
        lineBgRectF = new RectF(0, 0, width, height);
    }
    canvas.drawRoundRect(lineBgRectF, ROUND_SIZE, ROUND_SIZE, mPaint);//第二个参数是x半径,第三个参数是y半径
}
这里圆角为10px
ROUND_SIZE = 10;

3.0判断状态,然后绘制半边白色的位置

mPaint.setStyle(Paint.Style.FILL);//设置实心
if (oneHalfBg == null) {
    oneHalfBg = new RectF(0, 0, width / 2, height);
}
switch (state) {
    case LEFT_STATIC:
        oneHalfBg.left = 0;
        drawHalfBg(canvas, height, width);
        break;
    case RIGHT_STATIC:
        oneHalfBg.left = width / 2;
        drawHalfBg(canvas, height, width);
        break;
    case LEFT_TO_RIGHT:
        oneHalfBg.left += MOVE_RATE_PX;
        break;
    case RIGHT_TO_LEFT:
        oneHalfBg.left -= MOVE_RATE_PX;
        break;
}

此时判断状态,

3.1.

因为是有圆角效果的,所以在最左右静止和在最右边静止的时候,中间附近还是有圆角效果的,此时并不需要这个效果


所以会在中间贴一个没有圆角的白色矩形

private RectF whiteHalfBg;

private void drawHalfBg(Canvas canvas, int height, int width) {
    if (whiteHalfBg == null) {
        whiteHalfBg = new RectF(0, 0, 0, height);
    }
    //直角矩形的宽度
    int rectfWidth = 10;
    if (state == LEFT_STATIC) {
        whiteHalfBg.left = width / 2 - rectfWidth;
        whiteHalfBg.right = width / 2;
    } else if (state == RIGHT_STATIC) {
        whiteHalfBg.left = width / 2;
        whiteHalfBg.right = width / 2 + rectfWidth;
    }
    canvas.drawRect(whiteHalfBg, mPaint);
}
3.2从左往右滑动,或者从右往左滑动

除了3.0内的代码,后面还需要加上

oneHalfBg.right = oneHalfBg.left + width / 2;//设置右边位置(显示宽)
canvas.drawRoundRect(oneHalfBg, ROUND_SIZE, ROUND_SIZE, mPaint);//第二个参数是x半径,第三个参数是y半径

if (state == LEFT_TO_RIGHT || state == RIGHT_TO_LEFT) {
    invalidate();
}
if (oneHalfBg.left < 0) state = LEFT_STATIC;
if (oneHalfBg.left > width / 2) state = RIGHT_STATIC;

也就是不断绘制白色,并且设置好白色下一次绘制的位置,等下一次绘制,直到到了最左边或者最右边的时候,把状态改一下,使其不再绘制.


到此白色背景滚动效果基本完成了,但是有一点要注意,因为这个是继承viewgroup的,所以onDraw方法不会被调用,所以要在layout的xml控件处添加

android:background="@color/transparent"

设置一个透明背景,使其调用ondraw()方法.或者用其他方式使其调用即可


下面贴上完整代码:

1.Java代码



import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.LinearLayout;

import com.orhanobut.logger.Logger;

import static com.hoyar.beautyshop.view.TabLinearLayout.StateBG.LEFT_STATIC;
import static com.hoyar.beautyshop.view.TabLinearLayout.StateBG.LEFT_TO_RIGHT;
import static com.hoyar.beautyshop.view.TabLinearLayout.StateBG.RIGHT_STATIC;
import static com.hoyar.beautyshop.view.TabLinearLayout.StateBG.RIGHT_TO_LEFT;


/**
 * 可供标签切换的layout
 */

public class TabLinearLayout extends LinearLayout {
    public TabLinearLayout(Context context) {
        super(context);
    }

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

    private final Paint mPaint = new Paint();

    {
        mPaint.setAntiAlias(true);//设置抗锯齿

        mPaint.setColor(Color.WHITE);
    }

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

    private StateBG state = StateBG.LEFT_STATIC;
    //白色背景描边
    private RectF lineBgRectF;
    //一半的背景图片
    private RectF oneHalfBg;

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

        int height = getMeasuredHeight();
        int width = getMeasuredWidth();
        drawBg(canvas, height, width);

        mPaint.setStyle(Paint.Style.FILL);//设置实心
        if (oneHalfBg == null) {
            oneHalfBg = new RectF(0, 0, width / 2, height);
        }
        switch (state) {
            case LEFT_STATIC:
                oneHalfBg.left = 0;
                drawHalfBg(canvas, height, width);
                break;
            case RIGHT_STATIC:
                oneHalfBg.left = width / 2;
                drawHalfBg(canvas, height, width);
                break;
            case LEFT_TO_RIGHT:
                oneHalfBg.left += MOVE_RATE_PX;
                break;
            case RIGHT_TO_LEFT:
                oneHalfBg.left -= MOVE_RATE_PX;
                break;
        }
        oneHalfBg.right = oneHalfBg.left + width / 2;//设置右边位置(显示宽)
        canvas.drawRoundRect(oneHalfBg, ROUND_SIZE, ROUND_SIZE, mPaint);//第二个参数是x半径,第三个参数是y半径

        if (state == LEFT_TO_RIGHT || state == RIGHT_TO_LEFT) {
            invalidate();
        }
        if (oneHalfBg.left < 0) state = LEFT_STATIC;
        if (oneHalfBg.left > width / 2) state = RIGHT_STATIC;
        Logger.d("ondraw()了");
    }

    private static final int MOVE_RATE_PX = 8;

    private RectF whiteHalfBg;

    private void drawHalfBg(Canvas canvas, int height, int width) {
        if (whiteHalfBg == null) {
            whiteHalfBg = new RectF(0, 0, 0, height);
        }
        //直角矩形的宽度
        int rectfWidth = 10;
        if (state == LEFT_STATIC) {
            whiteHalfBg.left = width / 2 - rectfWidth;
            whiteHalfBg.right = width / 2;
        } else if (state == RIGHT_STATIC) {
            whiteHalfBg.left = width / 2;
            whiteHalfBg.right = width / 2 + rectfWidth;
        }
        canvas.drawRect(whiteHalfBg, mPaint);
    }

    public void leftToRight() {
        state = LEFT_TO_RIGHT;
        invalidate();
    }

    public void rightToLeft() {
        state = RIGHT_TO_LEFT;
        invalidate();
    }

    /**
     * 画圆角背景,画白色描边
     *
     * @param canvas
     * @param height
     * @param width
     */
    private void drawBg(Canvas canvas, int height, int width) {

        mPaint.setStyle(Paint.Style.STROKE);//设置空心
        // 设置个新的长方形
        if (lineBgRectF == null) {
            lineBgRectF = new RectF(0, 0, width, height);
        }
        canvas.drawRoundRect(lineBgRectF, ROUND_SIZE, ROUND_SIZE, mPaint);//第二个参数是x半径,第三个参数是y半径
    }

    /**
     * 圆角的大小
     */
    private static final int ROUND_SIZE = 10;

    /**
     * 背景状态
     */
    protected enum StateBG {
        /**
         * 静止在左边
         */
        LEFT_STATIC,
        /**
         * 静止在右边
         */
        RIGHT_STATIC,
        /**
         * 从左边滚动到右边
         */
        LEFT_TO_RIGHT,
        /**
         * 从右边滚动到左边
         */
        RIGHT_TO_LEFT;
    }
}

使用控件的关键xml代码,中间平分自定义的tabLinearLayout:

<com.hoyar.beautyshop.view.TabLinearLayout
    android:id="@+id/fragment_appointment_tab_linear_layout"
    android:layout_width="190dp"
    android:layout_height="25dp"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="30dp"
    android:background="@color/transparent"
    android:orientation="horizontal">
    <!--android:background="@drawable/appointment_tab_bg"-->
    <!--字体没有标注-->
    <RelativeLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1">

        <TextView
            android:id="@+id/fragment_appointment_tv_beauty_shop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:drawableLeft="@mipmap/appointment_tab_store_sel"
            android:drawablePadding="7dp"
            android:includeFontPadding="false"
            android:text="美丽店"
            android:textColor="@color/appointment_blue_bg" />

    </RelativeLayout>

    <RelativeLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1">

        <TextView
            android:id="@+id/fragment_appointment_tv_master"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:drawableLeft="@mipmap/appointment_tab_master"
            android:drawablePadding="7dp"
            android:includeFontPadding="false"
            android:text="技术达人"
            android:textColor="@color/white" />

    </RelativeLayout>
</com.hoyar.beautyshop.view.TabLinearLayout>

外部调用背景切换的代码

执行右往左动画:

tabLinearLayout.rightToLeft();
执行左往右动画:

tabLinearLayout.leftToRight();

记得在切换的时候要改相应的图标的文本颜色,这样看得到效果,这里不贴.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值