Android开发之使用TabLayout快速实现选项卡切换功能(附源码下载)

看下效果图:

先看下布局:

TabLayout的简单使用:
当选项卡过少时候设置填充全屏
 app:tabGravity="fill"
 设置下面切换选项卡的小滑片颜色
 app:tabIndicatorColor="#8B1C21"
 设置下面切换选项卡的小滑片高度
 app:tabIndicatorHeight="1dp"
 app:tabMaxWidth="0dp"
 app:tabSelectedTextColor="#8B1C21"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="@null"
              android:orientation="vertical">


    <android.support.design.widget.TabLayout
            android:id="@+id/tb_monetary"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="#ffffff"
            app:tabGravity="fill"
            app:tabIndicatorColor="#8B1C21"
            app:tabIndicatorHeight="1dp"
            app:tabMaxWidth="0dp"
            app:tabSelectedTextColor="#8B1C21"/>

    <FrameLayout
            android:id="@+id/fl_monetary_replace"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

</LinearLayout>

再看下首页activity代码:

package kotlin.yhsh.cn.tablayoutandline;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;

/**
 * @author DELL
 */
public class MainActivity extends AppCompatActivity {
    private TabLayout tbMonetary;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        tbMonetary = findViewById(R.id.tb_monetary);
        tbMonetary.addTab(tbMonetary.newTab().setText("近七日年化"));
        tbMonetary.addTab(tbMonetary.newTab().setText("万份收益"));
        //默认七日收益
        getSupportFragmentManager().beginTransaction().replace(R.id.fl_monetary_replace, SevenDayMoneyFragment.newInstance()).commit();
        initListener();
    }

    private void initListener() {
        tbMonetary.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                if (tab.getPosition() == 0) {
                    //七日年化
                    transaction.replace(R.id.fl_monetary_replace, SevenDayMoneyFragment.newInstance()).commit();
                } else if (tab.getPosition() == 1) {
                    //万份收益
                    transaction.replace(R.id.fl_monetary_replace, OneYearMoneyFragment.newInstance()).commit();
                }
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
    }
}

然后看下两个切换的fragment代码:

package kotlin.yhsh.cn.tablayoutandline;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RadioButton;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


/**
 * @author 下一页5(轻飞扬)
 * 创建时间:2019/6/12 17:37
 * 个人小站:http://yhsh.wap.ai(已挂)
 * 最新小站:http://www.iyhsh.icoc.in
 * 空间名称:group-wallet-android
 */
public class SevenDayMoneyFragment extends Fragment {

    private View sevenDayView;
    //x轴数据集合
    List<String> xValues;
    //y轴数据集合
    List<Integer> yValues;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        sevenDayView = View.inflate(getContext(), R.layout.fragment_seven_day_money, null);
        initViewID();
        return sevenDayView;
    }

    private void initViewID() {
        RadioButton rbMoneySevenDay = sevenDayView.findViewById(R.id.rb_money_seven_day);
        //默认选中7天
        rbMoneySevenDay.setChecked(true);
        MyLineChartView mlvFinancingLine = sevenDayView.findViewById(R.id.mlv_financing_line);
        xValues = new ArrayList<>();
        yValues = new ArrayList<>();
        for (int i = 1; i <= 7; i++) {
            Random random = new Random();
            xValues.add(i + "号");
            yValues.add(random.nextInt(1000));
        }
        // xy轴集合自己添加数据
        mlvFinancingLine.setXValues(xValues);
        mlvFinancingLine.setYValues(yValues);
    }

    public static Fragment newInstance() {
        return new SevenDayMoneyFragment();
    }
}

我们看下第一个fragment里面的自定义折线图

package kotlin.yhsh.cn.tablayoutandline;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;


/**
 * @author 下一页5(轻飞扬)
 *         创建时间:2019/7/12 10:20
 *         个人小站:http://yhsh.wap.ai(已挂)
 *         空间名称:group-wallet-android
 */
public class MyLineChartView extends View {
    //原点x坐标
    private int originX;
    //原点y坐标
    private int originY;
    //第一个点x坐标
    private int firstPointX;
    //移动时第一个点的最小x值
    private int firstMinX;
    //移动时第一个点的最大x值
    private int firstMaxX;
    //坐标刻度的间隔
    private int intervalX = 130;
    //y轴刻度的间隔
    private int intervalY = 80;

