1.android:divider=”” 列表之間繪制的顏色或者圖片。一般開發中用于分隔表項。在實際開發過程中,如果你不想要列表之間的分割線,可以設置屬性為 @null 2. android:dividerHeight=”” 前面 divider 的高度。 3. android:stackFromBottom=”” 使它們的內容從底部開始顯示。默認是 false 從頂部開始顯示,如果設置為 true 則從底部開始顯示。 4. android:transcriptMode=”” 設置列表的 transcriptMode 模式,該模式指定列表添加新的選項的時候,是否自動滑動到底部,顯示新的選項。 共三個枚舉值: disabled:取消 transcriptMode 模式,默認的 。 normal:當接受到數據集合改變的通知,并且僅僅當最后一個選項已經完全顯示在屏幕的時候,自動滑動到底部。 alwaysScroll:無論當前列表顯示什么選項,列表將會自動滑動到底部顯示最新的選項。
setEmptyView(View)設置ListView沒有數據時展示的布局 需要注意。該方法需要的參數 View。該 View 必須和 ListView 在同一個布局容器中。
setHeaderView() 添加頭部布局,可以添加多個(需要動態加載布局)
setFooterView() 添加尾部布局,可以添加多個(需要動態加載布局)
onItemCLick 方法的參數說明: parent:指 ListView 本身。 view:當前被點擊的 Item 的 View 對象 position:當前點擊的 Item 的視圖位置下標 id:該值是 Adapter 的 getItemId() 方法所返回的值。
Android 中適配器是連接后端數據和前端顯示的接口,是數據和UI之間重要的紐帶,主要在 View 上顯示【一般是 listview】。可以看作是界面數據綁定的一種理解。它所操縱的數據一般都是一些比較復雜的數據,如數組,鏈表,數據庫,集合等。適配器就像顯示器,把復雜的東西按人可以接受的方式來展現。 高級控件:ListView、GridView[網格視圖]、Spinner[下拉列表]、Gallery[畫廊]、ViewPage 等都需要使用適配器來為其設置數據源。
常用的適配器有:ArrayAdapter,SimpleAdapter,CursorAdapter 這三個都是繼承 BaseAdapter,BaseAdapter是一個抽象類,需要子類繼承并實現其中的方法才能使用,常用于用戶自定義適配器時,顯示比較復雜的數據。
使用簡單 例:
public class MainActivity extends AppCompatActivity { PRivate ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //動態加載布局... View main = getLayoutInflater().inflate(R.layout.activity_main, new FrameLayout(this), false); setContentView(main);// setContentView(R.layout.activity_main); ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, getData()); listView = (ListView) main.findViewById(R.id.listView); //動態加載布局.... //第一步:得到布局加載器// LayoutInflater inflater= getLayoutInflater();//在Activity中// LayoutInflater inflater= LayoutInflater.from(this);//在有Context 的地方// LayoutInflater inflater= (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);//在有Context 的地方 LayoutInflater inflater = LayoutInflater.from(this); //第二步:使用布局加載器 加載布局 View view1 = inflater.inflate(R.layout.view_list_header, listView, false); View view2 = inflater.inflate(R.layout.view_list_header, listView, false); View view3 = inflater.inflate(R.layout.view_list_header, listView, false); //添加頭部布局// listView.addHeaderView(view1);// listView.addHeaderView(view2); //添加尾部布局 // listView.addFooterView(view3); //添加空的視圖(當ListView 沒有數據的時候顯示) listView.setEmptyView(findViewById(R.id.lv_emptyView));//要求該視圖 必須在 ListView 所在的容器里 view1.findViewById(R.id.btn_sss).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "呵呵", Toast.LENGTH_SHORT).show(); } }); //綁定適配器到ListView listView.setAdapter(arrayAdapter); } //初始化數據 public List<String> getData() { ArrayList<String> data = new ArrayList<>(); for (int i = 0; i < 50; i++) { data.add("我和你吻別,在無人的街道..." + i); } return data; }}參數說明: context : 上下文 data:數據源 格式 List
setViewValue 方法的參數說明: view 當前需要綁定的視圖控件(不是整個布局,而是布局里面具體某一個ImageView 或 TextView 等); data:當前 view 所對應的 數據 textRepresentation:該參數是由 data 經過處理的,內部做了非空判斷,所以該參數永遠不為 null
例:
public class MainActivity extends AppCompatActivity { private ListView lv_show; //初始化數據 ArrayList<HashMap<String, Object>> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv_show = (ListView) findViewById(R.id.lv_show); HashMap<String, Object> map1 = new HashMap<>(); map1.put("icon", R.mipmap.icon0); map1.put("title", "訂閱號"); map1.put("msg", "iphone7發布了,激動我買note7"); map1.put("time", 17567898762L); list.add(map1); /******************/ HashMap<String, Object> map2 = new HashMap<>(); map2.put("icon", R.mipmap.icon1); map2.put("title", "QQ郵箱提醒"); map2.put("msg", "阿斯頓飛開了家"); map2.put("time", 17567898762L); list.add(map2); /******************/ HashMap<String, Object> map3 = new HashMap<>(); map3.put("icon", R.mipmap.icon2); map3.put("title", "微信活動"); map3.put("msg", "日體育偶朋友"); map3.put("time", 17567898762L); list.add(map3); /******************/ HashMap<String, Object> map4 = new HashMap<>(); map4.put("icon", R.mipmap.icon3); map4.put("title", "美團外賣"); map4.put("msg", "飛規劃局快樂空間和高峰過后"); map4.put("time", 17567898762L); list.add(map4); /******************/ String from[] = {"title", "icon", "msg", "time"}; int to[] = {R.id.tv_title, R.id.imageView, R.id.tv_lastMsg, R.id.tv_time}; //創建SimpleAdapter /** * @params 參數一:Context * @params 參數二:數據源,List<? extends Map<String,?>> * @params 參數三:布局 Layout ID * @params 參數四:由參數二 的 每一個Map 的 key 所組成的 String[] * @params 參數五:由參數三 布局 里面的子控件的 View 的 ID 組成的 int[] * * 注意:參數四與參數五,一個數據對應一個view。數組的數據和控件下標位置要對齊 * */ SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.view_list_item_wechat, from, to); //ViewBind adapter.setViewBinder(new SimpleAdapter.ViewBinder() { @Override public boolean setViewValue(View view, Object data, String textRepresentation) { if (view.getId() == R.id.tv_time && data instanceof Long) { //如果是時間,我們自己綁定 TextView t = (TextView) view; Long time = Long.parseLong(data + ""); t.setText(new SimpleDateFormat("HH:mm", Locale.CHINA).format(new Date(time))); return true;//返回true 自己綁定數據 } if (view instanceof ImageView) { //如果是ImageView 添加一個點擊事件 view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "點擊了頭像", Toast.LENGTH_SHORT).show(); } }); } return false;//交給系統綁定 } }); lv_show.setAdapter(adapter); //設置ListView 的 Item的點擊事件 lv_show.setOnItemClickListener(new AdapterView.OnItemClickListener() { /** * * @param parent 設置該點擊事件的 AdapterView 本身(ListView) * @param view 點擊的Item的視圖對象 * @param position 視圖在 Adapter 中的位置 * @param id 視圖在ListView 中的行的下標 */ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d(TAG, "parent=" + parent); Log.d(TAG, "view=" + view); Log.d(TAG, "position=" + position); Log.d(TAG, "id=" + id); HashMap<String, Object> map = list.get(position); Object title = map.get("title"); Toast.makeText(MainActivity.this, "" + title, Toast.LENGTH_SHORT).show(); } }); }}1.重用了convertView,很大程度上的減少了內存的消耗。通過判斷convertView是否為null,是的話就需要產生一個View出來,然后給這個視圖數據,最后將這個視圖返回給底層,呈獻給用戶。
2.ViewHolder 為static,也就是靜態的,靜態類只會在第一次加載時 會耗費比較長時間,但是后面就可以很好幫助加載,同時保證了內存中只有一個ViewHolder,節省了內存的開銷
3.給contentView設置tag(setTag()),傳入一個viewHolder對象,用于緩存要顯示的數據
4.圖片采用異步加載方式
5.如果listview需要顯示的item很多,就要考慮分頁加載。比如一共要顯示100條或者更多的時候,我們可以考慮先加載20條,等用戶拉到列表底部的時候再去 加載接下來的20條
6.盡量避免在ListView適配器中使用線程,因為線程是產生內存泄露的主要原因在于線程生命周期的不可控制
7.ListView布局的layout_height盡量使用march_parent防止用戶誤操作屏幕導致重復調用getview方法
@Override public View getView(int position, View convertView, ViewGroup parent) { View layout = null; if (convertView == null) { layout = getLayoutInflater().inflate(R.layout.activity_main, parent, false); //在這里去 findViewById 以及更新UI }else{//如果 convertView 不等于 null 了。則不需要再去加載一次布局 layout = convertView; //在這里去 findViewById 以及更新UI } return layout; }例: MainActivity
public class MainActivity extends AppCompatActivity { private ListView lv_show; private ArrayList<Student> data; private int images[] = {R.mipmap.icon0, R.mipmap.icon1, R.mipmap.icon2, R.mipmap.icon3, R.mipmap.icon4, R.mipmap.icon5, R.mipmap.icon6, R.mipmap.icon7, R.mipmap.icon8, R.mipmap.icon9, R.mipmap.icon10, R.mipmap.icon11}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv_show = (ListView) findViewById(R.id.lv_show); initData(); MyAdapter myAdapter = new MyAdapter(this, data); lv_show.setAdapter(myAdapter); lv_show.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MainActivity.this,"id="+id,Toast.LENGTH_SHORT).show(); } }); } //初始化數據 private void initData() { data = new ArrayList<>(); for (int i = 0; i < images.length; i++) { long l = System.currentTimeMillis(); data.add(new Student(images[i], "標題" + i, "最后一條消息" + i, l - i * 10000)); } }}MyAdapter
public class MyAdapter extends BaseAdapter { private Context mContext; private List<Student> mData; public MyAdapter(Context context, List<Student> data) { this.mData = data; this.mContext = context; } //適配器的Item的長度 @Override public int getCount() { return mData == null ? 0 : mData.size(); } //每個Item將要顯示的視圖(View) @Override public View getView(int position, View convertView, ViewGroup parent) { Student student = getItem(position); String title = student.getTitle(); if ("標題5".equals(title)) { View view = LayoutInflater.from(mContext).inflate(R.layout.view_list_item_news, parent, false); return view; } View view = LayoutInflater.from(mContext).inflate(R.layout.view_list_item_wechat, parent, false); ImageView imageView = (ImageView) view.findViewById(R.id.imageView); TextView tv_title = (TextView) view.findViewById(R.id.tv_title); TextView tv_lastMsg = (TextView) view.findViewById(R.id.tv_lastMsg); TextView tv_time = (TextView) view.findViewById(R.id.tv_time); imageView.setImageResource(student.getImageResId()); tv_title.setText(title); tv_lastMsg.setText(student.getLastMsg()); //格式化時間 String dataStr = new SimpleDateFormat("HH:mm", Locale.CHINA).format(new Date(student.getTime())); tv_time.setText(dataStr); return view; } //開發者自己實現,一般用來得到當前 position位置的 數據 @Override public Student getItem(int position) { return mData.get(position); } //當用戶設置了 ListView的Item的點擊事件的時候,講此值 作為第四個參數傳遞 @Override public long getItemId(int position) { return 0; }}Student
public class Student { private int imageResId; private String title; private String lastMsg; private long time; @Override public String toString() { return "Student{" + "imageResId=" + imageResId + ", title='" + title + '/'' + ", lastMsg='" + lastMsg + '/'' + ", time=" + time + '}'; } public Student() { } public Student(int imageResId, String title, String lastMsg, long time) { this.imageResId = imageResId; this.title = title; this.lastMsg = lastMsg; this.time = time; } public int getImageResId() { return imageResId; } public void setImageResId(int imageResId) { this.imageResId = imageResId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getLastMsg() { return lastMsg; } public void setLastMsg(String lastMsg) { this.lastMsg = lastMsg; } public long getTime() { return time; } public void setTime(long time) { this.time = time; }}上面寫法沒有布局復用
ConvertView復用寫法(使用RecycleBin機制 復用Item布局,防止OOM)
public class MainActivity extends AppCompatActivity { private Integer[] imageResId = {R.mipmap.a, R.mipmap.b, R.mipmap.c, R.mipmap.e , R.mipmap.f, R.mipmap.g, R.mipmap.h, R.mipmap.i}; private ListView lv_show; private List<Integer> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv_show = (ListView) findViewById(R.id.lv_show); for (int i = 0; i < 60; i++) { list.addAll(Arrays.asList(imageResId)); } lv_show.setAdapter(new MyAdapter()); } class MyAdapter extends BaseAdapter { class ViewHolder { TextView tv_title; ImageView iv_icon; } @Override public int getCount() { return list == null ? 0 : list.size(); } @Override public Integer getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { Integer data = getItem(position);//當前行的數據 View layout; ViewHolder viewHolder; if (convertView == null) { layout = getLayoutInflater().inflate(R.layout.view_list_item_wechat, parent, false); viewHolder = new ViewHolder();// 當前Item 的布局的子控件的引用的集合 viewHolder.tv_title = (TextView) layout.findViewById(R.id.tv_title); viewHolder.iv_icon = (ImageView) layout.findViewById(R.id.imageView); layout.setTag(viewHolder);//把該布局的 子控件的引用 綁定到該布局上 } else { viewHolder = (ViewHolder) convertView.getTag(); layout = convertView; } viewHolder.tv_title.setText("Item " + position); viewHolder.iv_icon.setImageResource(data); return layout; } }}1、使用Adapter提供的convertView 進行復用ItemView 2、使用ViewHolder 減少 findviewbyid 調用次數 3、 Listview 被多層嵌套,多次的onMessure導致卡頓,需要減少嵌套的層數 4、如果多層嵌套無法避免,建議把Listview的高和寬設置為 match_parent 5、使用分頁,減少每次ListView加載的數據 6、如果顯示圖片,可以對圖片進行緩存,減少加載的 7、減少不必要的視圖更新
新聞熱點
疑難解答