本文出自: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
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="自定义带文字分段堆积柱状图:" />
layout="@layout/item_text_bar" />
item_text_bar.xml
xml version="1.0" encoding="utf-8"?>与上篇背景布局类似,HorizontalScrollView控制左右滑动,动态添加柱状图,以及FlowLayout(自适应布局显示下标)xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp"> android:id="@+id/bg_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:orientation="vertical"> android:id="@+id/item0" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="12dp"> android:id="@+id/tv_num5_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="100%" /> 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" /> android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="12dp"> android:id="@+id/tv_num4_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 80%" /> android:layout_width="match_parent" android:layout_height="2dp" android:layout_gravity="center_vertical" android:layout_marginLeft="20dp" android:background="@drawable/view_dash_line" /> android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="12dp"> android:id="@+id/tv_num3_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 60%" /> android:layout_width="match_parent" android:layout_height="2dp" android:layout_gravity="center_vertical" android:layout_marginLeft="20dp" android:background="@drawable/view_dash_line" /> android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="12dp"> android:id="@+id/tv_num2_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 40%" /> android:layout_width="match_parent" android:layout_height="2dp" android:layout_gravity="center_vertical" android:layout_marginLeft="20dp" android:background="@drawable/view_dash_line" /> android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="12dp"> android:id="@+id/tv_num1_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 20%" /> android:layout_width="match_parent" android:layout_height="2dp" android:layout_gravity="center_vertical" android:layout_marginLeft="20dp" android:background="@drawable/view_dash_line" /> 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" /> android:id="@+id/bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="70dp" android:scrollbars="none"> android:id="@+id/bar_group_text" android:layout_width="wrap_content" android:layout_height="wrap_content" /> 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" />
说一说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 Listdatas, final int barHeight, FlowLayout sourceContainer) { removeAllViews(); if (datas == null || datas.isEmpty()) { return; } List 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 allSourceList = new ArrayList<>(); List allSourceNameList = new ArrayList<>(); Map , Integer> nameColorMap = new HashMap<>(); final int lineHeight = (int) getResources().getDisplayMetrics().density * 1; for (int i = 0; i < datas.size(); i++) { //加载所有来源,去重复 List 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 ;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 list, FlowLayout sourceContainer, Map , 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
造数据核心代码:
public List随机数10,20,30,40以内,最后100前去前面的数,刚好100,除以100为占比数parseData(){ recordList = new ArrayList<>(); Random r = new Random(); for (int i= 0;i<=4;i++){ Record record = new Record(); record.setTimeScale("第" + (i+1) + "周"); List 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; }
第二个参数 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
第二个参数 FlowLayout sourceContainer 容器 pie_lable_item.xml
xml version="1.0" encoding="utf-8"?>第三个参数 Mapxmlns: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"> android:id="@+id/icon" android:layout_width="10dp" android:layout_height="10dp" android:background="@drawable/source_small" /> android:id="@+id/txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" />
看代码,这个方法就是添加效果图下面的,产业1 2 3...了
第二个for循环(添加自定义View带文字柱状图 + 动画实现)
text_source_item_group.xml(这个带文字柱状的布局)
xml version="1.0" encoding="utf-8"?>这里FrameLayout,用一个白色View遮住TextBarView收起形成动画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"> 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"> android:id="@+id/barview" android:layout_width="match_parent" android:layout_height="wrap_content"> android:id="@+id/cover" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:visibility="visible" /> 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" />
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这里与上一篇的BarView类似,带文字柱状图就没必要再点击弹框了, 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); } }
直接看init()方法的for循环
text_source_item_txt.xml (就是一个textview)
xml version="1.0" encoding="utf-8"?>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