MPAndroidChart项目实战(九)——自定义带文字分段堆积柱状图

本文出自:http://blog.csdn.net/dt235201314/article/details/78085430

一丶效果展示

《MPAndroidChart项目实战(九)——自定义带文字分段堆积柱状图》 11.gif

二丶需求分析及技术点

  • 1.显示每个季度产业(收入)占比,低于5%不显示

与上篇类似,将View换成textView即可,高度小于5%不显示

  • 2.产业颜色与下面显示显色一致,且严格按照设计图颜色

使用map,键值对,一个产业名对应一个颜色

  • 3.柱状图可滑动

同上篇博客

http://blog.csdn.net/dt235201314/article/details/77534468

  • 4.带有动画效果

用白色View遮盖,由下往上收起,形成动画效果

  • 5.图解

《MPAndroidChart项目实战(九)——自定义带文字分段堆积柱状图》 image.png

三丶看代码

xml

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:text="自定义带文字分段堆积柱状图:" />

<include layout="@layout/item_text_bar" />

<Button
    android:id="@+id/bt_refresh2"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_margin="10dp"
    android:gravity="center"
    android:text="刷新" />

item_text_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp">

        <LinearLayout
            android:id="@+id/bg_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:orientation="vertical">

            <LinearLayout
                android:id="@+id/item0"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="12dp">

                <TextView
                    android:id="@+id/tv_num5_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="100%" />

                <View
                    android:id="@+id/left_base_line_text"
                    android:layout_width="match_parent"
                    android:layout_height="2dp"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="20dp"
                    android:background="@drawable/view_dash_line" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="12dp">

                <TextView
                    android:id="@+id/tv_num4_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text=" 80%" />

                <View
                    android:layout_width="match_parent"
                    android:layout_height="2dp"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="20dp"
                    android:background="@drawable/view_dash_line" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="12dp">

                <TextView
                    android:id="@+id/tv_num3_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text=" 60%" />

                <View
                    android:layout_width="match_parent"
                    android:layout_height="2dp"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="20dp"
                    android:background="@drawable/view_dash_line" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="12dp">

                <TextView
                    android:id="@+id/tv_num2_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text=" 40%" />

                <View
                    android:layout_width="match_parent"
                    android:layout_height="2dp"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="20dp"
                    android:background="@drawable/view_dash_line" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="12dp">

                <TextView
                    android:id="@+id/tv_num1_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text=" 20%" />

                <View
                    android:layout_width="match_parent"
                    android:layout_height="2dp"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="20dp"
                    android:background="@drawable/view_dash_line" />
            </LinearLayout>

            <View
                android:id="@+id/base_line_text"
                android:layout_width="match_parent"
                android:layout_height="0.5dp"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="20dp"
                android:background="#E6E6E6" />
        </LinearLayout>

        <HorizontalScrollView
            android:id="@+id/bar_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="70dp"
            android:scrollbars="none">

            <com.barchart.mpchartdemo.view.TextBarGroupView
                android:id="@+id/bar_group_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </HorizontalScrollView>
    </RelativeLayout>

    <com.nex3z.flowlayout.FlowLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/container2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:childSpacing="auto"
        app:childSpacingForLastRow="align"
        android:padding="10dp"
        app:rowSpacing="8dp" />

</LinearLayout>

与上篇背景布局类似,HorizontalScrollView控制左右滑动,动态添加柱状图,以及FlowLayout(自适应布局显示下标)
说一说FlowLayout

bulid添加依赖便可以使用

compile ‘com.nex3z:flow-layout:0.1.4’
属性讲解参看其他文章:
之前写的文章:
Android删除添加标签(FlowLayout案例)
自定义ViewGroup
TextBarGroupView.java

public class TextBarGroupView extends LinearLayout {
    public TextBarGroupView(Context context) {
        super(context);
        setOrientation(HORIZONTAL);
    }

