教你使用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章