美文网首页精选案例
android用recyclerview撸一个折线图

android用recyclerview撸一个折线图

作者: X_Sation | 来源:发表于2017-11-13 20:12 被阅读48次

    以前觉得用安卓画一个折线图好难,当然github上也有很多方便的框架,几行代码就可以实现一个折线图,而且各种样式都有,但是别人的框架毕竟是为了满足大众的需求,没有专一性,正好前几天接了一个私活,要用到折线图,时间也比较充足,就试着自己写一个玩玩。

    经过仔细分析小米天气中的折线图后,发现其实写一个折线图是非常简单的。先上两张小米天气的折线图


    图一 图二

    如果我告诉你这两个图的实现思路是一样的,而且都很容易,你会不会很震惊。
    其实是第二种图中“今天”那一项数据给提供了思路,没错,recyclerview。
    拿第一张图片来分析


    image.png

    黑色部分框起来的就当做一个item,首先我们要把所有数据中的最大值和最小值取出来,如红线画住的部分,然后确定一个区间(y坐标),表示我们所有的点都要在这个区间内显示。图中当值为176表示最大,在第一个红线那个高度,44表示最小,在第二条直线那个高度,这两个高度是我们指定的,那么70这个值对应的高度很明显就可以计算出来,103的高度也可以计算出来,70和103的中间值,也就是图中第一个红圈圈住的地方的高度也可以确定出来,同理,第三个小圆圈也知道了,然后第一个小圆圈和第二个连线,第二个和第三个连线,这就画好了一个item的折线。多个item连接起来就是上面的效果。

    第二张图思路也是一样的,只不过在一个item上画了两个折线,如图中黑线框住的区域


    image.png

    思路就是这样,是不是很简单了,当然前提要掌握view的绘制,因为每一个item都是继承自view,然后绘制的。

    上代码,总共就三个java文件,也不用布局文件,直接导进去就可以看效果。记得依赖recyclerview的库哦。代码注释也写得比较清楚,主要代码就是那个item

    item的代码
    
    package com.xk.linedemo;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.Log;
    import android.view.View;
    
    
    /**
     * Created by xuekai on 2016/11/10.
     */
    
    public class Item extends View {
        private int maxValue;//最高值
        private int minValue;//最低值
        private int currentValue = 15;//当前值
        private int lastValue = 10;//上一个值
        private int nextValue = 10;//下一个值
        private Paint mPaint;
        private int viewHeight;
        private int viewWidth;
        private int pointTopY = 130;//最高点的Y坐标 130
        private int pointBottomY = 255;//最低点的Y坐标 255
        private int pointX;//所有点的x坐标
        private int pointY;//当前点的Y
    
        private boolean drawLeftLine = true;//是否画左边的线
        private boolean drawRightLine = true;//是否画右边的线
    
        public Item(Context context) {
            super(context);
            init();
            setBackgroundColor(0xffffffff);
        }
    
        public void setCurrentValue(int currentValue) {
            this.currentValue = currentValue;
            invalidate();
        }
    
        public void setMaxValue(int maxValue) {
            this.maxValue = maxValue;
        }
    
        public void setMinValue(int minValue) {
            this.minValue = minValue;
        }
    
        private void init() {
            mPaint = new Paint();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            initValues();
        }
    
        private void initValues() {
            viewHeight = getMeasuredHeight();
            viewWidth = getMeasuredWidth();
            pointX = viewWidth / 2;
    
        }
    
    
        @Override
        public void draw(Canvas canvas) {
            super.draw(canvas);
            pointY = (int) ((pointBottomY - pointTopY) * 1f / (maxValue - minValue) * (maxValue - currentValue + minValue) + pointTopY);
    
            drawLine(canvas);
            drawGraph(canvas);
            drawPoint(canvas);
            drawValue(canvas);
    
            Log.e("Item", "" + lastValue + " " + currentValue + " " + nextValue);
        }
    
        /**
         * 画数字
         *
         * @param canvas
         */
        private void drawValue(Canvas canvas) {
            mPaint.setTextSize(20);
            mPaint.setColor(Color.parseColor("#ff333333"));
            mPaint.setStrokeWidth(0);
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setTextAlign(Paint.Align.CENTER);
            Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
            float baseLine1 = pointY - fontMetrics.bottom * 4;
            canvas.drawText(currentValue + "", viewWidth / 2, baseLine1, mPaint);
        }
    
        public void setlastValue(int lastValue) {
            this.lastValue = lastValue;
        }
    
        public void setNextValue(int nextValue) {
            this.nextValue = nextValue;
        }
    
        /**
         * 画折线
         *
         * @param canvas
         */
        private void drawGraph(Canvas canvas) {
    
            mPaint.setPathEffect(null);
    
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(0xff24C3F1);
            mPaint.setStrokeWidth(3);
            mPaint.setAntiAlias(true);
            if (drawLeftLine) {
                float middleValue = currentValue - (currentValue - lastValue) / 2f;
                float middleY = ((pointBottomY - pointTopY) * 1f / (maxValue - minValue) * (maxValue - middleValue + minValue) + pointTopY);
                canvas.drawLine(0, middleY, pointX, pointY, mPaint);
            }
            if (drawRightLine) {
                float middleValue = currentValue - (currentValue - nextValue) / 2f;
                float middleY = ((pointBottomY - pointTopY) * 1f / (maxValue - minValue) * (maxValue - middleValue + minValue) + pointTopY);
                canvas.drawLine(pointX, pointY, viewWidth, middleY, mPaint);
            }
        }
    
        /**
         * 画数字下面的小圆圈
         *
         * @param canvas
         */
        private void drawPoint(Canvas canvas) {
            mPaint.setColor(0xffffffff);
            mPaint.setPathEffect(null);
    
            mPaint.setStrokeWidth(2);
            mPaint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(pointX, pointY, 10, mPaint);
            mPaint.setColor(0xff24C3F1);
            mPaint.setStrokeWidth(2);
            mPaint.setStyle(Paint.Style.STROKE);
            canvas.drawCircle(pointX, pointY, 5, mPaint);
        }
    
        /**
         * 画最下面的一条线
         *
         * @param canvas
         */
        private void drawLine(Canvas canvas) {
            mPaint.setColor(0xffD3D3D3);
            mPaint.setPathEffect(null);
    
            mPaint.setStrokeWidth(2);
            mPaint.setStyle(Paint.Style.FILL);
            if (drawLeftLine) {
                canvas.drawLine(0, 500, viewWidth / 2, 500, mPaint);//这两个值表示下面那条横线的Y坐标(第二四个参数)
    
            }
            if (drawRightLine) {
                canvas.drawLine(viewWidth / 2, 500, viewWidth, 500, mPaint);//这两个值表示下面那条横线的Y坐标(第二四个参数)
    
            }
        }
    
        public void setDrawLeftLine(boolean drawLeftLine) {
            this.drawLeftLine = drawLeftLine;
        }
    
        public void setDrawRightLine(boolean drawRightLine) {
            this.drawRightLine = drawRightLine;
        }
    
    
    }
    
    
    
    折线view的代码
    package com.xk.linedemo;
    
    import android.content.Context;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.view.ViewGroup;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * Created by xk on 2016/11/9 22:14.
     */
    
    public class BrokenLineView extends RecyclerView {
    
        private int maxValue;
        private int minValue;
        private List<Integer> data = new ArrayList<>();
        private Adapter adapter;
    
        public BrokenLineView(Context context) {
            super(context);
            init();
        }
    
    
        private void init() {
            setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
            adapter = new Adapter();
            setAdapter(adapter);
        }
    
        public void setData(List<Integer> d) {
            if (data != null) {
                data.clear();
                data.addAll(d);
                Collections.sort(d);
                minValue = d.get(0);
                maxValue = d.get(d.size() - 1);
                adapter.notifyDataSetChanged();
            }
        }
    
        class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
    
    
            @Override
            public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                Item item = new Item(getContext());
                item.setMinValue(minValue);
                item.setMaxValue(maxValue);
                LayoutParams layoutParams = new LayoutParams(200, ViewGroup.LayoutParams.MATCH_PARENT);//这个数字表示每一个item的宽度
                item.setLayoutParams(layoutParams);
                return new ViewHolder(item);
            }
    
            @Override
            public void onBindViewHolder(ViewHolder holder, int position) {
                if (position == 0) {
                    holder.item.setDrawLeftLine(false);
                } else {
                    holder.item.setDrawLeftLine(true);
                    holder.item.setlastValue((data.get(position - 1)));
                }
                holder.item.setCurrentValue((data.get(position)));
    
    
                if (position == data.size() - 1) {
                    holder.item.setDrawRightLine(false);
                } else {
                    holder.item.setDrawRightLine(true);
                    holder.item.setNextValue((data.get(position + 1)));
                }
            }
    
            @Override
            public int getItemCount() {
                return data.size();
            }
    
            class ViewHolder extends RecyclerView.ViewHolder {
                Item item;
                public ViewHolder(View itemView) {
                    super(itemView);
                    this.item = (Item) itemView;
                }
            }
        }
    
    
    }
    
    
    MainActivity的代码
    package com.xk.linedemo;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    
    import java.util.ArrayList;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            BrokenLineView brokenLineView = new BrokenLineView(this);
            ArrayList<Integer> integers = new ArrayList<>();
            integers.add(2);
            integers.add(12);
            integers.add(21);
            integers.add(4);
            integers.add(7);
            integers.add(18);
            integers.add(30);
            brokenLineView.setData(integers);
            setContentView(brokenLineView);
    
        }
    }
    

    相关文章

      网友评论

        本文标题:android用recyclerview撸一个折线图

        本文链接:https://www.haomeiwen.com/subject/buhtvxtx.html