第六章 使用列表(list)和适配器(adapter)

列表和适配器是安卓开发中需要重点掌握的两个概念,在这章中,我们将学习列表和适配器的一些技巧。


1.处理空列表

当列表中没有内容的时候,可以展示一张图片或者文字进行提示,只需要使用ListView的setEmpltyView(view)方法即可。当adapter为null或者adapter的 isEmpty()方法返回true的时候就会展示出来。
例如:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <ListView
        android:id="@+id/my_list_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

    <ImageView
        android:id="@+id/empty_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:src="@drawable/empty_view" />
</FrameLayout>
当ListView为空时我们想展示ImageView这张图片,那么在java代码里可以这样写:
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mListView = (ListView) findViewById(R.id.my_list_view);
    mListView.setEmptyView(findViewById(R.id.empty_view));
  }

2.使用ViewHolder创建高效适配器

地球人都知道怎么用,这里就不重复了。


3.给ListView添加section headers

效果如图:

具体实现步骤如下:
首先,section header布局:把背景设置为蓝色,与listview的item区分开来
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/header"
    android:textSize="24sp"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#0000ff" />
然后,页面布局:通过include把header添加进来,这样section header会保持在listview顶部
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">

    <ListView
        android:id="@+id/my_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <include layout="@layout/header" />

</FrameLayout>
再是listview的item布局:每一个item都包含一个header,需要时显示,不需要时隐藏
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <include layout="@layout/header" />

    <TextView
        android:id="@+id/label"
        style="@android:style/TextAppearance.Large"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
adapter的getview方法中:当item在顶部,或者item的首字母与前一个item的首字母不一样时,就显示section header
  public View getView(int position, View view, ViewGroup parent) {
    final  ViewHolder viewHolder;
    if (view == null) {
      view = activity.getLayoutInflater().inflate(R.layout.list_item, parent, false);
      viewHolder=new ViewHolder();
      viewHolder.header=(TextView) view.findViewById(R.id.header);
      viewHolder.label=(TextView)view.findViewById(R.id.label);
      view.setTag(viewHolder);
    }else {
      viewHolder=(ViewHolder)view.getTag();
    }
    //赋值
    String label = countries[position];
    if (position == 0 || countries[position - 1].charAt(0) != label.charAt(0)) {
      viewHolder.header.setVisibility(View.VISIBLE);
      viewHolder.header.setText(label.substring(0, 1));
    } else {
      viewHolder.header.setVisibility(View.GONE);
    }
    viewHolder.label.setText(countries[position]);

    return view;
  }
  static class ViewHolder{
    TextView header;
    TextView label;
  }
然后在activity中:初始化ListView,并添加滑动监听,使得处于listview顶部的section header的标签与item的首字母相同。
public class MainActivity extends Activity {

  private TextView topHeader;
  private int topVisiblePosition = -1;
  private ListView mList;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.list);
    topHeader = (TextView) findViewById(R.id.header);
    mList=(ListView)findViewById(R.id.my_list);
    mList.setAdapter(new SectionAdapter(this, Countries.COUNTRIES));
    mList.setOnScrollListener(
        new AbsListView.OnScrollListener() {
          @Override
          public void onScrollStateChanged(AbsListView view,
              int scrollState) {
            // Empty.
          }
          @Override
          public void onScroll(AbsListView view, int firstVisibleItem,
              int visibleItemCount, int totalItemCount) {
            if (firstVisibleItem != topVisiblePosition) {
              topVisiblePosition = firstVisibleItem;
              setTopHeader(firstVisibleItem);
            }
          }
        });
    setTopHeader(0);
  }
  private void setTopHeader(int pos) {
    final String text = Countries.COUNTRIES[pos].substring(0, 1);
    topHeader.setText(text);
  }
}

4.使用委托delegate让Activity和Adapter通信

比如一个ListView中有一个按钮,点击按钮能删除当前item。如果Activity和Adapter写在两个不同的java文件中,那么在adapter中需要能修改ListView中的数据,就可以使用委托来实现。
adapter代码如下:
public class NumbersAdapter extends ArrayAdapter<Integer> {

