android 計(jì)步器實(shí)現(xiàn)原理操作

我們經(jīng)常會(huì)看到微信 QQ 以及其他一些運(yùn)動(dòng)app里面都有一個(gè)計(jì)步功能,那它是怎么實(shí)現(xiàn)的呢屿笼?

今天我們就來實(shí)現(xiàn)一下,以下代碼都是從一個(gè)整體項(xiàng)目中抽離出來的,為了理解簡(jiǎn)單方便我把UI部分?jǐn)?shù)據(jù)保存部分全部都去掉了辅甥,只有單純的計(jì)步邏輯和算法。

log日志顯示計(jì)步:

image.png

編寫計(jì)步邏輯的流程圖燎竖,方便理解思路:

image.png

MainActivity :

public class MainActivity extends AppCompatActivity {
    private BindService bindService;
    private TextView textView;
    private boolean isBind;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                textView.setText(msg.arg1 + "");
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.busu);
        Intent intent = new Intent(MainActivity.this, BindService.class);
        isBind =  bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        startService(intent); //繃定并且開啟一個(gè)服務(wù)璃弄,繃定是為了方便數(shù)據(jù)交換,啟動(dòng)是為了當(dāng)當(dāng)前app不在活動(dòng)頁的時(shí)候构回,計(jì)步服務(wù)不會(huì)被關(guān)閉夏块。需要保證當(dāng)activity不為活躍狀態(tài)是計(jì)步服務(wù)在后臺(tái)能一直運(yùn)行!
    }

    //和繃定服務(wù)數(shù)據(jù)交換的橋梁纤掸,可以通過IBinder service獲取服務(wù)的實(shí)例來調(diào)用服務(wù)的方法或者數(shù)據(jù)
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            BindService.LcBinder lcBinder = (BindService.LcBinder) service;
            bindService = lcBinder.getService();
            bindService.registerCallback(new UpdateUiCallBack() {
                @Override
                public void updateUi(int stepCount) {
                    //當(dāng)前接收到stepCount數(shù)據(jù)脐供,就是最新的步數(shù)
                    Message message = Message.obtain();
                    message.what = 1;
                    message.arg1 = stepCount;
                    handler.sendMessage(message);
                    Log.i("MainActivity—updateUi","當(dāng)前步數(shù)"+stepCount);
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onStart() {
        super.onStart();
    }
    @Override
    public void onDestroy() {  //app被關(guān)閉之前,service先解除綁定借跪,如果不解除綁定下次Activity切換到活動(dòng)界面的時(shí)候又會(huì)重新開啟一個(gè)新的計(jì)步線程政己。
        super.onDestroy();
        if (isBind) {
            this.unbindService(serviceConnection);
        }
    }
}

activity繃定并且開啟的服務(wù):當(dāng)前服務(wù)實(shí)現(xiàn)了SensorEventListener接口,SensorEventListener接口是計(jì)步傳感器的一個(gè)回調(diào)接口掏愁。

 @Override
    public void onCreate() {
        super.onCreate();
        Log.i("BindService—onCreate", "開啟計(jì)步");
        new Thread(new Runnable() {
            @Override
            public void run() {
                startStepDetector();
                Log.i("BindService—子線程", "startStepDetector()");
            }
        }).start();
    }

    /**
     * 選擇計(jì)步數(shù)據(jù)采集的傳感器
     * SDK大于等于19歇由,開啟計(jì)步傳感器,小于開啟加速度傳感器
     */
    private void startStepDetector() {
        if (sensorManager != null) {
            sensorManager = null;
        }
        //獲取傳感器管理類
        sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);
        int versionCodes = Build.VERSION.SDK_INT;//取得SDK版本
        if (versionCodes >= 19) {
            //SDK版本大于等于19開啟計(jì)步傳感器
            addCountStepListener();
        } else {        //小于就使用加速度傳感器
            addBasePedometerListener();
        }
    }

    /**
     * 啟動(dòng)計(jì)步傳感器計(jì)步
     */
    private void addCountStepListener() {
        Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
        Sensor detectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
        if (countSensor != null) {
            stepSensorType = Sensor.TYPE_STEP_COUNTER;
            sensorManager.registerListener(BindService.this, countSensor, SensorManager.SENSOR_DELAY_NORMAL);
            Log.i("計(jì)步傳感器類型", "Sensor.TYPE_STEP_COUNTER");
        } else if (detectorSensor != null) {
            stepSensorType = Sensor.TYPE_STEP_DETECTOR;
            sensorManager.registerListener(BindService.this, detectorSensor, SensorManager.SENSOR_DELAY_NORMAL);
        } else {
            addBasePedometerListener();
        }
    }

    /**
     * 啟動(dòng)加速度傳感器計(jì)步
     */
    private void addBasePedometerListener() {
        Log.i("BindService", "加速度傳感器");
        mStepCount = new StepCount();
        mStepCount.setSteps(nowBuSu);
        //獲取傳感器類型 獲得加速度傳感器
        Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        //此方法用來注冊(cè)托猩,只有注冊(cè)過才會(huì)生效印蓖,參數(shù):SensorEventListener的實(shí)例,Sensor的實(shí)例京腥,更新速率
        boolean isAvailable = sensorManager.registerListener(mStepCount.getStepDetector(), sensor, SensorManager.SENSOR_DELAY_UI);
        mStepCount.initListener(new StepValuePassListener() {
            @Override
            public void stepChanged(int steps) {
                nowBuSu = steps;//通過接口回調(diào)獲得當(dāng)前步數(shù)
                updateNotification();    //更新步數(shù)通知
            }
        });
    }

    /**
     * 通知調(diào)用者步數(shù)更新 數(shù)據(jù)交互
     */
    private void updateNotification() {
        if (mCallback != null) {
            Log.i("BindService", "數(shù)據(jù)更新");
            mCallback.updateUi(nowBuSu);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {

        return lcBinder;
    }

    /**
     * 計(jì)步傳感器數(shù)據(jù)變化回調(diào)接口
     */
    @Override
    public void onSensorChanged(SensorEvent event) {
        //這種類型的傳感器返回步驟的數(shù)量由用戶自上次重新啟動(dòng)時(shí)激活赦肃。返回的值是作為浮動(dòng)(小數(shù)部分設(shè)置為0),
        // 只在系統(tǒng)重啟復(fù)位為0。事件的時(shí)間戳將該事件的第一步的時(shí)候公浪。這個(gè)傳感器是在硬件中實(shí)現(xiàn),預(yù)計(jì)低功率他宛。
        if (stepSensorType == Sensor.TYPE_STEP_COUNTER) {
            //獲取當(dāng)前傳感器返回的臨時(shí)步數(shù)
            int tempStep = (int) event.values[0];
            //首次如果沒有獲取手機(jī)系統(tǒng)中已有的步數(shù)則獲取一次系統(tǒng)中APP還未開始記步的步數(shù)
            if (!hasRecord) {
                hasRecord = true;
                hasStepCount = tempStep;
            } else {
                //獲取APP打開到現(xiàn)在的總步數(shù)=本次系統(tǒng)回調(diào)的總步數(shù)-APP打開之前已有的步數(shù)
                int thisStepCount = tempStep - hasStepCount;
                //本次有效步數(shù)=(APP打開后所記錄的總步數(shù)-上一次APP打開后所記錄的總步數(shù))
                int thisStep = thisStepCount - previousStepCount;
                //總步數(shù)=現(xiàn)有的步數(shù)+本次有效步數(shù)
                nowBuSu += (thisStep);
                //記錄最后一次APP打開到現(xiàn)在的總步數(shù)
                previousStepCount = thisStepCount;
            }
        }
        //這種類型的傳感器觸發(fā)一個(gè)事件每次采取的步驟是用戶。只允許返回值是1.0,為每個(gè)步驟生成一個(gè)事件欠气。
        // 像任何其他事件,時(shí)間戳表明當(dāng)事件發(fā)生(這一步),這對(duì)應(yīng)于腳撞到地面時(shí),生成一個(gè)高加速度的變化厅各。
        else if (stepSensorType == Sensor.TYPE_STEP_DETECTOR) {
            if (event.values[0] == 1.0) {
                nowBuSu++;
            }
        }
        updateNotification();

    }

    /**
     * 計(jì)步傳感器精度變化回調(diào)接口
     */
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    /**
     * 綁定回調(diào)接口
     */
    public class LcBinder extends Binder {
        BindService getService() {
            return BindService.this;
        }
    }

    /**
     * 數(shù)據(jù)傳遞接口
     *
     * @param paramICallback
     */
    public void registerCallback(UpdateUiCallBack paramICallback) {
        this.mCallback = paramICallback;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //返回START_STICKY :在運(yùn)行onStartCommand后service進(jìn)程被kill后,那將保留在開始狀態(tài)预柒,但是不保留那些傳入的intent队塘。
        // 不久后service就會(huì)再次嘗試重新創(chuàng)建袁梗,因?yàn)楸A粼陂_始狀態(tài),在創(chuàng)建     service后將保證調(diào)用onstartCommand憔古。
        // 如果沒有傳遞任何開始命令給service遮怜,那將獲取到null的intent。
        return START_STICKY;
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        //取消前臺(tái)進(jìn)程
        stopForeground(true);

    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }
}

如果sdk版本大于等于19鸿市,到這里計(jì)步服務(wù)就能向activity反饋步數(shù)了锯梁。但是如果sdk版本小于19,通過加速度傳感器計(jì)數(shù)步數(shù)還要通過算法來獲妊媲椤:

public class StepCount implements StepCountListener {
    private int mCount; //當(dāng)前步數(shù)
    private int count;  //緩存步數(shù)陌凳,步數(shù)3秒內(nèi)小于10步則不計(jì)數(shù)
    private long timeOfLastPeak = 0;//計(jì)時(shí)  開始時(shí)間 步數(shù)3秒內(nèi)小于10步則不計(jì)數(shù)
    private long timeOfThisPeak = 0;//計(jì)時(shí)  現(xiàn)在時(shí)間 步數(shù)3秒內(nèi)小于10步則不計(jì)數(shù)
    private StepValuePassListener stepValuePassListener;//接口用來傳遞步數(shù)變化
    private StepDetector stepDetector;//傳感器SensorEventListener子類實(shí)例

    public StepCount() {
        stepDetector = new StepDetector();
        stepDetector.initListener(this);
    }

    @Override
    public void countStep() {
        this.timeOfLastPeak = this.timeOfThisPeak;
        this.timeOfThisPeak = System.currentTimeMillis();
        Log.i("countStep","傳感器數(shù)據(jù)刷新回調(diào)");
//        notifyListener();
        if (this.timeOfThisPeak - this.timeOfLastPeak <= 3000L) {
            if (this.count < 9) {
                this.count++;
            } else if (this.count == 9) {
                this.count++;
                this.mCount += this.count;
                notifyListener();
            } else {
                this.mCount++;
                notifyListener();
            }
        } else {//超時(shí)
            this.count = 1;//為1,不是0
        }

    }
    public void setSteps(int initNowBusu){
        this.mCount = initNowBusu;//接收上層調(diào)用傳遞過來的當(dāng)前步數(shù)
        this.count = 0;
        timeOfLastPeak = 0;
        timeOfThisPeak = 0;
        notifyListener();
    }

    /**
     * 用來給調(diào)用者獲取SensorEventListener實(shí)例
     * @return 返回SensorEventListener實(shí)例
     */
    public StepDetector getStepDetector(){
        return stepDetector;
    }
    /**
     * 更新步數(shù),通過接口函數(shù)通過上層調(diào)用者
     */
    public void notifyListener(){
        if(this.stepValuePassListener != null){
            Log.i("countStep","數(shù)據(jù)更新");
            this.stepValuePassListener.stepChanged(this.mCount);  //當(dāng)前步數(shù)通過接口傳遞給調(diào)用者
        }
    }
    public  void initListener(StepValuePassListener listener){
        this.stepValuePassListener = listener;
    }
}


    @Override
    public void onSensorChanged(SensorEvent event) {//當(dāng)傳感器值改變回調(diào)此方法
        for (int i = 0; i < 3; i++) {
            oriValues[i] = event.values[i];
        }
        gravityNew = (float) Math.sqrt(oriValues[0] * oriValues[0]
                + oriValues[1] * oriValues[1] + oriValues[2] * oriValues[2]);
        detectorNewStep(gravityNew);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        //
    }

    public void initListener(StepCountListener listener) {
        this.mStepListeners = listener;
    }

    /*
    * 檢測(cè)步子内舟,并開始計(jì)步
    * 1.傳入sersor中的數(shù)據(jù)
    * 2.如果檢測(cè)到了波峰合敦,并且符合時(shí)間差以及閾值的條件,則判定為1步
    * 3.符合時(shí)間差條件谒获,波峰波谷差值大于initialValue蛤肌,則將該差值納入閾值的計(jì)算中
    * */
    public void detectorNewStep(float values) {
        if (gravityOld == 0) {
            gravityOld = values;
        } else {
            if (detectorPeak(values, gravityOld)) {
                timeOfLastPeak = timeOfThisPeak;
                timeOfNow = System.currentTimeMillis();
                if (timeOfNow - timeOfLastPeak >= TimeInterval
                        && (peakOfWave - valleyOfWave >= ThreadValue)) {
                    timeOfThisPeak = timeOfNow;
                    /*
                     * 更新界面的處理壁却,不涉及到算法
                     * 一般在通知更新界面之前批狱,增加下面處理,為了處理無效運(yùn)動(dòng):
                     * 1.連續(xù)記錄10才開始計(jì)步
                     * 2.例如記錄的9步用戶停住超過3秒展东,則前面的記錄失效赔硫,下次從頭開始
                     * 3.連續(xù)記錄了9步用戶還在運(yùn)動(dòng),之前的數(shù)據(jù)才有效
                     * */
                    mStepListeners.countStep();
                }
                if (timeOfNow - timeOfLastPeak >= TimeInterval
                        && (peakOfWave - valleyOfWave >= InitialValue)) {
                    timeOfThisPeak = timeOfNow;
                    ThreadValue = peakValleyThread(peakOfWave - valleyOfWave);
                }
            }
        }
        gravityOld = values;
    }

    /*
     * 檢測(cè)波峰
     * 以下四個(gè)條件判斷為波峰:
     * 1.目前點(diǎn)為下降的趨勢(shì):isDirectionUp為false
     * 2.之前的點(diǎn)為上升的趨勢(shì):lastStatus為true
     * 3.到波峰為止盐肃,持續(xù)上升大于等于2次
     * 4.波峰值大于20
     * 記錄波谷值
     * 1.觀察波形圖爪膊,可以發(fā)現(xiàn)在出現(xiàn)步子的地方,波谷的下一個(gè)就是波峰砸王,有比較明顯的特征以及差值
     * 2.所以要記錄每次的波谷值推盛,為了和下次的波峰做對(duì)比
     * */
    public boolean detectorPeak(float newValue, float oldValue) {
        lastStatus = isDirectionUp;
        if (newValue >= oldValue) {
            isDirectionUp = true;
            continueUpCount++;
        } else {
            continueUpFormerCount = continueUpCount;
            continueUpCount = 0;
            isDirectionUp = false;
        }

        if (!isDirectionUp && lastStatus
                && (continueUpFormerCount >= 2 || oldValue >= 20)) {
            peakOfWave = oldValue;
            return true;
        } else if (!lastStatus && isDirectionUp) {
            valleyOfWave = oldValue;
            return false;
        } else {
            return false;
        }
    }

    /*
     * 閾值的計(jì)算
     * 1.通過波峰波谷的差值計(jì)算閾值
     * 2.記錄4個(gè)值,存入tempValue[]數(shù)組中
     * 3.在將數(shù)組傳入函數(shù)averageValue中計(jì)算閾值
     * */
    public float peakValleyThread(float value) {
        float tempThread = ThreadValue;
        if (tempCount < ValueNum) {
            tempValue[tempCount] = value;
            tempCount++;
        } else {
            tempThread = averageValue(tempValue, ValueNum);
            for (int i = 1; i < ValueNum; i++) {
                tempValue[i - 1] = tempValue[i];
            }
            tempValue[ValueNum - 1] = value;
        }
        return tempThread;

    }

    /*
     * 梯度化閾值
     * 1.計(jì)算數(shù)組的均值
     * 2.通過均值將閾值梯度化在一個(gè)范圍里
     * */
    public float averageValue(float value[], int n) {
        float ave = 0;
        for (int i = 0; i < n; i++) {
            ave += value[i];
        }
        ave = ave / ValueNum;
        if (ave >= 8)
            ave = (float) 4.3;
        else if (ave >= 7 && ave < 8)
            ave = (float) 3.3;
        else if (ave >= 4 && ave < 7)
            ave = (float) 2.3;
        else if (ave >= 3 && ave < 4)
            ave = (float) 2.0;
        else {
            ave = (float) 1.3;
        }
        return ave;
    }

}

全部代碼已經(jīng)上傳到github:
https://github.com/Guojiankai/JiBu

參考:https://github.com/linglongxin24/DylanStepCount

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谦铃,一起剝皮案震驚了整個(gè)濱河市耘成,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驹闰,老刑警劉巖瘪菌,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嘹朗,居然都是意外死亡师妙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門屹培,熙熙樓的掌柜王于貴愁眉苦臉地迎上來默穴,“玉大人怔檩,你說我怎么就攤上這事⌒罘蹋” “怎么了珠洗?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)若专。 經(jīng)常有香客問我许蓖,道長(zhǎng),這世上最難降的妖魔是什么调衰? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任膊爪,我火速辦了婚禮,結(jié)果婚禮上嚎莉,老公的妹妹穿的比我還像新娘米酬。我一直安慰自己,他們只是感情好趋箩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布赃额。 她就那樣靜靜地躺著,像睡著了一般叫确。 火紅的嫁衣襯著肌膚如雪跳芳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天竹勉,我揣著相機(jī)與錄音飞盆,去河邊找鬼。 笑死次乓,一個(gè)胖子當(dāng)著我的面吹牛吓歇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播票腰,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼城看,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了杏慰?” 一聲冷哼從身側(cè)響起测柠,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逃默,沒想到半個(gè)月后鹃愤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡完域,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年软吐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吟税。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凹耙,死狀恐怖姿现,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肖抱,我是刑警寧澤备典,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站意述,受9級(jí)特大地震影響提佣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜荤崇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一拌屏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧术荤,春花似錦倚喂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至子库,卻和暖如春舱权,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刚照。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工刑巧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人无畔。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像吠冤,于是被迫代替她去往敵國(guó)和親浑彰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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

  • 我們經(jīng)常會(huì)看到微信 QQ 以及其他一些運(yùn)動(dòng)app里面都有一個(gè)計(jì)步功能拯辙,那它是怎么實(shí)現(xiàn)的呢郭变? 今天我們就來實(shí)現(xiàn)一下,...
    嚶嚶嚶999閱讀 20,080評(píng)論 7 23
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,516評(píng)論 25 707
  • 第六十一章 聽著鳳九的話涯保,東華沒有控制住的撲哧一聲笑了出來诉濒。抬起一只手,屈指敲在鳳九額頭“都在想些什么夕春?總不會(huì)是九...
    墨冉_諾諾閱讀 3,066評(píng)論 4 50
  • 曲:高至豪《復(fù)刻回憶》詞:綦楓 把昨天翻頁以后 你在記憶里沒走桌角打翻的墨 灰色漸漸洇透續(xù)寫溫柔 有些話怎么說出口...
    李漢卿閱讀 492評(píng)論 0 0
  • 《風(fēng)箏》原創(chuàng)首發(fā) 文/一江水(黑龍江) 你翱翔在天空 追趕著人生的夢(mèng)想 你在風(fēng)里飄蕩 一次又一次地 尋找著自己的方...
    一江水wxj閱讀 255評(píng)論 0 0