教你使用RecyclerView画柱状图
最近项目要用到”变色且能横向滑动的柱状图”,如上图,首先想到的是网上找一篇柱状图来改,但看完几篇文章后内心是崩溃的,因为不光要画这五颜六色的柱状图,还要将其用在RecyclerView中,使其随着RecyclerView能上下滚动,这就可能会产生滑动冲突,还得自己处理冲突,虽然咱也会处理冲突,但项目要赶时间就放弃了这种方法。二是想到既然是柱状图,我们可否用ProgressBar,只需要用垂直的ProgressBar作为横向的RecyclerView的item即可,而用过RecyclerView的都知道,横向的RecyclerView与纵向的RecyclerView是不会有滑动冲突的。SO DO IT !!!
画柱状图坐标轴
既然是柱状图,那首先是画x,y轴了,x,y轴我们用view的宽高为0.5dp的直线来画如下,然后再放一个RecyclerView就可以了,(R.layout.activity_main)代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/chart"
android:layout_width="match_parent"
android:layout_height="200dp">
<View
android:id="@+id/view_y"
android:layout_width="0.5dp"
android:layout_height="match_parent"
android:layout_marginBottom="20dp"
android:layout_marginLeft="40dp"
android:layout_marginTop="30dp"
android:background="#666" />
<View
android:id="@+id/view_x"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignBottom="@+id/view_y"
android:layout_marginLeft="40dp"
android:background="#666" />
<TextView
android:id="@+id/text_low"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="30dp"
android:layout_marginLeft="5dp"
android:gravity="right"
android:minWidth="20dp"
android:text="低"
android:drawablePadding="2dp"
android:textSize="10sp" />
<TextView
android:id="@+id/text_mid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="95dp"
android:layout_marginLeft="5dp"
android:gravity="right"
android:minWidth="20dp"
android:text="中"
android:drawablePadding="2dp"
android:textSize="10sp" />
<TextView
android:id="@+id/text_hi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="150dp"
android:layout_marginLeft="5dp"
android:gravity="right"
android:minWidth="20dp"
android:drawablePadding="2dp"
android:text="高"
android:textSize="10sp" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rcv"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_marginLeft="45dp">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
画柱状图的柱子
其次就是我们的重点,adapter的item的布局。上图柱状图文字在下,进度在上,因此我们可以用相对布局将ProgressBar放置在TextView的上方即可,代码如下:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="50dp"
android:id="@+id/rl_chart_item"
android:layout_height="match_parent">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="11sp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:singleLine="true"
android:maxLines="1"
android:ellipsize="end"
android:text="name"
android:textColor="#999"/>
<ProgressBar
android:id="@+id/pb_vertical"
android:layout_width="20dp"
android:layout_height="150dp"
android:indeterminateOnly="false"
android:layout_above="@+id/name"
android:max="100"
android:progress="60"
android:layout_alignParentBottom="true"
android:layout_marginBottom="21dp"
android:layout_centerHorizontal="true"
android:progressDrawable="@drawable/progress_normal" />
</RelativeLayout>
这儿可能部分使用ProgressBar较少的小伙伴会有疑问,ProgressBar不是水平的吗,怎么在你这儿就变成垂直的了,其实关键在于progressDrawable我们将drawable的剪裁方向设置为垂直且从下往上剪裁,progress_normal代码如下,代码很简单就不解释了:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="#00000000" />
<corners android:radius="5dip" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip
android:clipOrientation="vertical"
android:gravity="bottom">
<shape>
<solid android:color="#00000000" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<!-- 定义ClipDrawable的剪裁方向为垂直,gravity="bottom" 从下往上 -->
<clip
android:clipOrientation="vertical"
android:gravity="bottom">
<shape android:shape="rectangle">
<solid android:color="#7ddd5c" />
</shape>
</clip>
</item>
</layer-list>
实现带有柱状图的adapter
接下来就很简单了,写一个使用上面的item布局的adapter即可。这儿需要注意的是,柱状图是多种颜色的,因此为了让ProgressBar有多钟颜色,我们是通过为adapter的ProgressBar设置不同drawable来实现的。代码如下,代码很简单,写过adapter的都能看懂,不再进一步解释:
public class BarAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final LayoutInflater mLayoutInflater;
private List<ChartData> mDatas;
private float mLowStandard;
private float mHighStandard;
private Context mContext;
private int indexSelected = -1;
public BarAdapter(Context context, float lowStandard, float highStandard,List<ChartData> mDatas) {
mLayoutInflater = LayoutInflater.from(context);
mContext = context;
mLowStandard = lowStandard;
mHighStandard = highStandard;
this.mDatas=mDatas;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewHolder holder = new ViewHolder(mLayoutInflater.inflate(R.layout.bar_chart_item, parent, false));
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final ChartData data = mDatas.get(position);
TextView tv_name = ((ViewHolder) holder).getView(R.id.name);
if(position == indexSelected){ //当用户选中时改变文字颜色
tv_name.setTextColor(Color.RED);
} else {
tv_name.setTextColor(Color.GRAY);
}
ProgressBar progressBar = ((ViewHolder) holder).getView(R.id.pb_vertical);
float pro = data.getProgress();
progressBar.setVisibility(View.VISIBLE);
if (pro > mHighStandard) {//大于“高”时使用一种颜色的Drawable
progressBar.setProgressDrawable(ContextCompat.getDrawable(mContext, R.drawable.progress_high));
} else if (pro < mLowStandard) {
progressBar.setProgressDrawable(ContextCompat.getDrawable(mContext, R.drawable.progress_low));
} else {//小于“低”时使用一种颜色的Drawable
progressBar.setProgressDrawable(ContextCompat.getDrawable(mContext, R.drawable.progress_normal));
}
progressBar.setProgress(Math.round(pro));
tv_name.setText(data.getName());
}
@Override
public int getItemCount() {
if (mDatas == null) {
return 0;
}
return mDatas.size();
}
public void setSelected(int position) {
if(indexSelected == -1){
indexSelected = position;
notifyItemChanged(indexSelected);
} else {
int a = indexSelected;
indexSelected = position;
notifyItemChanged(indexSelected);
notifyItemChanged(a);
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View view) {
super(view);
}
public void setText(int viewId, String text){
TextView tv = (TextView) itemView.findViewById(viewId);
tv.setText(text);
}
public <T extends View> T getView(int viewId){
return (T) itemView.findViewById(viewId);
}
}
}
使用柱状图
其次就是使用我们的adapter,这就更简单了,我直接给出代码了。
public class MainActivity extends AppCompatActivity {
protected View viewY;
protected View viewX;
protected TextView textLow;
protected TextView textMid;
protected TextView textHi;
protected RecyclerView rcv;
protected RelativeLayout chart;
BarAdapter mdapter;
List<ChartData> mDatas=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
initView();
initData();
initAdapter();
}
private void initView() {
viewY = (View) findViewById(R.id.view_y);
viewX = (View) findViewById(R.id.view_x);
textLow = (TextView) findViewById(R.id.text_low);
textMid = (TextView) findViewById(R.id.text_mid);
textHi = (TextView) findViewById(R.id.text_hi);
rcv = (RecyclerView) findViewById(R.id.rcv);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
rcv.setHasFixedSize(true);
rcv.setLayoutManager(linearLayoutManager);
chart = (RelativeLayout) findViewById(R.id.chart);
}
private void initData(){
for (int i=1;i<=25;i++){
ChartData item=new ChartData();
item.setName("第"+i+"个");
item.setProgress(i*4);
mDatas.add(item);
}
}
private void initAdapter(){
mdapter = new BarAdapter(this, 25, 75,mDatas);
rcv.setAdapter(mdapter);
}
}
总结
到此我们一个“高难度”的柱状图就这么简单的实现了。其实很多看似复杂的控件都与这个柱状图一样,是可以通过现有控件简单的组合来实现的,不必非得你会自定义,就看平时有没有多角度的去积累。
代码下载
上次代码上传到csdn,有人无法下载代码,就说我是骗积分的,这次专门注册了github的,不会被说了吧。
https://github.com/yxkrrhx/histogramdemo