摘要:方法根據(jù)子項(xiàng)所處的位置判斷具體類型并返回。調(diào)用方法解除子項(xiàng)與之間的關(guān)聯(lián)。自定義適配器適配器繼承自,并將泛型指定為內(nèi)部類。使用支持多種布局方式借助能夠靈活地將列表控件放入不同的容器。
ListView 和 RecyclerView
最常用和最難用的控件
由于手機(jī)屏幕空間有限,無法顯示全部內(nèi)容。當(dāng)有大量數(shù)據(jù)需要展示的時(shí)候,借助列表控件。通過手指上下滑動(dòng),使得屏幕內(nèi)外的數(shù)據(jù)不斷進(jìn)出。
最基本的列表工作模式需要列表控件、數(shù)據(jù)源,列表控件能夠進(jìn)行交互和展示數(shù)據(jù)。但是列表控件不與數(shù)據(jù)源直接打交道,Adapter 接口充當(dāng)橋梁,關(guān)聯(lián)數(shù)據(jù)源與列表控件,增強(qiáng)可擴(kuò)展性,適配不同數(shù)據(jù)類型數(shù)據(jù)源。例如:ArrayAdapter 數(shù)組、CursorAdapter 游標(biāo)。
數(shù)據(jù)源可能來自:
靜態(tài)數(shù)據(jù)
網(wǎng)絡(luò)數(shù)據(jù)
數(shù)據(jù)庫
ListViewListView extends AdapterView extends ViewGroup.
Adapter 管理數(shù)據(jù)源
AdapterView 展示數(shù)據(jù)并處理交互
數(shù)據(jù)無法直接傳遞給 ListView,需要借助 setAdapter() 適配器來完成。例如 ArrayAdapter<> 泛型指定要適配的數(shù)據(jù)類型。
ListView listView = (ListView) findViewById(R.id.listview); listView.setAdapter(adapter);自定義適配器
適配數(shù)據(jù)源并重寫一組父類方法:
構(gòu)造函數(shù):例如 ArrayAdapter 依次傳入當(dāng)前上下文、ListView 子項(xiàng)布局 id、數(shù)據(jù)源。
ArrayAdapter(Context context, int resource, int textViewResourceId, Listobjects)
getView() 方法:用于每個(gè)子項(xiàng)(單行)進(jìn)入屏幕可視區(qū)域時(shí)候調(diào)用,根據(jù)數(shù)據(jù)源繪制子項(xiàng)布局。
程序示例:
public class MySimpleArrayAdapter extends ArrayAdapter{ private final Context context; private final String[] values; public MySimpleArrayAdapter(Context context, String[] values) { super(context, R.layout.rowlayout, values); this.context = context; this.values = values; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rowView = inflater.inflate(R.layout.rowlayout, parent, false); TextView textView = (TextView) rowView.findViewById(R.id.label); ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); textView.setText(values[position]); // Change the icon for Windows and iPhone String s = values[position]; if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { imageView.setImageResource(R.drawable.no); } else { imageView.setImageResource(R.drawable.ok); } return rowView; } }
其他常用方法:
getCount() 方法
返回適配器表示的數(shù)據(jù)源中一共有多少項(xiàng)數(shù)據(jù)。
notifyDataSetChanged() 方法
數(shù)據(jù)源的數(shù)據(jù)發(fā)生變化,通知 ListView 更新數(shù)據(jù)重新繪制視圖。
提升 ListView 運(yùn)行效率避免在 Adapter 的 getView() 方法中重新加載布局(子項(xiàng)布局)
public abstract View getView(int position, View convertView, ViewGroup parent)
convertView 用于將加載好的布局進(jìn)行緩存,根據(jù) convertView 是否為空,判斷能否重用布局,減少 LayoutInflater.inflate() 調(diào)用次數(shù)從而提升性能。
減少 findViewById() 方法獲取控件實(shí)例的調(diào)用次數(shù)
通過內(nèi)部類 ViewHolder 對(duì)控件實(shí)例進(jìn)行緩存,調(diào)用 View 的 setTag() 方法,將 ViewHolder 對(duì)象存儲(chǔ)在 View 中。
程序示例:
public class MyPerformanceArrayAdapter extends ArrayAdapter存在多種類型的子項(xiàng)布局的場景{ private final Activity context; private final String[] names; static class ViewHolder { public TextView text; public ImageView image; } public MyPerformanceArrayAdapter(Activity context, String[] names) { super(context, R.layout.rowlayout, names); this.context = context; this.names = names; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; // reuse views if (rowView == null) { LayoutInflater inflater = context.getLayoutInflater(); rowView = inflater.inflate(R.layout.rowlayout, null); // configure view holder ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01); viewHolder.image = (ImageView) rowView .findViewById(R.id.ImageView01); rowView.setTag(viewHolder); } // fill data ViewHolder holder = (ViewHolder) rowView.getTag(); String s = names[position]; holder.text.setText(s); if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { holder.image.setImageResource(R.drawable.no); } else { holder.image.setImageResource(R.drawable.ok); } return rowView; } }
基本實(shí)現(xiàn)方式:
定義視圖類型常量
重寫 getViewTypeCount() 方法和 getItemViewType(int position) 方法
重寫 getView() 方法
getViewTypeCount() 方法
返回一共有多少個(gè)不同的視圖類型(布局),這些視圖將由 getView() 方法創(chuàng)建。
getItemViewType(int position) 方法
根據(jù)子項(xiàng)所處的位置判斷具體類型并返回。
程序示例:
@Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { return (contactList.get(position).getContactType() == ContactType.CONTACT_WITH_IMAGE) ? 0 : 1; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; int type = getItemViewType(position); if (v == null) { // Inflate the layout according to the view type LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (type == 0) { // Inflate the layout with image v = inflater.inflate(R.layout.image_contact_layout, parent, false); } else { v = inflater.inflate(R.layout.simple_contact_layout, parent, false); } } // fill data Contact c = contactList.get(position); TextView surname = (TextView) v.findViewById(R.id.surname); TextView name = (TextView) v.findViewById(R.id.name); TextView email = (TextView) v.findViewById(R.id.email); if (type == 0) { ImageView img = (ImageView) v.findViewById(R.id.img); img.setImageResource(c.imageId); } surname.setText(c.surname); name.setText(c.name); email.setText(c.email); return v; }ListView 的 RecycleBin 機(jī)制
ListView 即使加載成百上千條數(shù)據(jù),依然不會(huì)發(fā)生 OOM 的原因——RecycleBin 機(jī)制。
RecycleBin 類中存在兩個(gè)重要的數(shù)組:
mActiveViews 屏幕上可見的 View
mScrapViews 屏幕外不可見的 View
當(dāng) ListView 子項(xiàng) View 進(jìn)入屏幕可視區(qū)域時(shí)候,從 RecycleBin 的 mScrapViews 獲取 View 作為 convertView 參數(shù)傳遞給 Adapter 的 getView() 方法。
ListView 有如 View 一般執(zhí)行視圖繪制流程 onMeasure()、onLayout()、onDraw()。在 onLayout() 方法中會(huì)調(diào)用一個(gè)關(guān)鍵方法 layoutChildren(),該方法由 ListView 具體實(shí)現(xiàn)進(jìn)行子元素的布局,同時(shí)完成 ListView 對(duì)子項(xiàng) View 的添加和刪除操作。
layoutChildren() 方法主要邏輯:
若 Adapter 中的數(shù)據(jù)集發(fā)生變化,則將 ListView 中的所有子項(xiàng) View 放到 RecycleBin 中的 mScrapViews 廢棄 View 集合。
若 Adapter 中的數(shù)據(jù)集無變化,則將 ListView 中的所有子項(xiàng) View 放到 RecycleBin 中的 mActiveViews 激活 View 集合。
調(diào)用 detachAllViewsFromParent() 方法解除子項(xiàng) View 與 ListView 之間的關(guān)聯(lián)。
重新將子項(xiàng) View 添加到 ListView 中。根據(jù) mLayoutMode 判斷如何進(jìn)行添加,fillDown() 方法將子 View 從指定的 position 自上而下填充 ListView,fillUp() 則相反自下而上進(jìn)行填充。
RecyclerView 自定義適配器適配器繼承自 RecyclerView.Adapter<>,并將泛型指定為內(nèi)部類 Adapter.ViewHolder。
重寫一組父類方法:
onCreateViewHolder()
加載子項(xiàng)布局(LayoutInflater inflate()),創(chuàng)建 ViewHolder 實(shí)例。
onBindViewHolder()
用于每個(gè)子項(xiàng)(單行)進(jìn)入屏幕可視區(qū)域時(shí)候調(diào)用,根據(jù)數(shù)據(jù)源位置繪制子項(xiàng)布局。
getItemCount()
返回?cái)?shù)據(jù)源的長度
程序示例:
public class MyAdapter extends RecyclerView.AdapterRecyclerView vs ListView{ private String[] mDataset; // Provide a reference to the views for each data item // Complex data items may need more than one view per item, and // you provide access to all the views for a data item in a view holder public static class MyViewHolder extends RecyclerView.ViewHolder { // each data item is just a string in this case public TextView mTextView; public MyViewHolder(TextView v) { super(v); mTextView = v; } } // Provide a suitable constructor (depends on the kind of dataset) public MyAdapter(String[] myDataset) { mDataset = myDataset; } // Create new views (invoked by the layout manager) @Override public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // create a new view TextView v = (TextView) LayoutInflater.from(parent.getContext()) .inflate(R.layout.my_text_view, parent, false); ... MyViewHolder vh = new MyViewHolder(v); return vh; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(MyViewHolder holder, int position) { // - get element from your dataset at this position // - replace the contents of the view with that element holder.mTextView.setText(mDataset[position]); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return mDataset.length; } }
固有的 ViewHolder 模式規(guī)范
RecyclerView.Adapter 默認(rèn)采用 ViewHolder 模式,減少 findViewById() 方法獲取控件實(shí)例的調(diào)用次數(shù)。
使用 LayoutManager 支持多種布局方式
RecyclerView 借助 LayoutManager 能夠靈活地將列表控件放入不同的容器(LinearLayout, GridLayout)。
ListView 布局只能實(shí)現(xiàn)縱向排列,而 RecyclerView 將排列工作 setLayoutManager() 交給 LayoutManager 布局排列接口,因此可以定制出不同排列方式(橫向、瀑布流布局)。
通知 Adapter 的數(shù)據(jù)變化更加靈活
不僅 notifyDataSetChange() 方法,RecyclerView 可以使用 notifyItemRangeChanged() 等方法實(shí)現(xiàn)局部更新數(shù)據(jù)并重繪視圖。
子項(xiàng)視圖的動(dòng)畫效果更容易實(shí)現(xiàn)
RecyclerView.ItemAnimator
RecyclerView.ItemDecoration
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/72020.html
摘要:四種狀態(tài)運(yùn)行狀態(tài)暫停狀態(tài)停止?fàn)顟B(tài)銷毀狀態(tài)運(yùn)行狀態(tài)處于返回棧的棧頂位置,正在運(yùn)行與用戶發(fā)生著交互,系統(tǒng)不愿回收此種狀態(tài)的。和都是為了解決重復(fù)創(chuàng)建問題,的作用域是棧頂,的作用域是整個(gè)返回棧。 Context Android 系統(tǒng)組件不同于普通類對(duì)象,能夠直接創(chuàng)建實(shí)例,需要各自的上下文環(huán)境——Context。 Context 上下文環(huán)境確保 Android 系統(tǒng)組件(Activity、Se...
摘要:層面中可以通過方法攔截事件傳遞,返回代表同一事件列不再向下傳遞給子,返回代表事件繼續(xù)傳遞,默認(rèn)返回。同時(shí)注冊兩者事件傳遞順序,方法將會(huì)先于方法執(zhí)行,并且方法可能執(zhí)行多次事件。如此反復(fù)執(zhí)行初始化布局繪制過程容易造成性能問題。 View 和 ViewGroup View 是 Android 中最基本的 UI 組件,在屏幕上繪制一塊矩形區(qū)域。 ViewGroup 是一種特殊的 View,它...
閱讀 2578·2021-09-06 15:02
閱讀 3207·2021-09-02 10:18
閱讀 2829·2019-08-30 15:44
閱讀 691·2019-08-30 15:43
閱讀 1956·2019-08-30 14:08
閱讀 2764·2019-08-30 13:16
閱讀 1405·2019-08-26 13:52
閱讀 936·2019-08-26 12:21