    public TextBarGroupView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setOrientation(HORIZONTAL);
    }

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


    String other = "其它";

    public void init(final List<TextBarDataEntity.Record> datas, final int barHeight, FlowLayout sourceContainer) {
        removeAllViews();
        if (datas == null || datas.isEmpty()) {
            return;
        }
        List<Integer> colors = new ArrayList<>();
        colors.add(Color.parseColor("#3fa0ff"));
        colors.add(Color.parseColor("#98b3e5"));
        colors.add(Color.parseColor("#d7546d"));
        colors.add(Color.parseColor("#51d4c4"));
        colors.add(Color.parseColor("#6d43cc"));
        colors.add(Color.parseColor("#ffb256"));
        colors.add(Color.parseColor("#69390e"));
        colors.add(Color.parseColor("#7ab024"));
        colors.add(Color.parseColor("#a7d0c8"));
        colors.add(Color.parseColor("#a29258"));
        colors.add(Color.parseColor("#297350"));
        colors.add(Color.parseColor("#eebdc7"));
        colors.add(Color.parseColor("#bb59d0"));

        List<TextBarDataEntity.Record.Source> allSourceList = new ArrayList<>();
        List<String> allSourceNameList = new ArrayList<>();
        Map<String, Integer> nameColorMap = new HashMap<>();
        final int lineHeight = (int) getResources().getDisplayMetrics().density * 1;
        for (int i = 0; i < datas.size(); i++) {
            //加载所有来源,去重复
            List<TextBarDataEntity.Record.Source> sourceList = datas.get(i).getSourceList();
            if (sourceList != null && !sourceList.isEmpty()) {
                int j = 0;
                for (TextBarDataEntity.Record.Source entry : sourceList) {
                    if (!nameColorMap.containsKey(entry.getSourceName())) {
                        Integer colorValue = colors.get(j % colors.size());
                        if (!nameColorMap.containsValue(colorValue)) {
                            nameColorMap.put(entry.getSourceName(), colorValue);
                        } else {
                            int index=colors.indexOf(colorValue);
                            for(int x=index;x<colors.size();x++){
                                Integer colorValue1 = colors.get(x % colors.size());
                                if(!nameColorMap.containsValue(colorValue1)){
                                    nameColorMap.put(entry.getSourceName(), colorValue1);
                                    break;
                                }
                            }
                        }
                    }
                    if (!allSourceNameList.contains(entry.getSourceName())) {
                        allSourceNameList.add(entry.getSourceName());
                        allSourceList.add(entry);
                    }
                    j++;
                }
                Collections.reverse(sourceList);
            }
        }
        initAllSourceLayout(allSourceList, sourceContainer, nameColorMap);
        for (int i = 0; i < datas.size(); i++) {
            final View item = LayoutInflater.from(getContext()).inflate(R.layout.text_source_item_group, this, false);
            final TextBarView barView = (TextBarView) item.findViewById(R.id.barview);
            barView.init(datas.get(i), barHeight + lineHeight, nameColorMap);
            ((TextView) item.findViewById(R.id.time)).setText(datas.get(i).getTimeScale());
            item.findViewById(R.id.time).setMinimumWidth((int) (getResources().getDisplayMetrics().density*80));
            if (i == 0) {
                final LayoutParams lp = (LayoutParams) item.getLayoutParams();
                lp.leftMargin = 0;
                addView(item, lp);
            } else {
                addView(item);
            }
            final View coverView = item.findViewById(R.id.cover);
            ViewGroup.LayoutParams lp = coverView.getLayoutParams();
            lp.height = barHeight;
            coverView.setLayoutParams(lp);
            //动画
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    final int initCoverHeight = coverView.getHeight();
                    ObjectAnimator anim = ObjectAnimator.ofFloat(coverView, "translationY", 0, -initCoverHeight);
                    anim.setDuration(1000);
                    anim.start();
                }
            }, (i + 1) * 500);
        }

        getViewTreeObserver().

                addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        getViewTreeObserver().removeOnPreDrawListener(this);
                        HorizontalScrollView.LayoutParams lp = (HorizontalScrollView.LayoutParams) getLayoutParams();
                        lp.topMargin = barHeight / 5 / 2 - lineHeight;
                        setLayoutParams(lp);
                        return false;
                    }
                });

    }

    private void initAllSourceLayout(List<TextBarDataEntity.Record.Source> list, FlowLayout sourceContainer, Map<String, Integer> nameColorMap) {
        sourceContainer.removeAllViews();
        for (TextBarDataEntity.Record.Source source : list) {
            View item = LayoutInflater.from(getContext()).inflate(R.layout.pie_lable_item, sourceContainer, false);
            GradientDrawable bg = (GradientDrawable) item.findViewById(R.id.icon).getBackground();
            TextView txt = (TextView) item.findViewById(R.id.txt);
            bg.setColor(nameColorMap.get(source.getSourceName()));
            item.findViewById(R.id.icon).setBackground(bg);
            txt.setText(source.getSourceName());
            sourceContainer.addView(item);
        }
    }

}
  • init()方法
    第一个参数 List<TextBarDataEntity.Record> datas 为实体类展示数据

