`

ListView用法总结

阅读更多
一:Adapter优化
什么是Adapter,可以先看看我的上一篇文章,Android开发——说说Adapter那点事 Adapter与View的连接主要依靠getView这个方法返回我们需要的自定义view。ListView是Android app中一个最最最常用的控件了,所以如何让ListView流畅运行,获取良好的用户体验是非常重要的。对ListView优化就是对Adapter中的getView方法进行优化。09年的Google IO大会给出的优化建议如下:
@Override
 public View getView(int position, View convertView, ViewGroup parent) {
     Log.d("MyAdapter", "Position:" + position + "---"
             + String.valueOf(System.currentTimeMillis()));
     ViewHolder holder;
     if (convertView == null) {
         final LayoutInflater inflater = (LayoutInflater) mContext
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         convertView = inflater.inflate(R.layout.list_item_icon_text, null);
         holder = new ViewHolder();
         holder.icon = (ImageView) convertView.findViewById(R.id.icon);
         holder.text = (TextView) convertView.findViewById(R.id.text);
         convertView.setTag(holder);
     } else {
         holder = (ViewHolder) convertView.getTag();
     }
     holder.icon.setImageResource(R.drawable.icon);
     holder.text.setText(mData[position]);
     return convertView;
 }
  
 static class ViewHolder {
     ImageView icon;
  
     TextView text;
 }

以上是Google io大会上给出的优化建议,经过尝试ListView确实流畅了许多。

不建议的做法:
   1: @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         Log.d("MyAdapter", "Position:" + position + "---"
                 + String.valueOf(System.currentTimeMillis()));
             final LayoutInflater inflater = (LayoutInflater) mContext
                     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             View v = inflater.inflate(R.layout.list_item_icon_text, null);
             ((ImageView) v.findViewById(R.id.icon)).setImageResource(R.drawable.icon);
             ((TextView) v.findViewById(R.id.text)).setText(mData[position]);
           return v;
    }




二:美化你的android程序:自定义ListView背景:
在Android中,ListView是最常用的一个控件,在做UI设计的时候,很多人希望能够改变一下它的背景,使他能够符合整体的UI设计,改变背景背很简单只需要准备一张图片然后指定属性 android:background="@drawable/bg",不过不要高兴地太早,当你这么做以后,发现背景是变了,但是当你拖动,或者点击list空白位置的时候发现ListItem都变成黑色的了,破坏了整体效果。
这是为什么呢?
这个要从Listview的效果说起,默认的ListItem背景是透明的,而ListView的背景是固定不变的,所以在滚动条滚动的过程中如果实时地去将当前每个Item的显示内容跟背景进行混合运算,所以android系统为了优化这个过程用,就使用了一个叫做android:cacheColorHint的属性,在黑色主题下默认的颜色值是#191919,所以就出现了刚才的画面,有一半是黑色的

那怎么办呢?
如果你只是换背景的颜色的话,可以直接指定android:cacheColorHint为你所要的颜色,如果你是用图片做背景的话,那也只要将android:cacheColorHint指定为透明(#00000000)就可以了,当然为了美化是要牺牲一些效率的。



三:ListView页眉页脚效果VS android背景渐变:
大家都知道,在我们调用ListView的addFooterView()方法给List增加一个页脚时,如果列表内容很多,超过了屏幕大小,那么页脚就看不到了,可我们一般想要的效果是如下图所示的,在ListView的内容超过屏幕时,页脚还在屏幕的底部。
[img]

[/img]
本文将介绍上图所示的ListView效果,同时介绍一下在android中如何实现渐变效果,就像上图中的页眉页脚的背景色一样。

实现上面的效果主要使用几个RelativeLayout标签和ListView组合即可,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<RelativeLayout
		android:id="@+id/listHeader"
		android:background="@drawable/jbshape"
		android:layout_alignParentTop="true"
		android:gravity="center_horizontal"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content">
		<TextView
			android:text="IdeasAndroid 列表演示"
			android:textColor="#000000"
			android:textSize="18dip"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content" />
	</RelativeLayout>
	<RelativeLayout
		android:id="@+id/listFooter"
		android:background="@drawable/jbshape"
		android:gravity="center_horizontal"
		android:layout_alignParentBottom="true"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content">
		<Button
			android:id="@+id/prePage"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="上一页" 
			android:layout_alignParentLeft="true" />
		<Button
			android:layout_width="wrap_content"
			android:layout_gravity="right"
			android:layout_height="wrap_content"
			android:text="下一页"
			android:layout_toRightOf="@id/prePage" />
	</RelativeLayout>
	<ListView
		android:id="@+id/myListView"
		android:layout_width="fill_parent"
		android:layout_height="fill_parent"
		android:layout_below="@id/listHeader"
		android:layout_above="@id/listFooter" />
</RelativeLayout>

几个关键点:
1、在页眉(id为listHeader)使用属性android:layout_alignParentTop=”true”
声明页眉部分与父视图的顶部对齐。
2、在页脚(id为listFooter)使用属性android:layout_alignParentBottom=”true” 声明其与父视图的底部对齐。
3、在 ListView中使用属性android:layout_below=”@id/listHeader” android:layout_above=”@id/listFooter” 声明ListView位于listHeader的下方,位于listFooter的上方。

这样我们的页眉页脚效果就实现了。

再来看看是怎么实现渐变的?

我们在res/drawable目录下新建一个叫jbshape.xml的文件,内容如下所示:
<?xml version="1.0" encoding="utf-8"?>
<shape
	xmlns:android="http://schemas.android.com/apk/res/android" 
	android:shape="rectangle">
	<gradient
		android:startColor="#509245"
		android:centerColor="#3e8532" 
		android:endColor="#509245"
		android:type="linear"
		android:angle="90" 
		android:centerX="0.5"
		android:centerY="0.5" />
	<padding
		android:left="7dp"
		android:top="7dp"
		android:right="7dp" 
		android:bottom="7dp" />
	<corners
		android:radius="4dp" />
</shape> 

这里就不多讲了,相信你一看就能看懂,android:shape 配置的是图形的形式,主要包括方形、圆形等,本例中为方形。gradient节点主要配置起点颜色、终点颜色、中间点的坐标、中间点的颜色、渐变角度(90度为上下渐变,0为左右渐变),padding节点主要配置上下左右边距,corners节点配置四周园角的半径。更详细的配置参见http://www.ideasandroid.com/android/sdk/docs/guide/topics/resources/drawable-resource.html 。

使用渐变就更简单了,如第一部分代码中所示的,直接用android:background=”@drawable/jbshape” 配置背景为刚才配置的渐变图形即可。



四:让ListView显示在Button上面
[img]

[/img]
通常情况下ListView的android:layout_height属性我们都设置成"fill_parent",但是这样一来,它就会占据整个屏幕,如果它下面还有控件的话,就会被它“挤到”外面去。如何解决这种情况呢?表面上使用LinearLayout是最简单的,但就会出现上面的情形。那就使用RelativeLayout吧。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<Button
		android:id="@+id/btn_1"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="click_1"
		android:layout_alignParentLeft="true"
		android:layout_alignParentBottom="true" />
	<Button
		android:id="@+id/btn_0"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="click_0"
		android:layout_above="@id/btn_1"
		android:layout_alignParentLeft="true" />
	<ListView
		android:id="@+id/listview"
		android:layout_width="fill_parent"
		android:layout_height="fill_parent"
		android:layout_above="@id/btn_0"
		android:layout_alignParentLeft="true"
		android:layout_alignParentTop="true" />
</RelativeLayout>  

上面的布局仅作参考!
当然如果将ListView换成ScrollView也是一样的,这里只起到抛砖引玉的目的。

另外,你也可以在ListView中使用android:layout_weight="1",这样就可以将它放在LinearLayout,这样是最简单的




五:ListView列表分组的实现,效果图:
[img]

[/img]
布局文件:
group_list_item_tag.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:background="#555555"
	android:paddingLeft="10dip">
	<TextView
		android:id="@+id/group_list_item_text"
		android:layout_width="wrap_content"
		android:layout_height="20dip"
		android:textColor="#ffffff"
		android:gravity="center_vertical" />
</LinearLayout> 


group_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="horizontal"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:padding="5dip">     
	<!-- 图片和文字 -->    <!-- 随便放了一张图片,稍微美化一下 -->
	<ImageView
		android:src="@drawable/icon"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content" />
	<TextView
		android:id="@+id/group_list_item_text"
		android:layout_width="wrap_content"
		android:layout_height="fill_parent"
		android:paddingLeft="5dip"
		android:gravity="center_vertical" />
</LinearLayout>


group_list_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">    
	 <!--简单的列表显示-->
	<ListView
		android:id="@+id/group_list"
		android:layout_width="fill_parent"
		android:layout_height="fill_parent"
		android:cacheColorHint="#00000000" />
</LinearLayout> 


Activity代码:
package com.magus.list;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
	private GroupListAdapter adapter = null;
	private ListView listView = null;
	private List<String> list = new ArrayList<String>();
	private List<String> listTag = new ArrayList<String>();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.group_list_activity);
		setData();
		adapter = new GroupListAdapter(this, list, listTag);
		listView = (ListView) findViewById(R.id.group_list);
		listView.setAdapter(adapter);
	}

	public void setData() {
		list.add("A");
		listTag.add("A");
		for (int i = 0; i < 2; i++) {
			list.add("阿凡达" + i);
		}
		list.add("B");
		listTag.add("B");
		for (int i = 0; i < 2; i++) {
			list.add("比特风暴" + i);
		}
		list.add("C");
		listTag.add("C");
		for (int i = 0; i < 30; i++) {
			list.add("查理风云" + i);
		}
	}

	private static class GroupListAdapter extends ArrayAdapter<String> {
		private List<String> listTag = null;

		public GroupListAdapter(Context context, List<String> objects,
				List<String> tags) {
			super(context, 0, objects);
			this.listTag = tags;
		}

		@Override
		public boolean isEnabled(int position) {
			if (listTag.contains(getItem(position))) {
				return false;
			}
			return super.isEnabled(position);
		}

		public View getView(int position, View convertView, ViewGroup parent) {
			View view = convertView;
			if (listTag.contains(getItem(position))) {
				
				view = LayoutInflater.from(getContext()).inflate(
						R.layout.group_list_item_tag, null);
			} else {
				view = LayoutInflater.from(getContext()).inflate(
						R.layout.group_list_item, null);
			}
			//getItem(position) 得到的是list里面String,
			String str = getItem(position);
			System.out.println("getItem(position)="+str);
			
			TextView textView = (TextView) view
					.findViewById(R.id.group_list_item_text);
			textView.setText(getItem(position));
			return view;
		}
	}
}




六:ListView自适应实现表格
GridView比ListView更容易实现自适应的表格,但是GridView每个格单元的大小固定,而ListView实现的表格可以自定义每个格单元的大小,但因此实现自适应表格也会复杂些(格单元大小不一)。另外,GridView实现的表格可以定位在具体某个格单元,而ListView实现的表格则只能定位在表格行。因此还是那句老话:根据具体的使用环境而选择GridView 或者 ListView实现表格。

先贴出本文程序运行的效果图:
[img]

[/img]

布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<HorizontalScrollView
		android:id="@+id/HorizontalScrollView01"
		android:layout_height="fill_parent"
		android:layout_width="fill_parent">
		<ListView
			android:id="@+id/ListView01"
			android:layout_height="wrap_content"
			android:layout_width="wrap_content" />
	</HorizontalScrollView>
</LinearLayout>  


package com.magus.listview;  
import java.util.List;  
import android.content.Context;  
import android.graphics.Color;  
import android.view.Gravity;  
import android.view.View;  
import android.view.ViewGroup;  
import android.widget.BaseAdapter;  
import android.widget.ImageView;  
import android.widget.LinearLayout;  
import android.widget.TextView;  
public class TableAdapter extends BaseAdapter {  
    private Context context;  
    private List<TableRow> table;  
    public TableAdapter(Context context, List<TableRow> table) {  
        this.context = context;  
        this.table = table;  
    }  
    @Override  
    public int getCount() {  
        return table.size();  
    }  
    @Override  
    public long getItemId(int position) {  
        return position;  
    }  
    public TableRow getItem(int position) {  
        return table.get(position);  
    }  
    public View getView(int position, View convertView, ViewGroup parent) {  
        TableRow tableRow = table.get(position);  
        return new TableRowView(this.context, tableRow);  
    }  
    /** 
     * TableRowView 实现表格行的样式 
     * @author hellogv 
     */  
    class TableRowView extends LinearLayout {  
        public TableRowView(Context context, TableRow tableRow) {  
            super(context);  
              
            this.setOrientation(LinearLayout.HORIZONTAL);  
            for (int i = 0; i < tableRow.getSize(); i++) {//逐个格单元添加到行  
                TableCell tableCell = tableRow.getCellValue(i);  
                LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(  
                        tableCell.width, tableCell.height);//按照格单元指定的大小设置空间  
                layoutParams.setMargins(0, 0, 1, 1);//预留空隙制造边框  
                if (tableCell.type == TableCell.STRING) {//如果格单元是文本内容  
                    TextView textCell = new TextView(context);  
                    textCell.setLines(1);  
                    textCell.setGravity(Gravity.CENTER);  
                    textCell.setBackgroundColor(Color.BLACK);//背景黑色  
                    textCell.setText(String.valueOf(tableCell.value));  
                    addView(textCell, layoutParams);  
                } else if (tableCell.type == TableCell.IMAGE) {//如果格单元是图像内容  
                    ImageView imgCell = new ImageView(context);  
                    imgCell.setBackgroundColor(Color.BLACK);//背景黑色  
                    imgCell.setImageResource((Integer) tableCell.value);  
                    addView(imgCell, layoutParams);  
                }  
            }  
            this.setBackgroundColor(Color.WHITE);//背景白色,利用空隙来实现边框  
        }  
    }  
    /** 
     * TableRow 实现表格的行 
     * @author hellogv 
     */  
    static public class TableRow {  
        private TableCell[] cell;  
        public TableRow(TableCell[] cell) {  
            this.cell = cell;  
        }  
        public int getSize() {  
            return cell.length;  
        }  
        public TableCell getCellValue(int index) {  
            if (index >= cell.length)  
                return null;  
            return cell[index];  
        }  
    }  
    /** 
     * TableCell 实现表格的格单元 
     * @author hellogv 
     */  
    static public class TableCell {  
        static public final int STRING = 0;  
        static public final int IMAGE = 1;  
        public Object value;  
        public int width;  
        public int height;  
        private int type;  
        public TableCell(Object value, int width, int height, int type) {  
            this.value = value;  
            this.width = width;  
            this.height = height;  
            this.type = type;  
        }  
    }  
}  


package com.magus.listview;  
import java.util.ArrayList;  

import com.magus.listview.TableAdapter.TableCell;
import com.magus.listview.TableAdapter.TableRow;

import android.app.Activity;  
import android.os.Bundle;  
import android.view.View;  
import android.widget.AdapterView;  
import android.widget.ListView;  
import android.widget.LinearLayout.LayoutParams;  
import android.widget.Toast;  
/** 
 * @author hellogv 
 */  
public class testMyListView extends Activity {  
    /** Called when the activity is first created. */  
    ListView lv;  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        this.setTitle("ListView自适应实现表格---hellogv");  
        lv = (ListView) this.findViewById(R.id.ListView01);  
        ArrayList<TableRow> table = new ArrayList<TableRow>();  
        TableCell[] titles = new TableCell[5];// 每行5个单元  
        int width = this.getWindowManager().getDefaultDisplay().getWidth()/titles.length;  
        // 定义标题  
        for (int i = 0; i < titles.length; i++) {  
            titles[i] = new TableCell("标题" + String.valueOf(i),   
                                    width + 8 * i,  
                                    LayoutParams.FILL_PARENT,   
                                    TableCell.STRING);  
        }  
        table.add(new TableRow(titles));  
        // 每行的数据  
        TableCell[] cells = new TableCell[5];// 每行5个单元  
        for (int i = 0; i < cells.length - 1; i++) {  
            cells[i] = new TableCell("No." + String.valueOf(i),  
                                    titles[i].width,   
                                    LayoutParams.FILL_PARENT,   
                                    TableCell.STRING);  
        }  
        cells[cells.length - 1] = new TableCell(R.drawable.icon,  
                                                titles[cells.length - 1].width,   
                                                LayoutParams.WRAP_CONTENT,  
                                                TableCell.IMAGE);  
        // 把表格的行添加到表格  
        for (int i = 0; i < 12; i++)  
            table.add(new TableRow(cells));  
        TableAdapter tableAdapter = new TableAdapter(this, table);  
        lv.setAdapter(tableAdapter);  
        lv.setOnItemClickListener(new ItemClickEvent());  
    }  
    class ItemClickEvent implements AdapterView.OnItemClickListener {  
        @Override  
        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,  
                long arg3) {  
            Toast.makeText(testMyListView.this, "选中第"+String.valueOf(arg2)+"行", 500).show();  
        }  
    }  
}  




七:延时加载的ListView
效果图:

[img]

[/img]

项目结构图:

[img]

[/img]

布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<ProgressBar
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:indeterminateOnly="true"
   />
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Loading please wait..."
    />
</LinearLayout>


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <ListView 
    android:id="@+id/list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
  />
</LinearLayout>


package com.ql.app;

/*
 * Copyright (C) 2010 Makas Tzavellas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.ql.service.AbstractPageableAdapter;
import com.ql.service.DataLoaderHandler;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TextView;

/**
 * Sample Activity that contains the Pageable list view.
 * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
 *
 */
public class SampleActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ListView listView = (ListView)findViewById(R.id.list);
        SampleDataLoaderHandler handler = new SampleDataLoaderHandler();
        PageableAdapter adapter = new PageableAdapter(listView, this, R.layout.loading, handler);
        listView.setAdapter(adapter);
        // Set the adapter as the on scroll listener.
        // It is done here just incase the activity or other class needs to listen to onScroll event too.
        listView.setOnScrollListener(adapter);
    }
    
    /**
     * Sample Pageable adapter that extends {@link AbstractPageableAdapter}
     * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
     *
     */
    private class PageableAdapter extends AbstractPageableAdapter<String> {

        public PageableAdapter(ListView listView, Context context, int loadingViewResourceId, DataLoaderHandler<String> handler) {
            super(listView, context, loadingViewResourceId, handler);
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = getLayoutInflater().inflate(android.R.layout.simple_list_item_1, parent, false);
            }
            //((TextView)convertView).setText(getItem(position).toString());
            ((TextView)convertView).setText(getItem(position).toString()+"哈哈无敌小胖胖");
            return convertView;
        }
        
    }
}


package com.ql.app;

/*
 * Copyright (C) 2010 Makas Tzavellas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import android.os.AsyncTask;

import java.util.ArrayList;

import com.ql.service.DataLoaderHandler;
import com.ql.service.DataResponseHandler;

/**
 * Sample implementation of the {@link DataLoaderHandler}
 * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
 *
 */
public class SampleDataLoaderHandler implements DataLoaderHandler<String>, DataResponseHandler<SampleDataResult<String>> {
    private static final int MAX_SIZE_PER_PAGE = 10;
    private static final int MAX_ITEMS = 51;

    private boolean mLoading;
    private ArrayList<String> mValues = new ArrayList<String>();
    private DataLoadedCallback<String> mCallback;
    private int mMaxItems;
    
    public int getMaxItems() {
        return mMaxItems;
    }

    public void getNext(DataLoadedCallback<String> callback) {
        mLoading = true;
        mCallback = callback;
        new SampleBackgroundTask(this).execute(mValues.size());
    }

    public void getValues(DataLoadedCallback<String> callback) {
        if (mValues.isEmpty()) {
            // No data has been loaded before.
            // Load the initial data which includes the max size.
            mLoading = true;
            mCallback = callback;
            new SampleFirstBackgroundTask(this).execute();
        } else {
            callback.dataLoaded(mValues);
        }
    }

    public boolean isLoading() {
        return mLoading;
    }

    public void resultAvailable(int status, SampleDataResult<String> result) {
        mLoading = false;
        if (mMaxItems == 0)
            mMaxItems = result.maxItems;
        
        mValues.addAll(result.values);
        
        mCallback.dataLoaded(result.values);
    }
    
    /**
     * Sample implementation when loading the data the first time.
     * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
     *
     */
    private static class SampleFirstBackgroundTask extends AsyncTask<Void, Void, SampleDataResult<String>> {
        private DataResponseHandler<SampleDataResult<String>> mResponseHandler;
        
        private SampleFirstBackgroundTask(DataResponseHandler<SampleDataResult<String>> responseHandler) {
            mResponseHandler = responseHandler;
        }
        
        @Override
        protected SampleDataResult<String> doInBackground(Void... params) {
            // Put whatever info
            ArrayList<String> values = new ArrayList<String>(MAX_SIZE_PER_PAGE);
            for (int i = 0; i < MAX_SIZE_PER_PAGE; i++) {
                values.add("" + i);
            }
            SampleDataResult<String> result = new SampleDataResult<String>();
            // Since this is loaded the first time, the max item should be made available.
            result.maxItems = MAX_ITEMS;
            result.values = values;
            try {
                // Pause for 5 seconds to simulate "busy" state.
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return result;
        }

        @Override
        protected void onPostExecute(SampleDataResult<String> result) {
            mResponseHandler.resultAvailable(0, result);
        }
        
    }

    /**
     * Sample implementation that "downloads" more data.
     * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
     *
     */
    private class SampleBackgroundTask extends AsyncTask<Integer, Void, SampleDataResult<String>> {
        private DataResponseHandler<SampleDataResult<String>> mResponseHandler;
        
        private SampleBackgroundTask(DataResponseHandler<SampleDataResult<String>> responseHandler) {
            mResponseHandler = responseHandler;
        }

        @Override
        protected SampleDataResult<String> doInBackground(Integer... params) {
            int currentSize = params[0];
            ArrayList<String> values = new ArrayList<String>(MAX_SIZE_PER_PAGE);
            
            int maxSize = currentSize + MAX_SIZE_PER_PAGE;
            if (maxSize > MAX_ITEMS)
                maxSize = MAX_ITEMS;
            
            for (int i = currentSize; i < maxSize; i++) {
                values.add("" + i);
            }
            SampleDataResult<String> result = new SampleDataResult<String>();
            result.values = values;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return result;
        }
        
        @Override
        protected void onPostExecute(SampleDataResult<String> result) {
            mResponseHandler.resultAvailable(0, result);
        }
    }

}


package com.ql.app;

/*
 * Copyright (C) 2010 Makas Tzavellas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.util.ArrayList;

/**
 * Sample class that contains the download info.
 * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
 *
 * @param <T>
 */
public class SampleDataResult<T> {
    public int maxItems;

    public ArrayList<T> values;
}



package com.ql.service;

/*
 * Copyright (C) 2010 Makas Tzavellas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ListView;

import java.util.ArrayList;

import com.ql.service.DataLoaderHandler.DataLoadedCallback;

/**
 * Abstract adapter which will display the "busy" view as footer when a background download occurs.
 * Also calls the {@link DataLoaderHandler} for its initial values as well as subsequent items as 
 * the user scrolls to the end.
 * 
 * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
 *
 * @param <T>
 */
public abstract class AbstractPageableAdapter<T> extends BaseAdapter implements AbsListView.OnScrollListener, DataLoadedCallback<T> {

    private DataLoaderHandler<T> mDataLoaderHandler;
    private ListView mListView;
    private View mLoadingView;
    private Context mContext;
    private ArrayList<T> mList = new ArrayList<T>();

    /**
     * 
     * @param listView The list view that this adapter will be added to.
     * @param context The activity context.
     * @param loadingViewResourceId The layout to use when displaying the "busy" status
     * @param handler The handler to use when loading more data to the list view.
     */
    public AbstractPageableAdapter(ListView listView, Context context, int loadingViewResourceId, DataLoaderHandler<T> handler) {
        mContext = context;
        mDataLoaderHandler = handler;
        mListView = listView;
        mLoadingView = LayoutInflater.from(context).inflate(loadingViewResourceId, null);
        showLoading(true);
        mDataLoaderHandler.getValues(this);
    }
    
    /**
     * The {@link Context} passed in the constructor.
     * @return
     */
    public final Context getContext() {
        return mContext;
    }
    
    public final int getCount() {
        return mList.size();
    }

    public Object getItem(int position) {
        return mList.get(position);
    }

    public long getItemId(int position) {
        return position;
    }

    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
            int totalItemCount) {
        // Nothing to do I suppose...
        
    }

    public void onScrollStateChanged(final AbsListView view, final int scrollState) {
        switch (scrollState) {
            case SCROLL_STATE_IDLE: {
                final int maxItems = mDataLoaderHandler.getMaxItems();
                final int first = mListView.getFirstVisiblePosition();
                final int count = mListView.getChildCount();
                final int total = getCount();
                
                if (first + count < total || mDataLoaderHandler.isLoading())
                    return;
                
                if (total < maxItems) {
                    showLoading(true);
                    mDataLoaderHandler.getNext(this);
                }
                break;
            }
        }
    }
    
    private void showLoading(boolean show) {
        if (show)
            mListView.addFooterView(mLoadingView);
        else
            mListView.removeFooterView(mLoadingView);
    }
    
    public void dataLoaded(ArrayList<T> values) {
        ArrayList<T> list = mList;
        for (T value : values) {
            list.add(value);
        }
        showLoading(false);
        notifyDataSetChanged();
    }
    
}


package com.ql.service;

/*
 * Copyright (C) 2010 Makas Tzavellas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.ArrayList;

/**
 * 
 * The implementation of the interface is responsible to load data in the background.
 * 
 * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
 * 
 * @param <T>
 */
public interface DataLoaderHandler<T> {
    /**
     * The maximum item that can be loaded.
     * This method should probably only be called after the initial download has occured.
     * @return The max item available.
     */
    int getMaxItems();
    
    /**
     * Returns values that has been loaded or may trigger the download of the first batch.
     * @param callback The callback to notify when data is available.
     */
    void getValues(DataLoadedCallback<T> callback);
    /**
     * Returns the next batch of data.
     * @param callback The callback to notify when data is available.
     */
    void getNext(DataLoadedCallback<T> callback);

    /**
     * True if the implementation is busy loading data in the background.
     * @return
     */
    boolean isLoading();
    
    /**
     * The implementation of this interface will be called when the data has been downloaded.
     * 
     * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
     * 
     * @param <T>
     */
    interface DataLoadedCallback<T> {
        /**
         * Notifies the implementation that the download has completed with a list of values.
         * @param values The new values downloaded.
         */
        void dataLoaded(ArrayList<T> values);
    }
}


package com.ql.service;

/*
 * Copyright (C) 2010 Makas Tzavellas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * Default interface to use when receiving a response from server.
 * 
 * @author Makas Tzavellas (makas dot tzavellas at gmail dot com)
 *
 * @param <T>
 */
public interface DataResponseHandler<T> {
    /**
     * Notify the implementation that response is available.
     * @param status The status of the download. Whether it was successful or failed.
     * @param result The result of the download if any.
     */
    void resultAvailable(int status, T result);
}




八:自定义ListView圆角
String[] mStrings = { "aaaa", "bbbb", "cccc" };
ListView listView = (ListView) findViewById(R.id.list);
		listView.setAdapter(new ArrayAdapter<String>(this,
				android.R.layout.simple_list_item_1, mStrings));


<ListView android:id="@+id/list" android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:background="@layout/list_corner_1"
		android:layout_weight="1.0" />


<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:shape="rectangle">
	<gradient android:startColor="#CCCCFF" android:endColor="#99CCFF"
		android:angle="90" />
	<corners android:bottomRightRadius="10dp"
		android:bottomLeftRadius="10dp" android:topLeftRadius="10dp"
		android:topRightRadius="10dp" />
</shape>




九:ListView分组查询
请看效果图:
[img]

[/img]
工程结构图:
[img]

[/img]

布局文件:
city_list_item_city_index.xml:
<?xml version="1.0" encoding="utf-8"?>


<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">

	<TextView
		xmlns:android="http://schemas.android.com/apk/res/android"
		android:id="@+id/city_list_item_city_index"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:paddingLeft="14dip"
		android:paddingRight="14dip"
		android:gravity="center_vertical"
		android:background="#FFc0c0c0"
		android:textColor="#FF545454"
		android:textSize="18dip" />

</LinearLayout>



city_list_item_city.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">

	<TextView
		xmlns:android="http://schemas.android.com/apk/res/android"
		android:id="@+id/city_list_item_city"
		android:layout_width="fill_parent"
		android:layout_height="44dip"
		android:paddingLeft="14dip"
		android:paddingRight="14dip"
		android:gravity="center_vertical"
		android:checkMark="?android:attr/listChoiceIndicatorSingle"
		android:textColor="#FF333333"
		android:textSize="18dip" />


</LinearLayout>




city_list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/city_list_item"
	android:layout_width="fill_parent"
	android:layout_height="44dip"
	android:paddingLeft="14dip"
	android:paddingRight="14dip"
	android:gravity="center_vertical"
	android:checkMark="?android:attr/listChoiceIndicatorSingle"
	android:textColor="#FF333333"
	android:textSize="18dip" />



city_select.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white"
    android:orientation="vertical"
    android:padding="5dip" >

    <EditText
        android:id="@+id/edit_filter"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/input"
        android:hint="@string/str_region_filter" />

    <ListView
        android:id="@+id/list_view"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:cacheColorHint="#00000000"
        android:dividerHeight="1dip" />

</LinearLayout>



list_position.xml
<?xml version="1.0" encoding="utf-8"?>
	<!--
		Copyright (C) 2006 The Android Open Source Project Licensed under the
		Apache License, Version 2.0 (the "License"); you may not use this file
		except in compliance with the License. You may obtain a copy of the
		License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
		by applicable law or agreed to in writing, software distributed under
		the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
		CONDITIONS OF ANY KIND, either express or implied. See the License for
		the specific language governing permissions and limitations under the
		License.
	-->

<TextView
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:textSize="50sp"
	android:textColor="#5B5B5B5B"
	android:background="@drawable/bg_city_select_list_index"
	android:minWidth="70dip"
	android:maxWidth="70dip"
	android:padding="10dip"
	android:gravity="center" />



values/colors.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<color name="white">#FFFFFF</color><!--白色 -->
<color name="twhite">#7FFFFFFF</color><!--白色 -->
<color name="ivory">#FFFFF0</color><!--象牙色 -->
<color name="lightyellow">#FFFFE0</color><!--亮黄色 -->
<color name="yellow">#FFFF00</color><!--黄色 -->
<color name="snow">#FFFAFA</color><!--雪白色 -->
<color name="floralwhite">#FFFAF0</color><!--花白色 -->
<color name="lemonchiffon">#FFFACD</color><!--柠檬绸色 -->
<color name="cornsilk">#FFF8DC</color><!--米绸色 -->
<color name="seashell">#FFF5EE</color><!--海贝色 -->
<color name="lavenderblush">#FFF0F5</color><!--淡紫红 -->
<color name="papayawhip">#FFEFD5</color><!--番木色 -->
<color name="blanchedalmond">#FFEBCD</color><!--白杏色 -->
<color name="mistyrose">#FFE4E1</color><!--浅玫瑰色 -->
<color name="bisque">#FFE4C4</color><!--桔黄色 -->
<color name="moccasin">#FFE4B5</color><!--鹿皮色 -->
<color name="navajowhite">#FFDEAD</color><!--纳瓦白 -->
<color name="peachpuff">#FFDAB9</color><!--桃色 -->
<color name="gold">#FFD700</color><!--金色 -->
<color name="pink">#FFC0CB</color><!--粉红色 -->
<color name="lightpink">#FFB6C1</color><!--亮粉红色 -->
<color name="orange">#FFA500</color><!--橙色 -->
<color name="lightsalmon">#FFA07A</color><!--亮肉色 -->
<color name="darkorange">#FF8C00</color><!--暗桔黄色 -->
<color name="coral">#FF7F50</color><!--珊瑚色 -->
<color name="hotpink">#FF69B4</color><!--热粉红色 -->
<color name="tomato">#FF6347</color><!--西红柿色 -->
<color name="orangered">#FF4500</color><!--红橙色 -->
<color name="deeppink">#FF1493</color><!--深粉红色 -->
<color name="fuchsia">#FF00FF</color><!--紫红色 -->
<color name="magenta">#FF00FF</color><!--红紫色 -->
<color name="red">#FF0000</color><!--红色 -->
<color name="oldlace">#FDF5E6</color><!--老花色 -->
<color name="lightgoldenrodyellow">#FAFAD2</color><!--亮金黄色 -->
<color name="linen">#FAF0E6</color><!--亚麻色 -->
<color name="antiquewhite">#FAEBD7</color><!--古董白 -->
<color name="salmon">#FA8072</color><!--鲜肉色 -->
<color name="ghostwhite">#F8F8FF</color><!--幽灵白 -->
<color name="mintcream">#F5FFFA</color><!--薄荷色 -->
<color name="whitesmoke">#F5F5F5</color><!--烟白色 -->
<color name="beige">#F5F5DC</color><!--米色 -->
<color name="wheat">#F5DEB3</color><!--浅黄色 -->
<color name="sandybrown">#F4A460</color><!--沙褐色 -->
<color name="azure">#F0FFFF</color><!--天蓝色 -->
<color name="honeydew">#F0FFF0</color><!--蜜色 -->
<color name="aliceblue">#F0F8FF</color><!--艾利斯兰 -->
<color name="khaki">#F0E68C</color><!--黄褐色 -->
<color name="lightcoral">#F08080</color><!--亮珊瑚色 -->
<color name="palegoldenrod">#EEE8AA</color><!--苍麒麟色 -->
<color name="violet">#EE82EE</color><!--紫罗兰色 -->
<color name="darksalmon">#E9967A</color><!--暗肉色 -->
<color name="lavender">#E6E6FA</color><!--淡紫色 -->
<color name="lightcyan">#E0FFFF</color><!--亮青色 -->
<color name="burlywood">#DEB887</color><!--实木色 -->
<color name="plum">#DDA0DD</color><!--洋李色 -->
<color name="gainsboro">#DCDCDC</color><!--淡灰色 -->
<color name="crimson">#DC143C</color><!--暗深红色 -->
<color name="palevioletred">#DB7093</color><!--苍紫罗兰色 -->
<color name="goldenrod">#DAA520</color><!--金麒麟色 -->
<color name="orchid">#DA70D6</color><!--淡紫色 -->
<color name="thistle">#D8BFD8</color><!--蓟色 -->
<color name="lightgray">#D3D3D3</color><!--亮灰色 -->
<color name="lightgrey">#D3D3D3</color><!--亮灰色 -->
<color name="tan">#D2B48C</color><!--茶色 -->
<color name="chocolate">#D2691E</color><!--巧可力色 -->
<color name="peru">#CD853F</color><!--秘鲁色 -->
<color name="indianred">#CD5C5C</color><!--印第安红 -->
<color name="mediumvioletred">#C71585</color><!--中紫罗兰色 -->
<color name="silver">#C0C0C0</color><!--银色 -->
<color name="darkkhaki">#BDB76B</color><!--暗黄褐色 -->
<color name="rosybrown">#BC8F8F</color><!--褐玫瑰红 -->
<color name="mediumorchid">#BA55D3</color><!--中粉紫色 -->
<color name="darkgoldenrod">#B8860B</color><!--暗金黄色 -->
<color name="firebrick">#B22222</color><!--火砖色 -->
<color name="powderblue">#B0E0E6</color><!--粉蓝色 -->
<color name="lightsteelblue">#B0C4DE</color><!--亮钢兰色 -->
<color name="paleturquoise">#AFEEEE</color><!--苍宝石绿 -->
<color name="greenyellow">#ADFF2F</color><!--黄绿色 -->
<color name="lightblue">#ADD8E6</color><!--亮蓝色 -->
<color name="darkgray">#A9A9A9</color><!--暗灰色 -->
<color name="darkgrey">#A9A9A9</color><!--暗灰色 -->
<color name="brown">#A52A2A</color><!--褐色 -->
<color name="sienna">#A0522D</color><!--赭色 -->
<color name="darkorchid">#9932CC</color><!--暗紫色 -->
<color name="palegreen">#98FB98</color><!--苍绿色 -->
<color name="darkviolet">#9400D3</color><!--暗紫罗兰色 -->
<color name="mediumpurple">#9370DB</color><!--中紫色 -->
<color name="lightgreen">#90EE90</color><!--亮绿色 -->
<color name="darkseagreen">#8FBC8F</color><!--暗海兰色 -->
<color name="saddlebrown">#8B4513</color><!--重褐色 -->
<color name="darkmagenta">#8B008B</color><!--暗洋红 -->
<color name="darkred">#8B0000</color><!--暗红色 -->
<color name="blueviolet">#8A2BE2</color><!--紫罗兰蓝色 -->
<color name="lightskyblue">#87CEFA</color><!--亮天蓝色 -->
<color name="skyblue">#87CEEB</color><!--天蓝色 -->
<color name="gray">#808080</color><!--灰色 -->
<color name="olive">#808000</color><!--橄榄色 -->
<color name="purple">#800080</color><!--紫色 -->
<color name="maroon">#800000</color><!--粟色 -->
<color name="aquamarine">#7FFFD4</color><!--碧绿色 -->
<color name="chartreuse">#7FFF00</color><!--黄绿色 -->
<color name="lawngreen">#7CFC00</color><!--草绿色 -->
<color name="mediumslateblue">#7B68EE</color><!--中暗蓝色 -->
<color name="lightslategray">#778899</color><!--亮蓝灰 -->
<color name="lightslategrey">#778899</color><!--亮蓝灰 -->
<color name="slategray">#708090</color><!--灰石色 -->
<color name="slategrey">#708090</color><!--灰石色 -->
<color name="olivedrab">#6B8E23</color><!--深绿褐色 -->
<color name="slateblue">#6A5ACD</color><!--石蓝色 -->
<color name="dimgray">#696969</color><!--暗灰色 -->
<color name="dimgrey">#696969</color><!--暗灰色 -->
<color name="mediumaquamarine">#66CDAA</color><!--中绿色 -->
<color name="cornflowerblue">#6495ED</color><!--菊兰色 -->
<color name="cadetblue">#5F9EA0</color><!--军兰色 -->
<color name="darkolivegreen">#556B2F</color><!--暗橄榄绿 -->
<color name="indigo">#4B0082</color><!--靛青色 -->
<color name="mediumturquoise">#48D1CC</color><!--中绿宝石 -->
<color name="darkslateblue">#483D8B</color><!--暗灰蓝色 -->
<color name="steelblue">#4682B4</color><!--钢兰色 -->
<color name="royalblue">#4169E1</color><!--皇家蓝 -->
<color name="turquoise">#40E0D0</color><!--青绿色 -->
<color name="mediumseagreen">#3CB371</color><!--中海蓝 -->
<color name="limegreen">#32CD32</color><!--橙绿色 -->
<color name="darkslategray">#2F4F4F</color><!--暗瓦灰色 -->
<color name="darkslategrey">#2F4F4F</color><!--暗瓦灰色 -->
<color name="seagreen">#2E8B57</color><!--海绿色 -->
<color name="forestgreen">#228B22</color><!--森林绿 -->
<color name="lightseagreen">#20B2AA</color><!--亮海蓝色 -->
<color name="dodgerblue">#1E90FF</color><!--闪兰色 -->
<color name="midnightblue">#191970</color><!--中灰兰色 -->
<color name="aqua">#00FFFF</color><!--浅绿色 -->
<color name="cyan">#00FFFF</color><!--青色 -->
<color name="springgreen">#00FF7F</color><!--春绿色 -->
<color name="lime">#00FF00</color><!--酸橙色 -->
<color name="mediumspringgreen">#00FA9A</color><!--中春绿色 -->
<color name="darkturquoise">#00CED1</color><!--暗宝石绿 -->
<color name="deepskyblue">#00BFFF</color><!--深天蓝色 -->
<color name="darkcyan">#008B8B</color><!--暗青色 -->
<color name="teal">#008080</color><!--水鸭色 -->
<color name="green">#008000</color><!--绿色 -->
<color name="darkgreen">#006400</color><!--暗绿色 -->
<color name="blue">#0099FF</color><!--蓝色 -->
<color name="mediumblue">#0000CD</color><!--中兰色 -->
<color name="darkblue">#00008B</color><!--暗蓝色 -->
<color name="navy">#000080</color><!--海军色 -->
<color name="black">#000000</color><!--黑色 -->
<color name="downloadbar">#525552</color>
</resources>


valus/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, EX02_ListviewAlphabeticalIndexActivity!</string>
    <string name="app_name">EX02_ListviewAlphabeticalIndex</string>
    <string name="str_region_filter">请输入城市首字母</string>
</resources>



AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest
	xmlns:android="http://schemas.android.com/apk/res/android"
	package="com.magus.listview"
	android:versionCode="1"
	android:versionName="1.0">
	<uses-sdk
		android:minSdkVersion="8" />

	<application
		android:icon="@drawable/icon"
		android:label="@string/app_name">
		<activity
			android:configChanges="orientation|keyboardHidden"
			android:label="@string/app_name"
			android:name="CitySelectActivity"
			android:screenOrientation="portrait"
			android:windowSoftInputMode="stateHidden|adjustUnspecified">
			<intent-filter>
				<action
					android:name="android.intent.action.MAIN" />
				<category
					android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>

	</application>
</manifest>



City:
package com.magus.listview;

import java.util.ArrayList;

public class City {

	private String cityId;
	private String cityName;
	private String cityPinyin;
	private int cityType;
	public static final int TYPE_CITY_HOT = 0; // 热门城市
	public static final int TYPE_CITY_NORMAL = 1;// 普通城市

	City() {

	}

	City(String cityid, String ciryname, String citypinyin, int citytype) {

		cityId = cityid;
		cityName = ciryname;
		cityPinyin = citypinyin;
		cityType = citytype;
	}

	City(String ciryname, String citypinyin, int citytype) {

		cityId = "";
		cityName = ciryname;
		cityPinyin = citypinyin;
		cityType = citytype;
	}

	public String getCityId() {
		return cityId;
	}

	public void setCityId(String cityId) {
		this.cityId = cityId;
	}

	public String getCityName() {
		return cityName;
	}

	public void setCityName(String cityName) {
		this.cityName = cityName;
	}

	public String getCityPinyin() {
		return cityPinyin;
	}

	public void setCityPinyin(String cityPinyin) {
		this.cityPinyin = cityPinyin;
	}

	public int getCityType() {
		return cityType;
	}

	public void setCityType(int cityType) {
		this.cityType = cityType;
	}

	public static ArrayList<City> getCityList() {

		ArrayList<City> cityList = new ArrayList<City>();

		// 热门城市
		City mCity;

		// A
		mCity = new City("安顺", "AS", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("安徽", "AH", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		// B
		mCity = new City("包头", "BT", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("滨州", "BZ", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		// C
		mCity = new City("长春", "CC", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("常德", "CD", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("从化", "CH", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("重庆", "CQ", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("常熟", "CS", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("常州", "CZ", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("郴州", "CZ", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		// D
		mCity = new City("丹东", "DD", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("大连", "DL", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("大庆", "DQ", TYPE_CITY_NORMAL);
		cityList.add(mCity);
		mCity = new City("德州", "DZ", TYPE_CITY_NORMAL);
		cityList.add(mCity);

		return cityList;

	}

}


Utils:
package com.magus.listview;

import java.util.ArrayList;
import java.util.List;

public class Utils {

	/**
	 * 处理城市列表,加入开头字幕索引
	 * 
	 * @author johnson 安顺 北京 ==加工==> A 安顺 B 北京
	 * **/

	public static ArrayList<City> rebuildList(List<City> mList) {

		String cityPinyin = "";
		String indexPinyin = "0";

		final ArrayList<City> buildList = new ArrayList<City>();
		for (City city : mList) {
			cityPinyin = city.getCityPinyin().substring(0, 1);
			if (!cityPinyin.equals(indexPinyin)) {
				indexPinyin = cityPinyin;
				buildList.add(addCityIndex(indexPinyin));
				buildList.add(city);
			} else {
				buildList.add(city);
			}

		}
		return buildList;

	}

	/**
	 * 开头字幕索引
	 * 
	 * @author johnson
	 */
	public static City addCityIndex(String str) {

		return new City("-"+ str, str, City.TYPE_CITY_NORMAL);

	}

}



CitySelectActivity:
package com.magus.listview;

import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * 
 * 城市选择 activity
 * 
 * @author Johnson
 * 
 */
public class CitySelectActivity extends Activity implements
		ListView.OnScrollListener {

	// 城市选择

	private ListView listView;
	private ArrayList<City> listBasicData = new ArrayList<City>();// 存放原始数据;
	private ArrayList<City> list = new ArrayList<City>();// 存放临时数据
	private LayoutInflater mInflater;
	private RemoveWindow mRemoveWindow = new RemoveWindow();
	private WindowManager mWindowManager;
	private TextView mDialogText;
	private boolean mShowing;
	private boolean mReady;
	private String mPrevLetter = "";
	private EditText filterEdit;
	Handler mHandler = new Handler();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
        //原始数据
		listBasicData = City.getCityList();
		
		//初始化显示数据加入字幕索引item
		initShowData();
		this.setContentView(R.layout.city_select);
		mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		listView = (ListView) findViewById(R.id.list_view);
		final myAdapter adapter = new myAdapter();
		listView.setAdapter(adapter);
		listView.setItemsCanFocus(false);
		listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);// 设置单选模型
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				
			Toast.makeText(getApplicationContext(), list.get(position).getCityName(), Toast.LENGTH_SHORT).show();
				
			}
		});

		filterEdit = (EditText) findViewById(R.id.edit_filter);
		filterEdit.addTextChangedListener(new TextWatcher() {
			public void onTextChanged(CharSequence s, int start, int before,
					int count) {
			}

			public void beforeTextChanged(CharSequence s, int start, int count,
					int after) {
			}

			public void afterTextChanged(Editable s) {
	
				final String txt = s.toString().toLowerCase();
				if (list != null) {
					list.clear();

					if (listBasicData != null && listBasicData.size() > 0) {
						for (City mCity : listBasicData) {

							String pinyin = mCity.getCityPinyin();
							String name = mCity.getCityName();
							if (pinyin.startsWith(txt)
									|| pinyin.startsWith(txt.toLowerCase())
									|| pinyin.startsWith(txt.toUpperCase())
									|| (name.indexOf(txt) != -1)) {
								list.add(mCity);
							}
						}
						if ("".equals(txt)) {
							//当字母过滤框内容为空重新初始化数据
							initShowData();
						}

						if ("".equals(txt)) {

							mReady = true;
						} else {
					
							mReady = false;
						}

						// List数据变化时, 刷新Dialog
						// dialog.notifyDataSetChanged();
						adapter.notifyDataSetChanged();
					}
				}
			}
		});

		initIndexTextView();
	}



	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		int visibleItemIndex = firstVisibleItem;
		
		
		
		// int visibleItemIndex = firstVisibleItem + (visibleItemCount / 2 - 1);
		
		
		if (mReady) {

			City city = (City) list.get(visibleItemIndex);

			String firstLetter = city.getCityPinyin();
			System.out.println("@" + firstLetter);

			if (firstLetter.equals("-")) {
				firstLetter = firstLetter.substring(1, 2);
			} else {
				firstLetter = firstLetter.substring(0, 1);
			}

			if (!mShowing && firstLetter.equals(mPrevLetter)) {

				mShowing = true;
				mDialogText.setVisibility(View.VISIBLE);
			}

			mDialogText.setTextSize(40);
			mDialogText.setText(firstLetter);
			mHandler.removeCallbacks(mRemoveWindow);
			mHandler.postDelayed(mRemoveWindow, 1000);
			mPrevLetter = firstLetter;
		}
	}

	@Override
	public void onScrollStateChanged(AbsListView arg0, int arg1) {
		// TODO Auto-generated method stub

	}

	private void removeWindow() {

		if (mShowing) {
			mShowing = false;
			mDialogText.setVisibility(View.INVISIBLE);
		}
	}

	private final class RemoveWindow implements Runnable {
		public void run() {
			removeWindow();
		}
	}

	@Override
	protected void onResume() {
		super.onResume();
		mReady = true;
	}

	@Override
	protected void onPause() {
		super.onPause();
		removeWindow();
		mReady = false;
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		if (mWindowManager != null) {
			mWindowManager.removeView(mDialogText);
		}

		mReady = false;
	}

	class myAdapter extends BaseAdapter {

		View convertView;

		City city;

		@Override
		public boolean isEnabled(int position) {

			if (city != null) {
				return !city.getCityName().startsWith("-");
			} else {
				return true;
			}

		}

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return list.size();
		}

		@Override
		public Object getItem(int position) {
			// TODO Auto-generated method stub
			return list.get(position);
		}

		@Override
		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return position;
		}

		@Override
		public View getView(int position, View convertVie, ViewGroup arg2) {
			// TODO Auto-generated method stub
			convertView = convertVie;
			TextView tv;

			city = (City) list.get(position);

			if (city.getCityName().startsWith("-")) {

				convertView = mInflater.inflate(
						R.layout.city_list_item_city_index, null);
				tv = (TextView) convertView
						.findViewById(R.id.city_list_item_city_index);
				tv.setText(city.getCityName().substring(1,
						city.getCityName().length()));

			} else {

				convertView = mInflater.inflate(R.layout.city_list_item_city,
						null);
				tv = (TextView) convertView
						.findViewById(R.id.city_list_item_city);
				tv.setText(city.getCityName());
			}

			return convertView;
		}
	}

	/**
	 * 初始化用于显示的数据
	 *  
	 * @author johnson
	 * 
	 * */
	
	public void initShowData() {

		list.clear();
		list = Utils.rebuildList(listBasicData);

	}
	
	/**
	 * 初始化程序列表索引IndexTextView
	 * 
	 * @author johnson
	 * */ 
	
	
	public void initIndexTextView() {
		mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
		listView.setOnScrollListener(this);
		mDialogText = (TextView) mInflater
				.inflate(R.layout.list_position, null);
		mDialogText.setVisibility(View.INVISIBLE);
		mHandler.post(new Runnable() {

			public void run() {
				mReady = true;
				WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
						LayoutParams.WRAP_CONTENT,
						LayoutParams.WRAP_CONTENT,
						WindowManager.LayoutParams.TYPE_APPLICATION_STARTING,
						WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
								| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
						PixelFormat.TRANSLUCENT);

				mWindowManager.addView(mDialogText, lp);
			}
		});

	}

}




十:ListView去掉橙黄底色
在ListView中,系统默认选中时会出现橙黄底色。有时候我们不需要这样的效果,如何去掉?可在ListView中增加 android:listSelector="@android:color/transparent"


十一、List可扩展的列表:
请看效果图:
[img]

[/img]
package com.zhou.activity;

import android.app.ExpandableListActivity;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AbsListView;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;

public class ExpandableList extends ExpandableListActivity {
	//声明adapter
	private ExpandableListAdapter mAdapter;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//设这标题
		setTitle("可扩展的列表");
		//实例化adapter
		mAdapter = new MyExpandableListAdapter();
		//为列表设置adapter
		setListAdapter(mAdapter);
		//为list注册context菜单
		registerForContextMenu(this.getExpandableListView());
	}
	
	
	@Override
	public boolean onChildClick(ExpandableListView parent, View v,
			int groupPosition, int childPosition, long id) {
		Toast.makeText(this, " 组元素索引: " + groupPosition + " 子元素索引: " + childPosition, Toast.LENGTH_SHORT).show();
		return super.onChildClick(parent, v, groupPosition, childPosition, id);
	}


	@Override
	public void onGroupExpand(int groupPosition) {
		Toast.makeText(this, " 组元素索引: " + groupPosition, Toast.LENGTH_SHORT).show();
		super.onGroupExpand(groupPosition);
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo) {
		menu.setHeaderTitle("上下文菜单");
		menu.add(0, 0, 0, "单击我");
	}

	// 单击上下文菜单后的逻辑
	@Override
	public boolean onContextItemSelected(MenuItem item) {
		ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) item.getMenuInfo();
		String title = ((TextView) info.targetView).getText().toString();

		int type = ExpandableListView.getPackedPositionType(info.packedPosition);
		if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
			int groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition);
			int childPos = ExpandableListView.getPackedPositionChild(info.packedPosition);
			Toast.makeText(this, title + " 组元素索引: " + groupPos + " 子元素索引: " + childPos, Toast.LENGTH_SHORT).show();
			return true;
		} else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
			int groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition);
			Toast.makeText(this, title + " 组元素索引: " + groupPos, Toast.LENGTH_SHORT).show();
			return true;
		}
		return false;
	}
	//自定义Adapter
	public class MyExpandableListAdapter extends BaseExpandableListAdapter {
		// 父列表数据
		private String[] groups = 
		{ 
			"吉林省", 
			"辽宁省", 
			"黑龙江省",
			"山东省" 
		};
		// 子列表数据
		private String[][] children = 
		{
			{ "长春市" },
			{ "沈阳市", "铁岭市" },
			{ "哈尔滨市", "齐齐哈尔市", "牡丹江市" },
			{ "济南市", "青岛市", "淄博市", "潍坊市" }
		};
		@Override
		public Object getChild(int groupPosition, int childPosition) {
			return children[groupPosition][childPosition];
		}
		@Override
		public long getChildId(int groupPosition, int childPosition) {
			return childPosition;
		}
		@Override
		public int getChildrenCount(int groupPosition) {
			return children[groupPosition].length;
		}
		// 取子列表中的某一项的 View
		@Override
		public View getChildView(int groupPosition, int childPosition,
				boolean isLastChild, View convertView, ViewGroup parent) {
			TextView textView = getGenericView();
			textView.setText(getChild(groupPosition, childPosition).toString());
			return textView;
		}
		@Override
		public Object getGroup(int groupPosition) {
			return groups[groupPosition];
		}
		@Override
		public int getGroupCount() {
			return groups.length;
		}
		@Override
		public long getGroupId(int groupPosition) {
			return groupPosition;
		}
		// 取父列表中的某一项的 View
		@Override
		public View getGroupView(int groupPosition, boolean isExpanded,
				View convertView, ViewGroup parent) {
			TextView textView = getGenericView();
			textView.setText(getGroup(groupPosition).toString());
			return textView;
		}
		@Override
		public boolean hasStableIds() {
			return true;
		}
		@Override
		public boolean isChildSelectable(int groupPosition, int childPosition) {
			return true;
		}
		// 获取某一项的 View 的逻辑
		private TextView getGenericView() {
			AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
					ViewGroup.LayoutParams.FILL_PARENT, 48);
			TextView textView = new TextView(ExpandableList.this);
			textView.setLayoutParams(lp);
			textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
			textView.setPadding(32, 0, 0, 0);
			return textView;
		}
	}
}



Android ListView 效果美化 :
1.、listview在拖动的时候背景图片消失变成黑色背景。等到拖动完毕我们自己的背景图片才显示出来。
代码解决: android:scrollingCache=”false”


2 、listview的上边和下边有黑色的阴影。
代码解决:android:fadingEdge=”none” 


3、lsitview的每一项之间需要设置一个图片做为间隔。
代码解决:  android:divider=”@drawable/list_driver”  其中  @drawable/list_driver 是一个图片资源


4. 自定义listview的时候,会出现下面选中一个空间黑色底色的情况,破坏整体美观度。
代码解决:android:cacheColorHint=“#00000000”


5. 默认显示选中的item为橙黄底色,有时候我们需要去掉这种效果
代码解决:android:listSelector属性
  • 大小: 21.2 KB
  • 大小: 26.2 KB
  • 大小: 23.8 KB
  • 大小: 20.7 KB
  • 大小: 35.3 KB
  • 大小: 12.4 KB
  • 大小: 20.8 KB
  • 大小: 44.4 KB
  • 大小: 19.6 KB
分享到:
评论
1 楼 yahier 2012-02-23  
感谢分享

相关推荐

    vb listview的基本用法 源码

    对VB中的listView控件用法的总结 主要包括:复选框数值获取 右击菜单 ctrl多选值的获取 双击复选框的选中等 双击条目 选中复选框 右击 “测试1”,输出左击和右击选中的行数 右击“测试2”,输出复选框选中的行数 ...

    C#中ListView控件的虚模式使用方法

    C#中ListView控件的虚模式使用方法 本人收藏了3年的资源 现放出 都是总结了很多系统 软件项目实施过程中的经验的 慢慢积累的

    AndroidListView的三种适配器用法

    详细介绍了ListView比较常用的三种适配器的用法,基于代码一步一步完成,自己学习后的一点总结

    listview的属性使用大全

    把android中重要的listview的用法进行归纳总结

    onMeasure简单方法 完美解决ListView与ScollView!

    2、使用单个ListView的addHeaderView()方法(给listview设置顶部固定的一个view) 评价:比较简便的方法,但是如果顶部布局需要监听滑动事件,也不可取。 3、使用LinearLayout取代ListView(重写LinearLayout) ...

    Delphi控件ListView的属性及使用方法详解

    主要介绍了Delphi控件ListView的属性及使用方法详解,对于Delphi控件ListView做一复习总结,需要的朋友可以参考下

    Android编程使用ListView实现数据列表显示的方法

    主要介绍了Android编程使用ListView实现数据列表显示的方法,实例分析了Android中ListView控件的使用技巧,需要的朋友可以参考下

    Android中ListView的几种常见的优化方法总结

    Android中的ListView应该算是布局中几种最常用的组件之一了,使用也十分方便,下面将介绍ListView几种比较常见的优化方法: 首先我们给出一个没有任何优化的Listview的Adapter类,我们这里都继承自BaseAdapter,...

    ListView的多种实现方式包括多种适配器使用

    主要是总结了ListView的一些实现方式, 适配器使用的是默认的,还有BaseAdapter,ArrayAdapter。 包括定制适配器以及定制布局等

    Android之Adapter用法总结

    Android之Adapter用法总结 Adapter是连接后端数据和前端显示的适配器接口,是数据和UI(View)之间一个重要的纽带。在常见的 View(ListView,GridView)等地方都需要用到Adapter。如下图直观的表达了Data、Adapter、...

    Android中Listview下拉刷新和上拉加载更多的多种实现方案

    listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明。  方案一:添加头布局和脚布局  android系统为listview提供了addfootview和addheadview两个API。这样可以直接自定义一个...

    自己平时做的android相关的总结

    Ndk的使用方法: Ant 与 android update project 命令行只有在非根盘符上才能运行 android Launcher Android 运行环境搭建 Android:name什么时候加”.” Activity class {package/class} does not exist 问题的解决 ...

    浅析Android之Adapter用法总结

    在常见的View(ListView,GridView)等地方都需要用到Adapter。如下图直观的表达了Data、Adapter、View三者的关系: Android中所有的Adapter一览:  由图可以看到在Android中与Adapter有关的所有接口、类的完整层级图...

    windows应用高级编程-C#编程篇

    1.4 小结 第2章 简单用户界面控件 2.1 用户界面设计原则 2.2 漫游简单用户控件 2.2.1 PictureBox控件 2.2.2 Label控件 2.2.3 Textbox控件 2.2.4 List控件 2.2.5 基于按钮的控件 2.2.6 Bar控件 2.3 小结 第3章 高级...

    最全java面试题.zip

    Android内存泄漏总结 Handler内存泄漏分析及解决 Handler、Looper、Message、MessageQueue基础流程分析 Android性能优化 ListView详解 RecyclerView和ListView的异同 AsyncTask源码分析 插件化技术 自定义控件 事件...

    Java最全面试题宝典.rar

    Android内存泄漏总结 Handler内存泄漏分析及解决 Handler、Looper、Message、MessageQueue基础流程分析 Android性能优化 ListView详解 RecyclerView和ListView的异同 AsyncTask源码分析 插件化技术 自定义控件 事件...

    ASP.NET 控件的使用

    1.5 小结 37 第2章 使用标准控件 38 2.1 显示信息 38 2.1.1 使用Label控件 38 2.1.2 使用Literal控件 42 2.2 接收用户输入 44 2.2.1 使用TextBox控件 44 2.2.2 使用CheckBox控件 50 2.2.3 使用RadioButton控件 52 ...

    Android典型技术模块开发详解

    目录 第一篇 Android开发初步 第1章 Android初识 1.1 Android简介 1.1.1 认识Android 1.1.2 Android系统框架 1.1.3 应用程序框架 1.2 Eclipse开发环境 1.2.1 安装ADT插件 1.2.2 安装SDK ...16.7 本章小结

    疯狂Android讲义源码

     1.7 本章小结 33  第2章 Android应用的界面编程 35  2.1 界面编程与视图(View)组件 36  2.1.1 视图组件与容器组件 36  2.1.2 使用XML布局文件控制UI  界面 40  2.1.3 在代码中控制UI界面 41  2.1.4 使用...

Global site tag (gtag.js) - Google Analytics