1、立項目的、意義及特點html
目的:現代都市人羣追求綠色、環保、健康的生活方式,人們常會種植一些盆栽來陶冶情操、舒緩緊張工做的心情。可是,忙碌的工做時常會讓他們無暇顧及本身心愛的盆栽植物,或常常出差沒法澆水致使盆栽植物缺水而乾枯。研究代表,花草死亡是因爲澆水不及時或長期曝曬等緣由引發的。所以,能爲人們打理盆栽植物的智能監測系統會成爲都市人羣嚮往的家居用品。java
本項目目的:智能盆栽監測系統由Raspberry Pi三、智能傳感器、語音提示、LED顯示、藍牙、WiFi等模塊同相應的APP軟件進行交互,進行參數調整或者功能實現和社區論壇交流,而且可以獨立完成對盆栽周圍的溫度,溼度和光照強度的有效監測。利用監測到的數據來有效調節利於盆栽的生長環境。python
意義:爲解決用戶對合理照顧盆栽感到困難的問題,本項目基於樹莓派的智能盆栽經過傳感器監測空氣溫溼度、土壤溼度、光照等條件,並根據監測到的數據來實現自動澆灌,提醒用戶將盆栽移動至合適的位置。android
2、智能盆栽監測系統整體設計git
研究主要內容:算法
本項目的目的是利用ARM結構的嵌入式Raspberry Pi開發板搭建一個完整的嵌入式系統開發平臺,並利用該平臺創建基於mjpeg-streamer的嵌入式視頻監控系統、煙霧傳感器MQ-二、GY-30數字光強傳感器、DHT11溫溼度傳感器、FC-28土壤溼度傳感器、L298N電機驅動模塊、LED發光二極管、蜂鳴器、步進電機。嵌入式應用系統的開發離不開嵌入式操做系統的支持,Raspberry Pi開發板搭載針對 Raspberry Pi 專門優化、基於 Debian 的 Raspbian OS,這款 OS 對浮點運算有更好的支持,能爲用戶帶來更快的體驗。另外在固件、核心、應用方面也都有了改進,並且據稱它是最適合普通用戶使用的 OS,同時設計一個符合本系統的文件系統和系統所需運用的各類類型設備的驅動程序。感知層監測溫度、溼度、光照強度、煙霧、土壤溼度等環境參數,並根據監測到的數據進行響應(光照強度達到必定閾值自動啓動照明裝置;土壤溼度達到必定閾值自動啓動澆灌裝置;煙霧觸發報警裝置)。爲了進一步實現嵌入式監測系統的功能,須要在嵌入式操做系統的基礎之上,利用標準的TCP/IP協議設計相應的嵌入式Tornado WEB服務器和嵌入式流媒體服務器,而且集成了KNN Model算法以監測參數爲依據對植物健康情況進行分析,最後設計移動終端監測軟件,使其遠程監測、遠程控制、遠程監控,並添加社區模塊、機器人模塊,能夠同其餘用戶進行交流和學習更好地照料本身的植物。shell
一、系統硬件平臺設計編程
項目採用的是Raspberry Pi爲主的嵌入式開發平臺,開發板如圖2.1所示:vim
圖 2.1 Raspberry Pi開發平臺api
Raspberry Pi是一款基於ARM的微型電腦主板,以SD/MicroSD卡爲內存硬盤,卡片主板周圍有1/2/4個USB接口和一個10/100 以太網接口(A型沒有網口),可鏈接鍵盤、鼠標和網線,同時擁有視頻模擬信號的電視輸出接口和HDMI高清視頻輸出接口,以上部件所有整合在一張僅比信用卡稍大的主板上,具有全部PC的基本功能只需接通電視機和鍵盤,就能執行如電子表格、文字處理、播放高清視頻等諸多功能。 Raspberry Pi B款只提供電腦板,無內存、電源、鍵盤、機箱或連線。
1)煙霧傳感器MQ-2
MQ-2煙霧傳感器所使用的氣敏材料是在清潔空氣中電導率較低的二氧化錫(SnO2)。當傳感器所處環境中存在可燃氣體時,傳感器的電導率隨空氣中可燃氣體濃度的增長而增大。使用簡單的電路便可將電導率的變化轉換爲與該氣體濃度相對應的輸出信號。
Welcome To My Blog:http://www.cnblogs.com/sirius-swu/p/6674428.html
2)GY-30數字光強傳感器
GY-30 採用 ROHM 原裝 BH1750FVI 芯片供電電源:3-5v光照度範圍:0-65535lx傳感器內置16bitAD轉換器直接數字輸出,省略複雜的計算,省略標定不區分環 境光源接近於視覺靈敏度的分光特性可對普遍的亮度進行1勒克斯的高精度測定標準NXP IIC通訊協議模塊內部包含通訊電平轉換,與5v單片機io直接鏈接。
Welcome To My Blog:http://www.cnblogs.com/sirius-swu/p/6682746.html
3)DHT11溫溼度傳感器
含有已校準數字信號輸出的溫溼度複合傳感器。傳感器包括電阻式感溼元件和一個NTC測溫元件。
Welcome To My Blog:http://www.cnblogs.com/sirius-swu/p/6680952.html
4)FC-28土壤溼度傳感器
經過電位器調節控制相應閾值,溼度低於設定值時,DO輸出高電平,高於設定值時,DO輸出低電平;比較器採用LM393芯片,工做穩定。
Welcome To My Blog:http://www.cnblogs.com/sirius-swu/p/6674428.html
5)Raspberry Pi Camera v2
Raspberry Pi Camera v2是樹莓派新推出的官方攝像頭板,採用高質量8百萬像素索尼IMX219傳感器擴展板,擁有定焦鏡頭,能夠捕捉3280 x 2464像素靜態圖片和30FPS 1080P的視頻,也支持1080p30, 720p60 and 640x480p60/90攝像功能。樹莓派攝像頭經過板上表面的小插槽鏈接樹莓派,並使用專門爲樹莓派設計的CSI接口鏈接。
Welcome To My Blog:http://www.cnblogs.com/sirius-swu/p/6669093.html
6)L298N電機驅動模塊
內含兩個H橋的高電壓大電流全橋式驅動器,能夠用來驅動直流電動機和步進電動機、繼電器線圈等感性負載;採用標準邏輯電平信號控制;具備兩個使能控制端,在不受輸入信號影響的狀況下容許或禁止器件工做有一個邏輯電源輸入端,使內部邏輯電路部分在低電壓下工做;能夠外接檢測電阻,將變化量反饋給控制電路。
7)LED發光二極管
樹莓派的IO口驅動能力是比較弱的,驅動電平爲3.3V,高電平驅動比電平驅動能力稍弱些,但這也足夠驅動led工做,爲方便理解,如下實驗以高電平驅動方式進行。IO口輸出高電平,led燈亮,輸出低電平則滅。
鏈接電路:
實物圖以下:
使用vi編輯器編寫控制代碼及編譯運行:
1.登錄樹莓派後,輸入cd ./wiringPi 進入wiringPi目錄,使用vi編輯器編輯c源文件,輸入vim blink.c,如目錄下有blink.c文件打開編輯,若無則會新建一個打開編輯。
cd ./wiringPi/ vim blink.c
2.C語言代碼,以下是使GPIO17間隔500ms交替輸出高低電平的代碼。
對應各欄接口的標號,如如下程序使用的0即爲樹莓派的GPIO17接口也是物理接口的11接口。
#include <wiringPi.h> int main (void){ wiringPiSetup () ; pinMode (0, OUTPUT) ; for (;;){ digitalWrite (0, HIGH) ; delay (500) ; digitalWrite (0, LOW) ; delay (500) ; } return 0 ; }
3.輸入gcc –o blink blink.c -lwiringPi編譯、執行程序
gcc –o blink blink.c -lwiringPi sudo ./blink
8)無源蜂鳴器
內部沒有震盪源,直流信號沒法讓它鳴叫。必須用去震盪的電流驅動它,2K-5KHZ的方波PWM (Pulse Width Modulation脈衝寬度調製)。 5KHZ的電流方波是啥意思?那就是每秒震動5K次,每個完整的週期佔用200us的時間,高點平佔一部分時間,低電平佔一部分時間。
聲音頻率可控,能夠作出不一樣的音效。
有源蜂鳴器:內部帶震盪電路,一通電就鳴叫,因此能夠跟前面LED同樣,給個高電平就能響,編程比無源的方便。
Welcome To My Blog:http://www.cnblogs.com/sirius-swu/p/6823882.html
9)步進電機
步進電動機是一種將電脈衝信號轉換成角位移或線位移的機電元件。步進電動機的輸入量是脈衝序列,輸出量則爲相應的增量位移或步進運動。
Welcome To My Blog:http://www.cnblogs.com/sirius-swu/p/6668894.html
二、系統軟件模塊設計
1)發現
圖片輪播:
xml佈局文件
<FrameLayout android:layout_width="match_parent" android:layout_height="200dip" android:id="@+id/waterFlow"> <android.support.v4.view.ViewPager android:id="@+id/vp" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="35dip" android:layout_gravity="bottom" android:background="#33000000" android:gravity="center" android:orientation="vertical" > <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="圖片標題" android:textColor="@android:color/white" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="3dip" android:orientation="horizontal" > <View android:id="@+id/dot_0" android:layout_width="5dip" android:layout_height="5dip" android:layout_marginLeft="2dip" android:layout_marginRight="2dip" android:background="@drawable/dot_focused"/> <View android:id="@+id/dot_1" android:layout_width="5dip" android:layout_height="5dip" android:layout_marginLeft="2dip" android:layout_marginRight="2dip" android:background="@drawable/dot_normal"/> <View android:id="@+id/dot_2" android:layout_width="5dip" android:layout_height="5dip" android:layout_marginLeft="2dip" android:layout_marginRight="2dip" android:background="@drawable/dot_normal"/> <View android:id="@+id/dot_3" android:layout_width="5dip" android:layout_height="5dip" android:layout_marginLeft="2dip" android:layout_marginRight="2dip" android:background="@drawable/dot_normal"/> <View android:id="@+id/dot_4" android:layout_width="5dip" android:layout_height="5dip" android:layout_marginLeft="2dip" android:layout_marginRight="2dip" android:background="@drawable/dot_normal"/> </LinearLayout> </LinearLayout> </FrameLayout>
java文件
//顯示的圖片 images = new ArrayList<ImageView>(); for(int i = 0; i < imageIds.length; i++){ ImageView imageView = new ImageView(this); imageView.setBackgroundResource(imageIds[i]); images.add(imageView); } //顯示的小點 dots = new ArrayList<View>(); dots.add(findViewById(R.id.dot_0)); dots.add(findViewById(R.id.dot_1)); dots.add(findViewById(R.id.dot_2)); dots.add(findViewById(R.id.dot_3)); dots.add(findViewById(R.id.dot_4)); title = (TextView) findViewById(R.id.title); title.setText(titles[0]); adapter = new ViewPagerAdapter(); mViewPaper.setAdapter(adapter); mViewPaper.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { title.setText(titles[position]); dots.get(position).setBackgroundResource(R.drawable.dot_focused); dots.get(oldPosition).setBackgroundResource(R.drawable.dot_normal); oldPosition = position; currentItem = position; } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } });
瀑布流展現(上拉加載、下拉刷新):
xml佈局文件
<discover.widget.TryRefreshableView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/trymyRV" android:layout_width="fill_parent" android:layout_height="fill_parent" android:focusable="true" android:layout_marginTop="200dp" android:orientation="vertical" > <discover.widget.TryPullToRefreshScrollView android:id="@+id/waterfall_scroll" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scrollbars="vertical" > <LinearLayout android:id="@+id/waterfall_container" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/background" > </LinearLayout> </discover.widget.TryPullToRefreshScrollView> </discover.widget.TryRefreshableView>
java文件
// 瀑布流佈局 all_screen_view = new ArrayList<LinkedList<View>>(); display = this.getWindowManager().getDefaultDisplay(); // 根據屏幕大小計算每列大小 item_width = display.getWidth() / column_count + 2; column_height = new int[column_count]; context = this; pin_mark = new HashMap[column_count]; fb = new FinalBitmap(this).init();// 必須調用init初始化FinalBitmap模塊 fb.setCompleteListener(this); this.lineIndex = new int[column_count]; this.bottomIndex = new int[column_count]; this.topIndex = new int[column_count]; for (int i = 0; i < column_count; i++) { lineIndex[i] = -1; bottomIndex[i] = -1; pin_mark[i] = new HashMap(); } InitLayout(); // 瀑布流佈局 private void InitLayout() { waterfall_scroll = (TryPullToRefreshScrollView) findViewById(R.id.waterfall_scroll); rv = (TryRefreshableView) findViewById(R.id.trymyRV); rv.sv = waterfall_scroll; // 隱藏mfooterView rv.setRefreshListener(new TryRefreshableView.RefreshListener() { @Override public void onDownRefresh() { if (rv.mRefreshState == TryRefreshableView.READYTOREFRESH) { // 記錄第一個view的位置 firstView = waterfall_items.get(0).getChildAt(0); refreshType = DOWNREFRESH; AddItemToContainer(++current_page, page_count); } } }); rv.setOnBottomListener(new TryRefreshableView.OnBottomListener() { @Override public void onBottom() { if (rv.mRefreshState != TryRefreshableView.REFRESHING) { refreshType = UPREFRESH; AddItemToContainer(++current_page, page_count); } } }); waterfall_scroll.setOnScrollListener(new TryPullToRefreshScrollView.OnScrollListener() { @Override public void onAutoScroll(int l, int t, int oldl, int oldt) { // Log.d("MainActivity", // String.format("%d %d %d %d", l, t, oldl, oldt)); // Log.d("MainActivity", "range:" + range); // Log.d("MainActivity", "range-t:" + (range - t)); if (pin_mark.length <= 0) { return; } scroll_height = waterfall_scroll.getMeasuredHeight(); Log.d("MainActivity", "scroll_height:" + scroll_height); if (t > oldt) {// 向下滾動 if (t > 3 * scroll_height) {// 超過兩屏幕後 for (int k = 0; k < column_count; k++) { LinearLayout localLinearLayout = waterfall_items .get(k); if (pin_mark[k].get(Math.min(bottomIndex[k] + 1, lineIndex[k])) <= t + 3 * scroll_height) {// 最底部的圖片位置小於當前t+3*屏幕高度 View childAt = localLinearLayout .getChildAt(Math.min( 1 + bottomIndex[k], lineIndex[k])); if(childAt != null){ FlowView picView = (FlowView) childAt .findViewById(R.id.news_pic); if (picView.bitmap == null && !TextUtils.isEmpty(picView.get_url())) { fb.reload(picView.get_url(), picView); } bottomIndex[k] = Math.min(1 + bottomIndex[k], lineIndex[k]); } } // Log.d("MainActivity", // "headIndex:" + topIndex[k] + " footIndex:" // + bottomIndex[k] + " headHeight:" // + pin_mark[k].get(topIndex[k])); if (pin_mark[k].get(topIndex[k]) < t - 2 * scroll_height) {// 未回收圖片的最高位置<t-兩倍屏幕高度 int i1 = topIndex[k]; topIndex[k]++; ((FlowView) localLinearLayout.getChildAt(i1) .findViewById(R.id.news_pic)).recycle(); Log.d("MainActivity", "recycle,k:" + k + " headindex:" + topIndex[k]); } } } } else {// 向上滾動 if (t > 3 * scroll_height) {// 超過兩屏幕後 for (int k = 0; k < column_count; k++) { LinearLayout localLinearLayout = waterfall_items .get(k); if (pin_mark[k].get(bottomIndex[k]) > t + 3 * scroll_height) { ((FlowView) localLinearLayout.getChildAt( bottomIndex[k]).findViewById( R.id.news_pic)).recycle(); Log.d("MainActivity", "recycle,k:" + k + " headindex:" + topIndex[k]); bottomIndex[k]--; } if (pin_mark[k].get(Math.max(topIndex[k] - 1, 0)) >= t - 2 * scroll_height) { FlowView picView = ((FlowView) localLinearLayout .getChildAt( Math.max(-1 + topIndex[k], 0)) .findViewById(R.id.news_pic)); if (picView.bitmap == null && !TextUtils.isEmpty(picView.get_url())) { fb.reload(picView.get_url(), picView); } topIndex[k] = Math.max(topIndex[k] - 1, 0); } } } } } });
2)環境參數
3)機器人
圖靈API:
xml佈局文件
<ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:transcriptMode="alwaysScroll" android:divider="@null" android:textColor="#000000" android:listSelector="@android:color/transparent"></ListView> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/sendText" android:layout_width="0dp" android:textColor="#000000" android:layout_height="wrap_content" android:layout_weight="1" /> <Button android:id="@+id/send_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send"/> </LinearLayout>
java文件
private void initView() { getActionBar().setTitle(string.robot); lv = (ListView) findViewById(id.lv); sendText = (EditText) findViewById(id.sendText); sendBtn = (Button) findViewById(id.send_btn); lists = new ArrayList<ListData>(); sendBtn.setOnClickListener(this); adapter = new TextAdapter(lists, this); lv.setAdapter(adapter); ListData listData; listData = new ListData(getRandomWelcomeTips(), ListData.RECEIVER, getTime()); lists.add(listData); } private String getRandomWelcomeTips() { String welcome_tip = null; welcome_array = this.getResources().getStringArray(R.array.welcome_tips); int index = (int) (Math.random()*(welcome_array.length-1)); welcome_tip = welcome_array[index]; return welcome_tip; } @Override public void getDataUrl(String data) { // System.out.println(data); parseText(data); } public void parseText(String str) { try { JSONObject jb = new JSONObject(str); // System.out.println(jb.getString("code")); // System.out.println(jb.getString("text")); ListData listData; listData = new ListData(jb.getString("text"), ListData.RECEIVER, getTime()); lists.add(listData); adapter.notifyDataSetChanged(); } catch (Exception e) { e.printStackTrace(); } } @Override public void onClick(View v) { getTime(); content_str = sendText.getText().toString(); sendText.setText(""); String dropk = content_str.replace(" ", ""); String droph = dropk.replace("\n", ""); ListData listData; listData =new ListData(content_str, ListData.SEND, getTime()); lists.add(listData); if (lists.size()>30) { for (int i = 0; i < lists.size(); i++) { lists.remove(i); } } adapter.notifyDataSetChanged(); httpData = (HttpData) new HttpData("http://www.tuling123.com/openapi/api?key=d20467b5e6c340c3a18a4557ebf31693&info="+droph, this).execute(); } private String getTime() { currentTime = System.currentTimeMillis(); SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss"); Date curDate = new Date(); String str = format.format(curDate); if (currentTime-oldTime >= 5*60*1000) { oldTime = currentTime; return str; } else { return ""; } }
4)健康報告
5)終端控制
BUG解析及解決方案:
1.移動不斷髮出控制指令
setInterval() 方法會不停地調用函數,直到 clearInterval() 被調用或窗口被關閉。由 setInterval() 返回的 ID 值可用做 clearInterval() 方法的參數。
去掉setInterval() 方法不須要自動重複調用。
2.蜂鳴器打開、關閉操做以後再次打開響聲不連續
執行開啓程序文件關閉程序文件操做,因爲關閉程序文件未關閉致使再次開啓程序只發出短暫的響聲就中止了。
mProcess = subprocess.Popen(shellLine)來建立進程,mProcess.kill()函數去結束該進程
6)視頻監控
7)論壇
8)消息
9)盆友圈
xml佈局文件:
<android.support.v7.widget.RecyclerView android:id="@+id/rv_text_list" android:layout_width="match_parent" android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
java 文件
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pot); getActionBar().setTitle("盆友圈"); mRvTextList= (RecyclerView) findViewById(R.id.rv_text_list); mRvTextList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); mRvTextList.setAdapter(new TextListAdapter(this)); }
TextListAdapter.java文件
package pot; /** * Created by sirius_swu on 2017/3/19. */ import android.app.Activity; import android.support.v7.widget.RecyclerView; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.Adapter; import android.widget.TextView; import com.special.ResideMenuDemo.R; public class TextListAdapter extends RecyclerView.Adapter<TextListAdapter.TextHolder> { private Activity mContent; private final int MAX_LINE_COUNT = 3; private final int STATE_UNKNOW = -1; private final int STATE_NOT_OVERFLOW = 1;//文本行數不能超過限定行數 private final int STATE_COLLAPSED = 2;//文本行數超過限定行數,進行摺疊 private final int STATE_EXPANDED = 3;//文本超過限定行數,被點擊全文展開 private SparseArray<Integer> mTextStateList; public TextListAdapter(Activity context) { mContent = context; mTextStateList = new SparseArray<Integer>(); } @Override public TextHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new TextHolder(mContent.getLayoutInflater().inflate(R.layout.item_test_list, parent, false)); } @Override public void onBindViewHolder(final TextHolder holder,final int position) { holder.hend.setText(position+1+"");//設置頭部的文字 holder.name.setText(Util.getName(position));//設置名稱 int state=mTextStateList.get(position,STATE_UNKNOW); // 若是該itme是第一次初始化,則取獲取文本的行數 if (state==STATE_UNKNOW){ holder.content.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { // 這個回掉會調用屢次,獲取玩行數後記得註銷監聽 holder.content.getViewTreeObserver().removeOnPreDrawListener(this); // holder.content.getViewTreeObserver().addOnPreDrawListener(null); // 若是內容顯示的行數大於限定顯示行數 if (holder.content.getLineCount()>MAX_LINE_COUNT) { holder.content.setMaxLines(MAX_LINE_COUNT);//設置最大顯示行數 holder.expandOrCollapse.setVisibility(View.VISIBLE);//讓其顯示全文的文本框狀態爲顯示 holder.expandOrCollapse.setText("全文");//設置其文字爲全文 mTextStateList.put(position, STATE_COLLAPSED); }else{ holder.expandOrCollapse.setVisibility(View.GONE);//顯示全文隱藏 mTextStateList.put(position,STATE_NOT_OVERFLOW);//讓其不能超過限定的行數 } return true; } }); holder.content.setMaxLines(Integer.MAX_VALUE);//設置文本的最大行數,爲整數的最大數值 holder.content.setText(Util.getContent(position));//用Util中的getContent方法獲取內容 }else{ // 若是以前已經初始化過了,則使用保存的狀態,無需在獲取一次 switch (state){ case STATE_NOT_OVERFLOW: holder.expandOrCollapse.setVisibility(View.GONE); break; case STATE_COLLAPSED: holder.content.setMaxLines(MAX_LINE_COUNT); holder.expandOrCollapse.setVisibility(View.VISIBLE); holder.expandOrCollapse.setText("全文"); break; case STATE_EXPANDED: holder.content.setMaxLines(Integer.MAX_VALUE); holder.expandOrCollapse.setVisibility(View.VISIBLE); holder.expandOrCollapse.setText("收起"); break; } holder.content.setText(Util.getContent(position)); } // 設置顯示和收起的點擊事件 holder.expandOrCollapse.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int state=mTextStateList.get(position,STATE_UNKNOW); if (state==STATE_COLLAPSED){ holder.content.setMaxLines(Integer.MAX_VALUE); holder.expandOrCollapse.setText("收起"); mTextStateList.put(position,STATE_EXPANDED); }else if (state==STATE_EXPANDED){ holder.content.setMaxLines(MAX_LINE_COUNT); holder.expandOrCollapse.setText("全文"); mTextStateList.put(position,STATE_COLLAPSED); } } }); } @Override public int getItemCount() { return 15; } public class TextHolder extends RecyclerView.ViewHolder { public TextView hend; public TextView name; public TextView content; public TextView expandOrCollapse; public TextHolder(View itemView) { super(itemView); // 綁定xml佈局中的控件 hend = (TextView) itemView.findViewById(R.id.tv_hend); name = (TextView) itemView.findViewById(R.id.tv_name); content = (TextView) itemView.findViewById(R.id.tv_content); expandOrCollapse = (TextView) itemView.findViewById(R.id.tv_expand_or_collapse); } } }
3、系統設計思惟導圖