LoaderManager.LoaderCallbacks是3.0之后出现的新特性,通过LoaderManager.LoaderCallbacks接口可以很轻松的实现异步加载数据到Fragment或Activity 中,Loaders提供了回调机制onLoadFinished()通知最终的运行结果,有点类似AsyncTask类,但由于Loader对于并发可以用过Loader管理器统一管理,所以更适合批量处理多个异步任务的处理(当然内部仍然是多线程)。要使用Loader就要实现LoaderManager.LoaderCallbacks接口,并实现它的抽象方法
下面是我加载通话记录的Demo,实现了过滤拨入、拨出、未接、所有,删除单条记录,拨号功能,实现效果图
目录结构
ListView中每个item的布局listview_item.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/bt_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="10dp" android:background="#00000000" android:contentDescription="@string/content_description" android:src="@drawable/calllog_incoming" /> <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginTop="2dp" android:layout_toRightOf="@id/bt_icon" android:textSize="23sp" /> <TextView android:id="@+id/tv_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@id/tv_name" android:layout_below="@id/tv_name" android:layout_marginBottom="2dp" android:textColor="#7f7f7f" android:textSize="16sp" /> <TextView android:id="@+id/tv_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@id/tv_number" android:layout_marginLeft="10dp" android:layout_toRightOf="@id/tv_number" android:ellipsize="end" android:textColor="#7f7f7f" android:textSize="16sp" /> <ImageButton android:id="@+id/btn_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="10dp" android:background="#00000000" android:contentDescription="@string/content_description" android:focusableInTouchMode="false" android:src="@drawable/cl_delete_selector" /> <ImageButton android:id="@+id/btn_call" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="5dp" android:layout_toLeftOf="@id/btn_delete" android:background="#00000000" android:contentDescription="@string/content_description" android:focusableInTouchMode="false" android:src="@drawable/cl_call_selector" /> </RelativeLayout>
整个Activity的布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <TextView android:id="@+id/tv_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <Button android:id="@+id/btn_all" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_text" android:text="@string/all" /> <Button android:id="@+id/btn_incoming" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_text" android:layout_marginLeft="20dp" android:layout_toRightOf="@id/btn_all" android:text="@string/incoming" /> <Button android:id="@+id/btn_outcoming" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/btn_all" android:layout_marginTop="10dp" android:text="@string/outcoming" /> <Button android:id="@+id/btn_missed" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@id/btn_incoming" android:layout_below="@id/btn_all" android:layout_marginTop="10dp" android:layout_toRightOf="@id/btn_outcoming" android:text="@string/missed" /> <View android:id="@+id/line_layout" android:layout_width="match_parent" android:layout_height="2dp" android:layout_below="@id/btn_missed" android:background="#000000" > </View> <ListView android:id="@+id/lv_list" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/line_layout" android:clipToPadding="false" android:divider="#ff553311" android:dividerHeight="2dp" android:fadingEdge="none" android:paddingTop="10dp" /> </RelativeLayout>
ListView对应的适配器MyCursorAdapter.java
package com.dzt.loaderdemo; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.provider.CallLog; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.CursorAdapter; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; class MyCursorAdapter extends CursorAdapter { private static final String TAG = "dzt"; private final Context mContext; public MyCursorAdapter(Context context, Cursor c) { this(context, c, true); // TODO Auto-generated constructor stub } public MyCursorAdapter(Context context, Cursor c, boolean autoRequery) { super(context, c, autoRequery); // TODO Auto-generated constructor stub mContext = context; } public MyCursorAdapter(Context context, Cursor c, int flags) { super(context, c, flags); // TODO Auto-generated constructor stub mContext = context; } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { // TODO Auto-generated method stub LayoutInflater inflater = LayoutInflater.from(context); return inflater.inflate(R.layout.listview_item, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { // TODO Auto-generated method stub if (cursor == null) return; final String id = cursor.getString(0); String number = cursor.getString(1); String name = cursor.getString(2); int type = cursor.getInt(3); String date = cursor.getString(4); ImageView TypeView = (ImageView) view.findViewById(R.id.bt_icon); TextView nameCtrl = (TextView) view.findViewById(R.id.tv_name); if (name == null) { nameCtrl.setText(mContext.getString(R.string.name_unknown)); } else { nameCtrl.setText(name); } TextView numberCtrl = (TextView) view.findViewById(R.id.tv_number); numberCtrl.setText(number); String value = ComputeDate(date); TextView dateCtrl = (TextView) view.findViewById(R.id.tv_date); dateCtrl.setText(value); switch (type) { case CallLog.Calls.INCOMING_TYPE: TypeView.setImageResource(R.drawable.calllog_incoming); break; case CallLog.Calls.OUTGOING_TYPE: TypeView.setImageResource(R.drawable.calllog_outcoming); break; case CallLog.Calls.MISSED_TYPE: TypeView.setImageResource(R.drawable.calllog_missed); break; case 4: // CallLog.Calls.VOICEMAIL_TYPE break; default: break; } ImageButton dailBtn = (ImageButton) view.findViewById(R.id.btn_call); dailBtn.setTag(number); dailBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub // Intent.ACTION_CALL_PRIVILEGED 由于Intent中隐藏了,只能用字符串代替 Intent intent = new Intent(Intent.ACTION_CALL, Uri.fromParts( "tel", (String) v.getTag(), null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } }); ImageButton deleteBtn = (ImageButton) view .findViewById(R.id.btn_delete); deleteBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub // 根据ID进行记录删除 String where = CallLog.Calls._ID + "=?"; String[] selectionArgs = new String[] { id }; int result = mContext.getContentResolver().delete( CallLog.Calls.CONTENT_URI, where, selectionArgs); Log.d(TAG, "11result = " + result); } }); } private String ComputeDate(String date) { long callTime = Long.parseLong(date); long newTime = new Date().getTime(); long duration = (newTime - callTime) / (1000 * 60); String value; // SimpleDateFormat sfd = new // SimpleDateFormat("yyyy-MM-dd HH:mm:ss", // Locale.getDefault()); // String time = sfd.format(callTime); // Log.d(TAG, "[MyCursorAdapter--ComputeDate] time = " + time); // 进行判断拨打电话的距离现在的时间,然后进行显示说明 if (duration < 60) { value = duration + "分钟前"; } else if (duration >= 60 && duration < MainActivity.DAY) { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault()); value = sdf.format(new Date(callTime)); // value = (duration / 60) + "小时前"; } else if (duration >= MainActivity.DAY && duration < MainActivity.DAY * 2) { value = "昨天"; } else if (duration >= MainActivity.DAY * 2 && duration < MainActivity.DAY * 3) { value = "前天"; } else if (duration >= MainActivity.DAY * 7) { SimpleDateFormat sdf = new SimpleDateFormat("MM/dd", Locale.getDefault()); value = sdf.format(new Date(callTime)); } else { value = (duration / MainActivity.DAY) + "天前"; } return value; } }
加载器是放在MainActivity.java类当中
package com.dzt.loaderdemo; import android.app.Activity; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.provider.CallLog; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ListView; /** * 使用加载器加载通话记录 * * @author Administrator * */ public class MainActivity extends Activity { private static final String TAG = "dzt"; // 查询指定的条目 private static final String[] CALLLOG_PROJECTION = new String[] { CallLog.Calls._ID, CallLog.Calls.NUMBER, CallLog.Calls.CACHED_NAME, CallLog.Calls.TYPE, CallLog.Calls.DATE }; static final int DAY = 1440; // 一天的分钟值 private static final int ALL = 0; // 默认显示所有 private static final int INCOMING = CallLog.Calls.INCOMING_TYPE; // 来电 private static final int OUTCOMING = CallLog.Calls.OUTGOING_TYPE; // 拔号 private static final int MISSED = CallLog.Calls.MISSED_TYPE; // 未接 private ListView mListView; private MyLoaderListener mLoader = new MyLoaderListener(); private MyCursorAdapter mAdapter; private int mCallLogShowType = ALL; private boolean m_FinishLoaderFlag = false; // 第一次加载完成 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initWidgets(); initMyLoader(); } private void initWidgets() { mListView = (ListView) findViewById(R.id.lv_list); Button btn = (Button) findViewById(R.id.btn_all); btn.setOnClickListener(new buttonListener()); btn = (Button) findViewById(R.id.btn_incoming); btn.setOnClickListener(new buttonListener()); btn = (Button) findViewById(R.id.btn_outcoming); btn.setOnClickListener(new buttonListener()); btn = (Button) findViewById(R.id.btn_missed); btn.setOnClickListener(new buttonListener()); mAdapter = new MyCursorAdapter(MainActivity.this, null); mListView.setAdapter(mAdapter); } private void initMyLoader() { getLoaderManager().initLoader(0, null, mLoader); } /** * 实现一个加载器 * * @author Administrator * */ private class MyLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { // TODO Auto-generated method stub m_FinishLoaderFlag = false; CursorLoader cursor = new CursorLoader(MainActivity.this, CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, null, null, CallLog.Calls.DEFAULT_SORT_ORDER); Log.d(TAG, "MyLoaderListener---------->onCreateLoader"); return cursor; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // TODO Auto-generated method stub if (data == null) return; Cursor tempData = data; if (tempData.getCount() == 0) { Log.d(TAG, "MyLoaderListener---------->onLoadFinished count = 0"); mAdapter.swapCursor(null); return; } if (m_FinishLoaderFlag) { tempData = null; String selection = null; String[] selectionArgs = null; if (mCallLogShowType == INCOMING) { selection = CallLog.Calls.TYPE + "=?"; selectionArgs = new String[] { "1" }; } else if (mCallLogShowType == OUTCOMING) { selection = CallLog.Calls.TYPE + "=?"; selectionArgs = new String[] { "2" }; } else if (mCallLogShowType == MISSED) { selection = CallLog.Calls.TYPE + "=?"; selectionArgs = new String[] { "3" }; } tempData = getContentResolver().query( CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, selection, selectionArgs, CallLog.Calls.DEFAULT_SORT_ORDER); } mAdapter.swapCursor(tempData); Log.d(TAG, "MyLoaderListener---------->onLoadFinished data count = " + data.getCount()); m_FinishLoaderFlag = true; } @Override public void onLoaderReset(Loader<Cursor> loader) { // TODO Auto-generated method stub Log.d(TAG, "MyLoaderListener---------->onLoaderReset"); mAdapter.swapCursor(null); } } private class buttonListener implements OnClickListener { @Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()) { case R.id.btn_all: allCalllog(); break; case R.id.btn_incoming: incomingCalllog(); break; case R.id.btn_outcoming: outcomingCalllog(); break; case R.id.btn_missed: missedCalllog(); break; default: break; } } } private void allCalllog() { mCallLogShowType = ALL; String selection = null; String[] selectionArgs = null; Cursor allCursor = getContentResolver().query( CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, selection, selectionArgs, CallLog.Calls.DEFAULT_SORT_ORDER); mAdapter.swapCursor(allCursor); } private void incomingCalllog() { mCallLogShowType = INCOMING; String selection = CallLog.Calls.TYPE + "=?"; String[] selectionArgs = new String[] { "1" }; Cursor incomingCursor = getContentResolver().query( CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, selection, selectionArgs, CallLog.Calls.DEFAULT_SORT_ORDER); mAdapter.swapCursor(incomingCursor); } private void outcomingCalllog() { mCallLogShowType = OUTCOMING; String selection = CallLog.Calls.TYPE + "=?"; String[] selectionArgs = new String[] { "2" }; Cursor outcomingCursor = getContentResolver().query( CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, selection, selectionArgs, CallLog.Calls.DEFAULT_SORT_ORDER); mAdapter.swapCursor(outcomingCursor); } private void missedCalllog() { mCallLogShowType = MISSED; String selection = CallLog.Calls.TYPE + "=?"; String[] selectionArgs = new String[] { "3" }; Cursor missedCursor = getContentResolver().query( CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, selection, selectionArgs, CallLog.Calls.DEFAULT_SORT_ORDER); mAdapter.swapCursor(missedCursor); } }加载器的使用就是重写三个回调函数,并在加载完时设置Adapter的Cursor,使用Loader之后,只要数据源发生变化,就会自动调用onLoadFinished(),此时listview也会自动刷新
完整的Demo
http://download.csdn.net/detail/deng0zhaotai/7116173