android用recyclerview擼一個折線圖

以前覺得用安卓畫一個折線圖好難疮蹦,當然github上也有很多方便的框架惰瓜,幾行代碼就可以實現(xiàn)一個折線圖否副,而且各種樣式都有,但是別人的框架畢竟是為了滿足大眾的需求崎坊,沒有專一性备禀,正好前幾天接了一個私活,要用到折線圖奈揍,時間也比較充足曲尸,就試著自己寫一個玩玩。

經(jīng)過仔細分析小米天氣中的折線圖后男翰,發(fā)現(xiàn)其實寫一個折線圖是非常簡單的另患。先上兩張小米天氣的折線圖


圖一
圖二

如果我告訴你這兩個圖的實現(xiàn)思路是一樣的,而且都很容易奏篙,你會不會很震驚柴淘。
其實是第二種圖中“今天”那一項數(shù)據(jù)給提供了思路,沒錯秘通,recyclerview为严。
拿第一張圖片來分析


image.png

黑色部分框起來的就當做一個item,首先我們要把所有數(shù)據(jù)中的最大值和最小值取出來肺稀,如紅線畫住的部分第股,然后確定一個區(qū)間(y坐標),表示我們所有的點都要在這個區(qū)間內(nèi)顯示话原。圖中當值為176表示最大夕吻,在第一個紅線那個高度,44表示最小繁仁,在第二條直線那個高度涉馅,這兩個高度是我們指定的,那么70這個值對應的高度很明顯就可以計算出來黄虱,103的高度也可以計算出來稚矿,70和103的中間值,也就是圖中第一個紅圈圈住的地方的高度也可以確定出來捻浦,同理晤揣,第三個小圓圈也知道了,然后第一個小圓圈和第二個連線朱灿,第二個和第三個連線昧识,這就畫好了一個item的折線。多個item連接起來就是上面的效果盗扒。

第二張圖思路也是一樣的跪楞,只不過在一個item上畫了兩個折線,如圖中黑線框住的區(qū)域


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);
    }

    /**
     * 畫數(shù)字
     *
     * @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);
        }
    }

    /**
     * 畫數(shù)字下面的小圓圈
     *
     * @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坐標(第二四個參數(shù))

        }
        if (drawRightLine) {
            canvas.drawLine(viewWidth / 2, 500, viewWidth, 500, mPaint);//這兩個值表示下面那條橫線的Y坐標(第二四個參數(shù))

        }
    }

    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);//這個數(shù)字表示每一個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);

    }
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斟湃,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子檐薯,更是在濱河造成了極大的恐慌凝赛,老刑警劉巖注暗,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異墓猎,居然都是意外死亡捆昏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門毙沾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骗卜,“玉大人,你說我怎么就攤上這事左胞】懿郑” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵烤宙,是天一觀的道長遍烦。 經(jīng)常有香客問我,道長躺枕,這世上最難降的妖魔是什么乳愉? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮屯远,結果婚禮上蔓姚,老公的妹妹穿的比我還像新娘。我一直安慰自己慨丐,他們只是感情好坡脐,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著房揭,像睡著了一般备闲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捅暴,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天恬砂,我揣著相機與錄音,去河邊找鬼蓬痒。 笑死泻骤,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的梧奢。 我是一名探鬼主播狱掂,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼亲轨!你這毒婦竟也來了趋惨?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惦蚊,失蹤者是張志新(化名)和其女友劉穎器虾,沒想到半個月后讯嫂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡兆沙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年端姚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挤悉。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖巫湘,靈堂內(nèi)的尸體忽然破棺而出装悲,到底是詐尸還是另有隱情,我是刑警寧澤尚氛,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布诀诊,位于F島的核電站,受9級特大地震影響阅嘶,放射性物質(zhì)發(fā)生泄漏属瓣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一讯柔、第九天 我趴在偏房一處隱蔽的房頂上張望抡蛙。 院中可真熱鬧,春花似錦魂迄、人聲如沸粗截。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熊昌。三九已至,卻和暖如春湿酸,著一層夾襖步出監(jiān)牢的瞬間婿屹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工推溃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留昂利,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓铁坎,卻偏偏與公主長得像页眯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子厢呵,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容