安卓Intent、Bundle的使用以及RecyclerView、ListView的應用
一、實驗題目
Intent、Bundle的使用以及RecyclerView、ListView的應用
二、實現內容
本次實驗模擬實現一個健康食品列表,有兩個介面,第一個介面用於呈現食品列表 如下所示 資料在"manual/素材"目錄下給出。 點選右下方的懸浮按鈕可以切換到收藏夾 上面兩個列表點選任意一項後,可以看到詳細的資訊:
三、課堂實驗結果
(1)實驗截圖
1、主介面
2、點選一個食品進入詳情介面
3、點選收藏按鈕
4、進入收藏夾欄
5、長按收藏欄夾中食品
6、點選確定
7、點選星星
8、長按食品欄刪除
(2)實驗步驟以及關鍵程式碼
1、設定主介面,由一個RecyclerView和一個ListView以及一個FloatingActionButton組成。一開始啟動介面為RecyclerView,暫時設定ListView屬性為gone。
<android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="wrap_content" /> <ListView android:visibility="gone" android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="wrap_content" />
2、設定RecyclerView。首先獲取設定RecyclerView,使用setLayoutManager設定佈局屬性。然後要先自定義RecyclerView.ViewHolder。ViewHolder通常出現在介面卡裡,為的是listview滾動的時候快速設定值,而不必每次都重新建立很多物件,從而提升效能。使用一個SparseArray陣列儲存listItem中的子View。
private SparseArray<View> views; private View view; public MyViewHolder(Context _context, View _view, ViewGroup _viewGroup){ super(_view); view = _view; views = new SparseArray<View>(); }
注意ViewHolder尚未將子View快取到SparseArray陣列中時,仍然需要通過findViewById()建立View物件,如果已快取,直接返回即可。
public <T extends View> T getView(int _viewId) {
View _view = views.get(_viewId);
if (_view == null) {
// 建立view
_view = view.findViewById(_viewId);
// 將view存入views
views.put(_viewId, _view);
}
return (T)_view;
}
之後需要自定義一個adapter。Adapter扮演著兩個角色,一是根據不同ViewType建立與之相應的Item-Layout,二是訪問資料集合並將資料繫結到正確的View上。因此需要重寫一下兩個函式。 public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)建立Item檢視,並返回相應的ViewHolder。 public void onBindViewHolder(final MyViewHolder holder, int position)繫結資料到正確的Item檢視上。 convert函式是一個抽象方法,在宣告adapter時需要實現它。作用是將資料繫結到RecyclerView的各個控制元件中。
final MyRecyclerViewAdapter myAdapter = new MyRecyclerViewAdapter<Map<String,String>>(MainActivity.this, R.layout.items, data) {
@Override
public void convert(MyViewHolder holder, Map<String,String> s) {
TextView name = holder.getView(R.id.name);
name.setText(s.get("name"));
TextView first = holder.getView(R.id.ID);
first.setText(s.get("ID"));
}
};
RecyclerView沒有OnItemClickListener方法,需要在Adapter中實現。方法為:在Adapter中設定一個監聽器,當itemView被點選時,呼叫該監聽器並且將itemView的position作為引數傳遞出去。先新增介面
public interface OnItemClickListener{
void onClick(int position);
void onLongClick(int position);
}
public void setOnItemClickListener(OnItemClickListener _onItemClickListener) {
this.onItemClickListener = _onItemClickListener;
}
然後在onBindViewHolder()中新增重寫函式。 3、設定ListView。ListView的原理和RecyclerView類似。這裡使用了simpleadapter 構造一個SimpleAdapter,需要以下的引數:
1.Context context:上下文,這個是每個元件都需要的,它指明瞭SimpleAdapter關聯的View的執行環境,也就是我們當前的Activity。
2.List<? extends Map
先建立一個simpleAdapter
simpleAdapter = new SimpleAdapter(this,data,R.layout.shoplist,
new String[] {"icon","itemname"},new int[]{R.id.Icon,R.id.Name});
然後用setAdapter函式設定adapter 之後要給listview設定點選函式,分別為單擊和長按事件。
4、現在要在點選函式中加上頁面跳轉以及資料傳遞。在recycleview中繫結的是一個List<Map<String,String>>連結串列,分別儲存了標籤和名稱。我這裡傳遞的資料是該列表項所處的位置以及名稱。所有的資訊儲存為一個string陣列,然後檢測該列表項的名稱匹配陣列中的位置,將該位置和名稱新建一個bundle,再通過intent傳入。
Bundle bundle = new Bundle();
bundle.putInt("position",index);
bundle.putString("name",_name);
intent.putExtras(bundle);
startActivityForResult(intent,1);
5、在詳情頁面內,使用getIntent()函式來接收資訊,用getExtra獲取bundle,然後判斷該位置屬於哪個食品,再繫結資訊到各個控制元件中。
Intent intent=getIntent();
Bundle bundle =intent.getExtras();
String na=bundle.getString("name");
6、之後要進行詳情頁面的佈局設計。我這裡選擇的是LinearLayout,把整個佈局分為三塊RelativeLayout。要設定頂部佔三分之一,需要將整個LinearLayout的weightSum設為3,把頂部的RelativeLayout的layout_weight設為1.使用layout_alignParentLeft設定左對齊,底部對齊同理。要使得垂直居中需要設定layout_centerVertical屬性為true。介面底部的listview的使用方法和之前類似,就不再詳談了。 7、現在要建立收藏的事件。和之前類似,依然使用一個bundle儲存該食品的名稱和位置。
Intent intent1 = new Intent(Info.this,MainActivity.class);
Bundle bundle1 = new Bundle();
bundle1.putInt("position",tag);
bundle1.putString("name",name[tag]);
intent1.putExtras(bundle);
值得一提的是,在之前的主介面中需要新增onActivityResult函式,來處理返回的值,也就是將收藏的值新增進之前建的List < Map < String,String>> 收藏列表中去。
protected void onActivityResult(int requestCode, int resultCode, Intent data1) {
super.onActivityResult(requestCode, resultCode, data1);
// RESULT_OK,判斷另外一個activity已經結束資料輸入功能,Standard activity result:
// operation succeeded. 預設值是-1
if (resultCode == 1) {
if (requestCode == 1) {
Bundle bundle=data1.getExtras();
int tag = bundle.getInt("position");
Map<String,String>temp = new LinkedHashMap<>();
temp.put("icon",ID[tag]);
temp.put("itemname",name[tag]);
data.add(temp);
simpleAdapter.notifyDataSetChanged();
}
}
}
8、星標的轉換需要給該imagebutton加一個tag,再根據tag來setImageResource。
Star.setOnClickListener((view) ->{
int index=(Integer) view.getTag();
if (index==0)
{
Star.setTag(1);
Star.setImageResource(R.mipmap.full_star);
}
else
{
Star.setTag(0);
Star.setImageResource(R.mipmap.empty_star);
}
});
9、點選長按事件需要設定onItemLongClick函式。在recycleview中需要刪除陣列元素,並且將繫結的arraylist中也刪除掉。在listview中需要獲取所點選的view,然後獲取名稱。
LinearLayout layout = (LinearLayout)view;
TextView status = (TextView) layout.findViewById(R.id.Name);
String _name=status.getText().toString();
再通過判斷位置來刪除該收藏。 10、接下來還有懸浮窗的設計。新增依賴implementation
'com.android.support:design:27.1.1’設定佈局檔案 <android.support.design.widget.FloatingActionButton android:id="@+id/btn" android:layout_width=“wrap_content” android:layout_height=“wrap_content” android:src="@mipmap/colletions" android:backgroundTint="@color/colorWhite" android:backgroundTintMode=“src_atop” app:layout_constraintBottom_toBottomOf=“parent” app:layout_constraintRight_toRightOf=“parent” android:layout_margin=“25dp” />
之後設定點選函式。判斷兩個view的可見狀態,然後分別改變可視狀態,並且改變懸浮窗圖片。 11、新增RecyclerView動畫。這裡使用的預設動畫。使用DefaultItemAnimator函式
DefaultItemAnimator defaultItemAnimator = new DefaultItemAnimator();
defaultItemAnimator.setAddDuration(1000);
defaultItemAnimator.setRemoveDuration(1000);
recyclerView.setItemAnimator(defaultItemAnimator);
這裡要注意的是,如果要採用預設動畫,資料更新時不能使用Adapter.notifyDataSetChanged();而是要用Adapter.notifyItemChanged(int position)。
(3)實驗遇到的困難以及解決思路
1、長按recycleview中控制元件刪除時出現錯誤,提示陣列超出範圍。 解決思路:經過除錯發現此時長按卻進入了onclick函式,此時的positon為-1,陣列name[position]自然不存在。經查閱後得知點選事件和長按點選事件是可以同時發生的。而此時可能處於一個兩者之間的時間值。解決方法就是講onLongClick中的返回引數改為true,使長點選事件成為一個獨佔事件。 2、recycleview一直無法匯入依賴 解決思路:無法找到所對應版本的recycleview,因此直接從dependency中搜索相近版本的recycleview,加入進去。 3、佈局不知道怎麼使頂部佔整體的三分之一 解決思路:令整個介面的weightSum為3,然後把頂部介面的RelativeLayout的layout_weight設為1. 4、傳遞頁面時發現如果刪除了一個列表項裡的食品,則進入詳情頁面後與點選時的食品不匹配。 解決思路:刪除食品時只是刪除了adapter繫結的資料,並沒有刪除外部的字元陣列的元素。應該將字串的陣列刪除乾淨。 5、設定垂直居中時控制元件無法處在正確的位置 解決思路:因為垂直居中會使得控制元件處於整個父控制元件的中間,因為一開始我設定的整個中部空間為一個RelativeLayout,所以使得收藏夾按鈕位於整個中間位置。經過修改後把收藏夾一行單獨設為一個RelativeLayout,這樣就可以垂直居中了。
四、實驗思考及感想
1、這次實驗主要涉及了頁面之間的跳轉和listview、recycleview的繫結。其中頁面之間的跳轉是安卓應用開發的重中之重,畢竟一個app不可能永遠只有一個頁面。頁面跳轉時需要注意傳遞的資訊是否符合需要。還要注意在新的頁面如何接收資訊。recycleview等控制元件的繫結可以幫助我們更好地處理成批的資訊,可以更方便之後的修改。 2、recycleview中自定義ViewHoler和adapter可以幫助我們實現許多新奇的效果。它具有低耦合高類聚的特性,過設定不同的LayoutManager,以及結合ItemDecoration , ItemAnimator,ItemTouchHelper,可以實現非常炫酷的效果。同時RecyclerView封裝了viewholder的回收複用,也就是說RecyclerView標準化了ViewHolder,編寫Adapter面向的是ViewHolder而不再是View了,複用的邏輯被封裝了,寫起來更加簡單。直接省去了listview中convertView.setTag(holder)和convertView.getTag()這些繁瑣的步驟。 3、我們再注入依賴時必須注意自己的版本號,如果版本號不匹配,將無法build,甚至於去掉該依賴後仍然會有報錯。有時候需要重啟一下as才行。如果實在不能匹配,可以考慮直接從project structure中的dependencies裡新增依賴。 4、佈局時要熟悉各種佈局的形式與效果,儘量嘗試每一種佈局,這樣到真正使用時可以根據需求更好地選擇正確的佈局方式。