看下效果图:
先看下布局:
TabLayout的简单使用:
当选项卡过少时候设置填充全屏
app:tabGravity="fill"
设置下面切换选项卡的小滑片颜色
app:tabIndicatorColor="#8B1C21"
设置下面切换选项卡的小滑片高度
app:tabIndicatorHeight="1dp"
app:tabMaxWidth="0dp"
app:tabSelectedTextColor="#8B1C21"
再看下首页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 xValues;
//y轴数据集合
List 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 xValues;
private List 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 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 values) {
this.xValues = values;
}
public void setYValues(List 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下载
代码中自定义的折线图源码感谢博主:自定义折线图博主