  public static interface NumbersAdapterDelegate {
    void removeItem(Integer value);
  }

  private LayoutInflater mInflator;
  private NumbersAdapterDelegate mDelegate;

  public NumbersAdapter(Context context, List<Integer> objects) {
    super(context, 0, objects);
    mInflator = LayoutInflater.from(context);
  }

  @Override
  public View getView(int position, View cv, ViewGroup parent) {

    if (null == cv) {
      cv = mInflator.inflate(R.layout.number_row, parent, false);
    }

    final Integer value = getItem(position);
    TextView tv = (TextView) cv.findViewById(R.id.numbers_row_text);
    tv.setText(value.toString());

    View button = cv.findViewById(R.id.numbers_row_button);
    button.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        if (null != mDelegate) {
          mDelegate.removeItem(value);
        }
      }
    });

    return cv;
  }

  public void setDelegate(NumbersAdapterDelegate delegate) {
    mDelegate = delegate;
  }

}
其实就是在adapter中定义了一个接口,然后在item的按钮点击事件中调用接口中的方法。
Activity代码如下:让activity继承adapter中的接口,然后实现接口中的方法。当用户点击item中的按钮,就会调用activity中的removeItem方法
public class MainActivity extends Activity implements NumbersAdapterDelegate {
  private ListView mListView;
  private ArrayList<Integer> mNumbers;
  private NumbersAdapter mAdapter;
  private EditText mEditText;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mEditText = (EditText) findViewById(R.id.main_edittext);
    mListView = (ListView) findViewById(R.id.main_listview);
    mNumbers = new ArrayList<Integer>();
    mAdapter = new NumbersAdapter(this, mNumbers);
    mListView.setAdapter(mAdapter);
  }

  @Override
  protected void onResume() {
    super.onResume();
    mAdapter.setDelegate(this);
  }

  @Override
  protected void onPause() {
    super.onPause();
    mAdapter.setDelegate(null);
  }

  @Override
  public void removeItem(Integer value) {
    mNumbers.remove(value);
    Toast.makeText(this, "Removed object: " + value, Toast.LENGTH_SHORT).show();
    mAdapter.notifyDataSetChanged();
  }
}


5.其它

1.设定屏幕的方向
其中一个方法是在manifest.xml里设定activity的属性 android:screenOrientation="landscape"
也可在Activity的Java文件中通过代码设定:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

setRequestedOrientation 参数说明:

  • 系统默认:此为默认值,由Android系统自己选择适当的方向,选择策略视具体设备的配置情况而定,因此不同的设备会有不同的方向选择;
    ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
  • 锁定直式:竖屏 (肖像照) , 显示时高度大于宽度 ;
    ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
  • 锁定横式:横屏(风景照) ,显示时宽度大于高度;
    ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
  • 随使用者当下:用户当前的首选方向;
    ActivityInfo.SCREEN_ORIENTATION_USER
  • 与活动线程下相同的设定:继承Activity堆栈中当前Activity下面的那个Activity的方向;
    ActivityInfo.SCREEN_ORIENTATION_BEHIND
  • 不随SENSOR改变:忽略物理感应器 即显示方向与物理感应器无关,不管用户如何旋转设备显示方向都不会随着改变("unspecified"设置除外);
    ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
  • 随SENSOR改变:由物理感应器决定显示方向,它取决于用户如何持有设备,当 设备 被旋转时方向会随之变化 在横屏与竖屏之间;
    ActivityInfo.SCREEN_ORIENTATION_SENSOR
2.ListView的选择模式choiceMode

 android:choiceMode="multipleChoice"
 choiceMode属性解析
Constant Value Description
none 0 Normal list that does not indicate choices.
singleChoice 1 The list allows up to one choice.
multipleChoice 2 The list allows multiple choices.
multipleChoiceModal 3 The list allows multiple choices in a custom selection mode.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值