造数据核心代码:

public List<Record>  parseData(){
    recordList = new ArrayList<>();
    Random r = new Random();
    for (int i= 0;i<=4;i++){
        Record record = new Record();
        record.setTimeScale("第" + (i+1) + "周");
        List<Record.Source> list = new ArrayList<>();
        for (int j=0; j<= 3; j++ ){
            Record.Source source = new Record.Source();
            source.setSourceName("TCL第" + (j+1) + "产业");
            source.setSourceNum(r.nextInt(10*(j+1)));
            list.add(source);
        }
        Record.Source source = new Record.Source();
        source.setSourceName("TCL第5产业");
        int sourceNum = 100 - list.get(0).getSourceNum() - list.get(1).getSourceNum()
                - list.get(2).getSourceNum() - list.get(3).getSourceNum();
        source.setSourceNum(sourceNum);
        list.add(source);
        record.setSourceList(list);
        recordList.add(record);
    }
    return recordList;
}

随机数10,20,30,40以内,最后100前去前面的数,刚好100,除以100为占比数

第二个参数 int barHeight 高度
这里为柱状图高度,5倍上面的那个横线布局 id item0 高度

第三个参数 FlowLayout sourceContainer定义view容器
动态添加子view,“第1产业”

看for循环(是不是很牛逼的感觉)

运用map,一个产业名对应一个颜色数值

注意了,为什么后面要循环遍历,因为各个季度产业数可能不同,例:季度一(1 2 3 4产业)季度二(3 4 5 6产业)

Collections.reverse(sourceList);
这一句,逆序展示业务要求(后台排好大小顺序)

  • initAllSourceLayout()方法
    第一个参数 List<TextBarDataEntity.Record.Source> list 产业名称
    第二个参数 FlowLayout sourceContainer 容器

pie_lable_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <View
        android:id="@+id/icon"
        android:layout_width="10dp"
        android:layout_height="10dp"
        android:background="@drawable/source_small" />

    <TextView
        android:id="@+id/txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="12sp" />
</LinearLayout>

第三个参数 Map<String, Integer> nameColorMap 产业名→颜色
看代码,这个方法就是添加效果图下面的,产业1 2 3…了

第二个for循环(添加自定义View带文字柱状图 + 动画实现)

text_source_item_group.xml(这个带文字柱状的布局)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/text_bar_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/time"
        android:layout_alignRight="@+id/time"
        android:paddingLeft="10dp"
        android:paddingRight="10dp">

        <com.barchart.mpchartdemo.view.TextBarView
            android:id="@+id/barview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></com.barchart.mpchartdemo.view.TextBarView>

        <View
            android:id="@+id/cover"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/white"
            android:visibility="visible" />
    </FrameLayout>

    <TextView
        android:id="@+id/time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/text_bar_container"
        android:layout_marginTop="15dp"
        android:gravity="center"
        android:text="17.01.01-17.03.31"
        android:textSize="12sp" />
</RelativeLayout>

这里FrameLayout,用一个白色View遮住TextBarView收起形成动画
TextBarView.java

