天天看点

教你使用RecyclerView画柱状图教你使用RecyclerView画柱状图

教你使用RecyclerView画柱状图

教你使用RecyclerView画柱状图教你使用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

继续阅读