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

来源:互联网 发布:java可变参数判断为空 编辑:程序博客网 时间:2024/05/20 01:10

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

一丶效果展示


二丶需求分析及技术点

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

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

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

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

3.柱状图可滑动

同上篇博客

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

4.带有动画效果

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

5.图解


三丶看代码

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

之前的文章:

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循环(是不是很牛逼的感觉,cnm产品要求的)

运用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项目实战(九)——自定义带文字分段堆积柱状图 

五丶跪求关注下载源码,200粉小目标
欢迎关注我的博客及微信公众号,后面会给大家带来更多相关MPAndroidChart无法解决的仿MPAndroidChart图标自定义控件
源码下载记得顺便Star哦~
下载链接:https://github.com/JinBoy23520/MPAndroidChartDemoByJin

阅读全文
3 0
原创粉丝点击