public class TextBarView extends LinearLayout {
    public TextBarView(Context context) {
        super(context);
        setOrientation(VERTICAL);
    }

    public TextBarView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setOrientation(VERTICAL);
    }

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

    DecimalFormat format = new DecimalFormat("##.##");
    PopupWindow popupWindow;
    View popView;

    public void init(TextBarDataEntity.Record record, int height, Map<String, Integer> nameColorMap) {
        if (record.getSourceList() == null && record.getSourceList().isEmpty()) {
            return;
        }
        popView = LayoutInflater.from(getContext()).inflate(
                R.layout.pop_bg, null);
        //计算空白填充部分占比
        double blankScale = 1;
        for (int i = 0; i < record.getSourceList().size(); i++) {
            blankScale -= record.getSourceList().get(i).getSourceNum();
        }
//        if (blankScale==1) {
//            TextView item = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.negative_sentiment_source_item_txt, this, false);
//            ViewGroup.LayoutParams lp = item.getLayoutParams();
//            lp.height = (int) (blankScale * height);
//            addView(item);
//            return;
//        }

        for (int i = 0; i < record.getSourceList().size(); i++) {
            final TextView item = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.text_source_item_txt, this, false);
            final TextBarDataEntity.Record.Source source = record.getSourceList().get(i);
            item.setText(source.getSourceNum() >= 5 ? format.format(source.getSourceNum()) + "%" : "");
            GradientDrawable bg = (GradientDrawable) getResources().getDrawable(R.drawable.n_s_bar_bg);
            bg.setColor(nameColorMap.get(source.getSourceName()));
            item.setBackground(bg);
            ViewGroup.LayoutParams lp = item.getLayoutParams();
            lp.height = (int) (source.getSourceNum()/(double)100 * height);
            addView(item, lp);
        }
    }

    private void showPop(final View view) {
        if (popupWindow != null)
            popupWindow.dismiss();
        popupWindow = null;
        popupWindow = new PopupWindow(popView,
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, false);
        popupWindow.setBackgroundDrawable(new BitmapDrawable());
        popupWindow.setOutsideTouchable(true);
        popupWindow.showAsDropDown(view, view.getWidth() / 2, view.getHeight() / 2);
    }
}

这里与上一篇的BarView类似,带文字柱状图就没必要再点击弹框了
直接看init()方法的for循环

text_source_item_txt.xml (就是一个textview)

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/gray3"
    android:gravity="right|center_vertical"
    android:paddingRight="5dp"
    android:text="50%"
    android:textColor="@android:color/white"
    android:textSize="12sp" />

item.setText(source.getSourceNum() >= 5 ? format.format(source.getSourceNum()) + “%” : “”);
小于5%,不显示
添加bg

lp.height = (int) (source.getSourceNum()/(double)100 * height);
这里表示每个text站总高度的百分比,必须加上double
白色view揭盖动画
获取高度运用ObjectAnimator收起
给自定义带文字布局添加topMargin

getViewTreeObserver().
动态测量高度

四丶往期文章

MPAndroidChart常见设置属性(一)——应用层
MPAndroidChart项目实战(一)——实现对比性柱状图
MPAndroidChart项目实战(二)——双平滑曲线(双折线图)和MarkView实现
MPAndroidChart项目实战(三)——饼状图实现和文字重合问题解决
MPAndroidChart项目实战(四)——柱状图实现及X轴文字不显示问题和柱状图上显示文字
MPAndroidChart X轴文字斜着显示
MPAndroidChart项目实战(五)——组合图实现趋势图
MPAndroidChart项目实战(六)——自定义1MPAndroidChart滑动冲突解决(搞不定产品设计师就只能搞自己)
MPAndroidChart项目实战(七)——自定义横向柱状图
MPAndroidChart项目实战(八)——自定义分段堆积柱状图
MPAndroidChart项目实战(九)——自定义带文字分段堆积柱状图

    原文作者:天一方蓝
    原文地址: https://www.jianshu.com/p/eaba4cbf2bbf
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