搜索github會發(fā)現(xiàn)已經(jīng)有很多相對成熟的圖表框架飒硅,但與我們拿到的ui效果圖總會有一些差別,然后就會陷入是把別人源碼下載下來修改之后作為已用涩笤,或是重新定義一個新控件的兩難境地。初拿到效果圖感覺還是比較復雜難以實現(xiàn),但是仔細觀察發(fā)現(xiàn)其實這就是一個列表,一個橫向列表蛋辈,一個可以用RecyclerView實現(xiàn)的列表属拾。
如果借助recyclerview的話,那么重點就在每個條目也就是柱狀圖每個柱子的實現(xiàn)冷溶。
public class BarGraphItem extends View {
private static final String TAG = "BarGraphView";
private Paint paint;
private int measuredWidth;
private int measuredHeight;
private double ratio;
private GradientDrawable gradientDrawable;
public BarGraphItem(Context context) {
super(context);
initPaint();
}
public BarGraphItem(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initPaint();
}
public BarGraphItem(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
//設置柱子的高度
public void setRatio(double ratio) {
this.ratio = ratio;
invalidate();
}
private void initPaint() {
paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(getResources().getColor(R.color.colorBarBack));
paint.setAntiAlias(true);
int colors[] = {getResources().getColor(R.color.colorBarColor), getResources().getColor(R.color.colorGradientGreen)};
gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP,colors)渐白;
//設置漸變色
gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
//設置頂部圓角
gradientDrawable.setCornerRadii(new float[]{15,15,15,15,0,0,0,0});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measuredWidth = getMeasuredWidth();
measuredHeight = getMeasuredHeight();
}
@Override
protected void onDraw(Canvas canvas) {
//畫柱子的背景色
canvas.drawRect(0, 0, measuredWidth, measuredHeight, paint);
//計算柱子實際高度
if (ratio != 0){
int ratioHeight = (int) (measuredHeight * ratio + 0.5);
//默認坐標原點在左上角,這里我們把畫布移到左上角
canvas.translate(0,measuredHeight - ratioHeight);
//x,y,w,h
gradientDrawable.setBounds(0,0,measuredWidth, ratioHeight);
gradientDrawable.draw(canvas);
}
}
}
代碼并不復雜逞频,可以看到最終效果有很多漸變色的運用纯衍,平時我們會用shape標簽繪制一些簡單的圖形,其中有一個gradient屬性可以幫我們實現(xiàn)漸變效果苗胀,那在代碼中可以直接使用GradientDrawable來實現(xiàn)同樣的效果。接下來在布局文件中使用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center_horizontal"
android:paddingTop="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:textSize="9dp"
android:text="--"
android:id="@+id/tv_amount_bar_graph"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RelativeLayout
android:id="@+id/rl_bar_graph"
android:layout_marginTop="8dp"
android:background="@drawable/shape_bar_graph"
android:layout_width="43dp"
android:layout_height="wrap_content">
<com.example.a14617.bargraphtest.view.BarGraphItem
android:layout_centerHorizontal="true"
android:id="@+id/bgi_bar_graph"
android:layout_gravity="center_horizontal"
android:layout_width="8dp"
android:layout_height="110dp" />
</RelativeLayout>
<TextView
android:textColor="#999999"
android:textSize="12dp"
android:gravity="center"
android:text="--"
android:background="#F9F9F9"
android:layout_marginTop="8dp"
android:id="@+id/tv_time_bar_graph"
android:layout_width="match_parent"
android:layout_height="40dp" />
<ImageView
android:id="@+id/iv_highest_bar_graph"
android:visibility="invisible"
android:src="@drawable/triangle_red_rose"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
因為柱狀圖整體的漸變背景并沒到底部,只是中間柱子的部分借帘,所以把這個背景的設置放到item的布局中剑按,也是用shape 標簽實現(xiàn)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="90"
android:startColor="@color/colorLightBlue"
android:endColor="@color/colorGradientWhite"
>
</gradient>
</shape>
標識最高點的紅色三角可以用png或其它格式的圖片,為了盡量縮減包體積也可以用layerlist標簽來實現(xiàn),
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/shape_id">
<!--正三角-->
<rotate
android:fromDegrees="45"
android:toDegrees="45"
android:pivotX="-40%"
android:pivotY="80%">
<shape android:shape="rectangle">
<solid android:color="@color/colorAccent"/>
<size android:width="8dp"
android:height="8dp"/>
</shape>
</rotate>
</item>
</layer-list>
準備工作已經(jīng)完成澜驮,接下來就可以愉快地進行recyclerview三步曲了陷揪。在xml布局文件中使用recyclerview(比較簡單代碼就省略了),然后在MainActivity中初始化recyclerview,
private void initRecyclerView() {
//先造幾條假數(shù)據(jù)
DecimalFormat df = new DecimalFormat("0.00");
int maxIndex = 0;
double max = 0;
for (int i = 1;i <= 10;i++){
IncomeDetailsBean incomeDetailsBean = new IncomeDetailsBean();
incomeDetailsBean.setDate(i + "點");
double random = Math.random();
//記錄最大值
if (random > max) {
max = random;
maxIndex = i - 1;
}
incomeDetailsBean.setIncome(df.format(random * 10) + "");
incomeDetailsBean.setRatio(random);
incomeDetails.add(incomeDetailsBean);
}
incomeDetails.get(maxIndex).setHighest(true);
//初始化recyclerview
layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
barGraphAdapter = new BarGraphAdapter(this,incomeDetails);
recyclerView.setAdapter(barGraphAdapter);
}
最后一步adapter杂穷,只看綁定控件這一部分悍缠,
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
//如果沒有數(shù)據(jù)會有幾條占位圖
if (incomeDetails == null || incomeDetails.size() == 0){
holder.barGraphItem.setRatio(0);
holder.tv_time.setText("--");
holder.tv_amount.setText("--");
if (View.VISIBLE == holder.iv_highest.getVisibility()) {
holder.iv_highest.setVisibility(View.INVISIBLE);
holder.tv_time.setTextSize(12);
holder.tv_time.setTextColor(Color.parseColor("#999999"));
}
holder.tv_amount.setTextColor(Color.LTGRAY);
}else {
//綁定數(shù)據(jù)
IncomeDetailsBean incomeDetailsBean = incomeDetails.get(position);
holder.barGraphItem.setRatio(incomeDetailsBean.getRatio());
holder.tv_time.setText(incomeDetailsBean.getDate());
holder.tv_amount.setText(incomeDetailsBean.getIncome());
holder.tv_amount.setTextColor(incomeDetailsBean.getRatio() == 0 ? Color.LTGRAY : context.getResources().getColor(R.color.colorAccent));
holder.iv_highest.setVisibility(incomeDetailsBean.isHighest() ? View.VISIBLE : View.INVISIBLE);
holder.tv_time.setTextSize(incomeDetailsBean.isHighest() ? 16 : 12);
holder.tv_time.setTextColor(incomeDetailsBean.isHighest() ? context.getResources().getColor(R.color.colorAccent) : Color.parseColor("#999999"));
}
//縮放動畫(x軸方向不變,y軸由0到1增長效果)
Animation animation = AnimationUtils.loadAnimation(context, R.anim.scale_item);
holder.barGraphItem.startAnimation(animation);
}
縱向縮放動畫
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="700"
android:fromXScale="1.0"
android:fromYScale="0.0"
android:pivotY="100%"
android:toXScale="1.0"
android:toYScale="1.0">
</scale>
最后是圖中用到的配色資源
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">@android:color/holo_green_dark</color>
<color name="colorLightBlue">#3399cc00</color>
<color name="colorBarColor">#ff669900</color>
<color name="colorBarBack">#5099cc00</color>
<color name="colorGradientWhite">#0599cc00</color>
<color name="colorGradientGreen">#40669900</color>
</resources>
整個過程沒有很生僻的點耐量,基本是對Android Drawable飞蚓、動畫及簡單自定義控件這些基本技能的綜合運用。
希望對你有所幫助廊蜒,喜歡記得點贊喔玷坠。
作者簡介:現(xiàn)就職于甜橙金融信息技術部蜗搔,負責安卓客戶端開發(fā)工作。