    private List<String> xValues;

    private List<Integer> yValues;
    //控件宽度
    private int mWidth;
    //控件高度
    private int mHeight;
    //滑动时上一次手指的x坐标值
    private int startX;
    //xy轴文字大小
    private int xyTextSize = 24;
    //默认上下左右的padding
    private int paddingTop = 140;

    private int paddingLeft = 160;

    private int paddingRight = 100;

    private int paddingDown = 150;
    //x轴刻度线高度
    private int scaleHeight = 10;
    //xy轴的文字距xy线的距离
    private int textToXYAxisGap = 20;
    //x轴左右向外延伸的长度
    private int leftRightExtra = intervalX / 3;
    // Y轴刻度个数
    private int lableCountY = 6;

    private int bigCircleR = 7;

    private int smallCircleR = 5;
    //y轴最小值
    private float minValueY;
    // y轴最大值
    private float maxValueY = 0;
    //比例图线段长度
    private int shortLine = 34;

    private Paint paintWhite, paintBlue, paintRed, paintBack, paintText;
    //view的背景颜色
    private int backGroundColor = Color.parseColor("#ffffff");

    private GestureDetector gestureDetector;

    private String legendTitle = "涨停";


    public MyLineChartView(Context context) {
        this(context, null);
    }

    public MyLineChartView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyLineChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        gestureDetector = new GestureDetector(context, new MyOnGestureListener());
    }

    private void initPaint() {
        paintWhite = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintWhite.setColor(Color.parseColor("#5C6170"));
        paintWhite.setStyle(Paint.Style.STROKE);

        paintBlue = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintBlue.setColor(Color.parseColor("#5C6170"));
        paintBlue.setStrokeWidth(3f);
        paintBlue.setStyle(Paint.Style.STROKE);

        paintBack = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintBack.setColor(Color.parseColor("#ffffff"));
        paintBack.setStyle(Paint.Style.FILL);

        paintRed = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintRed.setColor(Color.RED);
        paintRed.setStrokeWidth(3f);
        paintRed.setStyle(Paint.Style.STROKE);

        paintText = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintText.setColor(Color.parseColor("#5C6170"));
        paintText.setTextSize(xyTextSize);
        paintText.setStrokeWidth(2f);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (changed) {
            mWidth = getWidth();
            mHeight = getHeight();

            originX = paddingLeft - leftRightExtra;
            originY = mHeight - paddingDown;

            firstPointX = paddingLeft;
            firstMinX = mWidth - originX - (xValues.size() - 1) * intervalX - leftRightExtra;
            // 滑动时,第一个点x值最大为paddingLeft,在大于这个值就不能滑动了
            firstMaxX = firstPointX;
            setBackgroundColor(backGroundColor);
        }
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        drawX(canvas);
        drawBrokenLine(canvas);
        drawY(canvas);
        drawLegend(canvas);

    }

    /**
     * 画x轴
     *
     * @param canvas
     */
    private void drawX(Canvas canvas) {
        Path path = new Path();
        path.moveTo(originX, originY);
        for (int i = 0; i < xValues.size(); i++) {
            // x轴线 写死不变
            path.lineTo(mWidth - paddingRight, originY);
            // x轴箭头
            canvas.drawLine(mWidth - paddingRight, originY, mWidth - paddingRight - 15, originY + 10, paintWhite);
            canvas.drawLine(mWidth - paddingRight, originY, mWidth - paddingRight - 15, originY - 10, paintWhite);

            // x轴线上的刻度线
            canvas.drawLine(firstPointX + i * intervalX, originY, firstPointX + i * intervalX, originY - scaleHeight, paintWhite);
            // x轴上的文字
            canvas.drawText(xValues.get(i), firstPointX + i * intervalX - getTextWidth(paintText, "17.01") / 2,
                    originY + textToXYAxisGap + getTextHeight(paintText, "17.01"), paintText);
        }
        canvas.drawPath(path, paintWhite);

        // x轴虚线
        Paint p = new Paint();
        p.setStyle(Paint.Style.STROKE);
        p.setColor(Color.parseColor("#5C6170"));

        Path path1 = new Path();
        DashPathEffect dash = new DashPathEffect(new float[]{8, 10, 8, 10}, 0);
        p.setPathEffect(dash);
        for (int i = 0; i < lableCountY; i++) {
            path1.moveTo(originX, mHeight - paddingDown - leftRightExtra - i * intervalY);
            path1.lineTo(mWidth - paddingRight, mHeight - paddingDown - leftRightExtra - i * intervalY);
        }
        canvas.drawPath(path1, p);
    }

    /**
     * 画折线
     *
     * @param canvas
     */
    private void drawBrokenLine(Canvas canvas) {
        canvas.save();
        // y轴文字
        minValueY = yValues.get(0);
        for (int i = 0; i < yValues.size(); i++) {
            // 找出y轴的最大最小值
            if (yValues.get(i) > maxValueY) {
                maxValueY = yValues.get(i);
            }
            if (yValues.get(i) < minValueY) {
                minValueY = yValues.get(i);
            }
        }
        // 画折线
        float aver = (lableCountY - 1) * intervalY / (maxValueY - minValueY);
        Path path = new Path();
        for (int i = 0; i < yValues.size(); i++) {
            if (i == 0) {
                path.moveTo(firstPointX, mHeight - paddingDown - leftRightExtra - yValues.get(i) * aver + minValueY * aver);
            } else {
                path.lineTo(firstPointX + i * intervalX, mHeight - paddingDown - leftRightExtra - yValues.get(i) * aver + minValueY * aver);
            }
        }
        canvas.drawPath(path, paintBlue);
        // 折线中的圆点
        for (int i = 0; i < yValues.size(); i++) {
            canvas.drawCircle(firstPointX + i * intervalX,
                    mHeight - paddingDown - leftRightExtra - yValues.get(i) * aver + minValueY * aver, bigCircleR, paintBlue);
            canvas.drawCircle(firstPointX + i * intervalX,
                    mHeight - paddingDown - leftRightExtra - yValues.get(i) * aver + minValueY * aver, smallCircleR, paintBack);
        }
        //将折线超出x轴坐标的部分截取掉(左边)
        paintBack.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
        RectF rectF = new RectF(0, 0, originX, mHeight);
        canvas.drawRect(rectF, paintBack);
        canvas.restore();
    }

    /**
     * 画y轴
     *
     * @param canvas
     */
    private void drawY(Canvas canvas) {
        canvas.save();
        Path path = new Path();
        path.moveTo(originX, originY);

        for (int i = 0; i < lableCountY; i++) {
            // y轴线
            if (i == 0) {
                path.lineTo(originX, mHeight - paddingDown - leftRightExtra);
            } else {
                path.lineTo(originX, mHeight - paddingDown - leftRightExtra - i * intervalY);
            }

            int lastPointY = mHeight - paddingDown - leftRightExtra - i * intervalY;
            if (i == lableCountY - 1) {
                int lastY = lastPointY - leftRightExtra - leftRightExtra / 2;
                // y轴最后一个点后,需要额外加上一小段,就是一个半leftRightExtra的长度
                canvas.drawLine(originX, lastPointY, originX, lastY, paintWhite);
                // y轴箭头
                canvas.drawLine(originX, lastY, originX - 10, lastY + 15, paintWhite);
                canvas.drawLine(originX, lastY, originX + 10, lastY + 15, paintWhite);
            }
        }
        canvas.drawPath(path, paintWhite);

        // y轴文字
        float space = (maxValueY - minValueY) / (lableCountY - 1);
        DecimalFormat decimalFormat = new DecimalFormat("0.00");
        List<String> yTitles = new ArrayList<>();
        for (int i = 0; i < lableCountY; i++) {
            yTitles.add(decimalFormat.format(minValueY + i * space));
        }
        for (int i = 0; i < yTitles.size(); i++) {
            canvas.drawText(yTitles.get(i), originX - textToXYAxisGap - getTextWidth(paintText, "00.00"),
                    mHeight - paddingDown - leftRightExtra - i * intervalY + getTextHeight(paintText, "00.00") / 2, paintText);
        }
        // 截取折线超出部分(右边)
        paintBack.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
        RectF rectF = new RectF(mWidth - paddingRight, 0, mWidth, mHeight);
        canvas.drawRect(rectF, paintBack);
        canvas.restore();
    }

    /**
     * 画图例
     */
    private void drawLegend(Canvas canvas) {
        // 开始点的坐标
        int x = 350;
        int y = mHeight - (paddingDown - textToXYAxisGap - getTextHeight(paintText, "06.00")) / 2;
        canvas.save();
        canvas.drawLine(x, y, x + 2 * shortLine, y, paintBlue);
        canvas.drawCircle(x + shortLine, y, bigCircleR, paintBlue);
        canvas.drawCircle(x + shortLine, y, smallCircleR, paintBack);
        canvas.drawText(legendTitle, x + 2 * shortLine + 10, y + getTextHeight(paintText, legendTitle) / 2 - 2, paintText);

        canvas.drawLine(x + 2 * shortLine + getTextWidth(paintText, legendTitle) + 20,
                y, x + 2 * shortLine + getTextWidth(paintText, legendTitle) + 20 + 2 * shortLine, y, paintRed);
        canvas.drawCircle(x + 2 * shortLine + getTextWidth(paintText, legendTitle) + 20 + shortLine, y, bigCircleR, paintRed);
        canvas.drawCircle(x + 2 * shortLine + getTextWidth(paintText, legendTitle) + 20 + shortLine, y, smallCircleR, paintBack);
        canvas.drawText("涨停", x + 2 * shortLine + getTextWidth(paintText, legendTitle) + 30 + 2 * shortLine,
                y + getTextHeight(paintText, legendTitle) / 2 - 2, paintText);
        canvas.restore();
    }


    /**
     * 手势事件
     */
    class MyOnGestureListener implements GestureDetector.OnGestureListener {
        @Override
        public boolean onDown(MotionEvent e) { // 按下事件
            return false;
        }

        // 按下停留时间超过瞬时,并且按下时没有松开或拖动,就会执行此方法
        @Override
        public void onShowPress(MotionEvent motionEvent) {
        }

        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) { // 单击抬起
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (e1.getX() > originX && e1.getX() < mWidth - paddingRight &&
                    e1.getY() > paddingTop && e1.getY() < mHeight - paddingDown) {
                //注意:这里的distanceX是e1.getX()-e2.getX()
                distanceX = -distanceX;
                if (firstPointX + distanceX > firstMaxX) {
                    firstPointX = firstMaxX;
                } else if (firstPointX + distanceX < firstMinX) {
                    firstPointX = firstMinX;
                } else {
                    firstPointX = (int) (firstPointX + distanceX);
                }
                invalidate();
            }
            return false;
        }

        @Override
        public void onLongPress(MotionEvent motionEvent) {
        } // 长按事件

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            return false;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (yValues.size() < 7) {
            return false;
        }
        gestureDetector.onTouchEvent(event);
        return true;
    }

    public void setXValues(List<String> values) {
        this.xValues = values;
    }

    public void setYValues(List<Integer> values) {
        this.yValues = values;
    }

    /**
     * 获取文字的宽度
     *
     * @param paint
     * @param text
     * @return
     */
    private int getTextWidth(Paint paint, String text) {
        return (int) paint.measureText(text);
    }

    /**
     * 获取文字的高度
     *
     * @param paint
     * @param text
     * @return
     */
    private int getTextHeight(Paint paint, String text) {
        Rect rect = new Rect();
        paint.getTextBounds(text, 0, text.length(), rect);
        return rect.height();
    }
}

第二个fragment是空的

package com.yhsh.financing.home;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.yhsh.financing.R;
import com.yhsh.mobile.common.base.BaseFragment;


/**
 * @author 下一页5(轻飞扬)
 *         创建时间:2019/6/12 17:37
 *         个人小站:http://yhsh.wap.ai(已挂)
 *         最新小站:http://www.iyhsh.icoc.in
 *         空间名称:group-wallet-android
 *         项目包名:com.yhsh.financing.home
 */
public class OneYearMoneyFragment extends BaseFragment {

    private View oneYearView;

    @Override
    public View generateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        oneYearView = View.inflate(mContext, R.layout.fragment_one_year_money, null);
        return oneYearView;
    }

    public static Fragment newInstance() {
        return new OneYearMoneyFragment();
    }
}

可下载源码:CSDN下载    GitHub下载

代码中自定义的折线图源码感谢博主:自定义折线图博主

发布了191 篇原创文章 · 获赞 105 · 访问量 36万+

猜你喜欢

转载自blog.csdn.net/xiayiye5/article/details/95619117