加载器: Android 3.0 中引入了加载器,支持轻松在Activity或者片段中异步加载数据。
加载器的特征:
- 可用于每个 Activity和 Fragment。
- 支持异步加载数据。
- 监控其数据源并在内容变化时传递新的结果。
- 在某一配置更改后重建加载器时,会自动重新连接上一个加载器的游标。 因此,它们无需重新查询其数据。
好了,加载器的基本介绍就到这里,下面是本文的参考博客和资料链接。
下面就以两个例子的代码对加载器来进行说明。
例子一,用listView显示通话记录。其主要源码代参考博客,具体移步去参考。这里我就直接贴代码了。
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="学习加载其的使用" />
<Button
android:id="@+id/btn_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_text"
android:text="所有通话记录" />
<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="拨入记录" />
<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="播出记录" />
<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="未接记录" />
<Button
android:layout_below="@id/btn_all"
android:layout_toRightOf="@id/btn_missed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="nextView"
android:text="简单版示例"/>
<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的子布局文件,item_load.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="yi"
android:src="@drawable/ic_playlist_play_black_24dp" />
<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="er"
android:focusableInTouchMode="false"
android:src="@drawable/ic_delete_forever_black_24dp" />
<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="san"
android:focusableInTouchMode="false"
android:src="@mipmap/ic_launcher" />
</RelativeLayout>
activity的源代码:
package reoger.hut.openfiretest.activty;
import android.Manifest;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.CallLog;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import reoger.hut.openfiretest.R;
public class TestLoaderActivity extends AppCompatActivity implements View.OnClickListener {
private static final String[] CALLLOG_PROJECTION = new String[]{
CallLog.Calls._ID, CallLog.Calls.NUMBER, CallLog.Calls.CACHED_NAME,
CallLog.Calls.TYPE, CallLog.Calls.DATE};
private ListView mListView;
private MyCursorAdapter mAdapter;
private MyLoaderListener mLoader = new MyLoaderListener();
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 int mCallLogShowType = ALL;
private boolean m_FinishLoaderFlag = false; // 第一次加载完成
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_loader);
initWidgets();
getLoaderManager().initLoader(0, null, mLoader);
}
private void initWidgets() {
mListView = (ListView) findViewById(R.id.lv_list);
Button btn = (Button) findViewById(R.id.btn_all);
btn.setOnClickListener(this);
btn = (Button) findViewById(R.id.btn_incoming);
btn.setOnClickListener(this);
btn = (Button) findViewById(R.id.btn_outcoming);
btn.setOnClickListener(this);
btn = (Button) findViewById(R.id.btn_missed);
btn.setOnClickListener(this);
mAdapter = new MyCursorAdapter(TestLoaderActivity.this, null);
mListView.setAdapter(mAdapter);
}
@Override
public void onClick(View v) {
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 missedCalllog() {
mCallLogShowType = MISSED;
String selection = CallLog.Calls.TYPE + "=?";
String[] selectionArgs = new String[]{"3"};
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
return;
}
Cursor missedCursor = getContentResolver().query(
CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, selection,
selectionArgs, CallLog.Calls.DEFAULT_SORT_ORDER);
mAdapter.swapCursor(missedCursor);
}
private void allCalllog() {
mCallLogShowType = ALL;
String selection = null;
String[] selectionArgs = null;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
return;
}
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"};
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
return;
}
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"};
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
return;
}
Cursor outcomingCursor = getContentResolver().query(
CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, selection,
selectionArgs, CallLog.Calls.DEFAULT_SORT_ORDER);
mAdapter.swapCursor(outcomingCursor);
}
private class MyLoaderListener implements android.app.LoaderManager.LoaderCallbacks<Cursor> {
@Override
public android.content.Loader<Cursor> onCreateLoader(int id, Bundle args) {
m_FinishLoaderFlag = false;
CursorLoader cursor = new CursorLoader(TestLoaderActivity.this,
CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION, null, null,
CallLog.Calls.DEFAULT_SORT_ORDER);
return cursor;
}
@Override
public void onLoadFinished(android.content.Loader<Cursor> loader, Cursor data) {
if (data == null)
return;
Cursor tempData = data;
if (tempData.getCount() == 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"};
}
if (ActivityCompat.checkSelfPermission(TestLoaderActivity.this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
return;
}
tempData = getContentResolver().query(
CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION,
selection, selectionArgs,
CallLog.Calls.DEFAULT_SORT_ORDER);
}
mAdapter.swapCursor(tempData);
m_FinishLoaderFlag = true;
}
@Override
public void onLoaderReset(android.content.Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
public void nextView(View view){
startActivity(new Intent(TestLoaderActivity.this,NextActivity.class));
}
}
最后是适配器的代码:
package reoger.hut.openfiretest.activty;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CallLog;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.Date;
import reoger.hut.openfiretest.R;
import static android.content.ContentValues.TAG;
/**
* Created by 24540 on 2017/6/2.
*
*/
public class MyCursorAdapter extends CursorAdapter {
private final Context mContext;
public MyCursorAdapter(Context context, Cursor c) {
this(context, c, true);
}
public MyCursorAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
mContext = context;
}
public MyCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mContext = context;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
return inflater.inflate(R.layout.item_load, parent, false);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
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.ic_playlist_play_black_24dp);
break;
case CallLog.Calls.OUTGOING_TYPE:
TypeView.setImageResource(R.drawable.chatright);
break;
case CallLog.Calls.MISSED_TYPE:
TypeView.setImageResource(R.drawable.ic_delete_forever_black_24dp);
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 View.OnClickListener() {
@Override
public void onClick(View v) {
// 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);
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
mContext.startActivity(intent);
}
});
ImageButton deleteBtn = (ImageButton) view
.findViewById(R.id.btn_delete);
deleteBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 根据ID进行记录删除
String where = CallLog.Calls._ID + "=?";
String[] selectionArgs = new String[]{id};
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
return;
}
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);
// 进行判断拨打电话的距离现在的时间,然后进行显示说明
value=duration + "分钟前";
// 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;
}
}
最后程序运行的截图类似于这种:
这里我直接copy原作者的运行截图。
要注意的一点就是,当写继承LoaderCallback的时候,不要倒错包了,导入的包是android.app.Load......的那个。如图;
示例代码二,这里我只是将手机通讯录里的名字显示到了listView中。代码如下:
public class NextActivity extends AppCompatActivity implements LoaderCallbacks<Cursor> {
private ListView mList;
private final static int REQUEST_READ_CONTACTS = 0;
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.CONTACT_STATUS,
ContactsContract.Contacts.CONTACT_PRESENCE,
ContactsContract.Contacts.PHOTO_ID,
ContactsContract.Contacts.LOOKUP_KEY,
};
// If non-null, this is the current filter the user has provided.
String mCurFilter;
SimpleCursorAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_next);
mAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_2, null,
new String[] { ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.CONTACT_STATUS },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
mList = (ListView) findViewById(R.id.listView);
mList.setAdapter(mAdapter);
}
private void populateAutoComplete() {
if(!mayRequestContacts()){
return;
}
getLoaderManager().initLoader(0,null,this);
}
/**
* 获取联系人列表的权限
* @return
*/
private boolean mayRequestContacts() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
return true;
}
if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {
Snackbar.make(mList, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
.setAction(android.R.string.ok, new View.OnClickListener() {
@Override
@TargetApi(Build.VERSION_CODES.M)
public void onClick(View v) {
requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
}
});
} else {
requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
}
return false;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode == REQUEST_READ_CONTACTS){
if(grantResults.length == 1 && grantResults[0] ==PackageManager.PERMISSION_GRANTED){
populateAutoComplete();
}
}
}
public void showContent(View view){
populateAutoComplete();
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = ContactsContract.Contacts.CONTENT_URI;
}
String select = "((" + ContactsContract.Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ ContactsContract.Contacts.DISPLAY_NAME + " != '' ))";
CursorLoader cursorLoader = new CursorLoader(this,baseUri,CONTACTS_SUMMARY_PROJECTION,select,null, ContactsContract.Contacts.DISPLAY_NAME+" COLLATE LOCALIZED ASC");
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
if(loader!=null)
mAdapter.swapCursor(null);
}
}
对应的布局文件为:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="reoger.hut.openfiretest.activty.NextActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="showContent"
android:text="查看联系人"/>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
最后显示的效果为:
如果对上面的字段是不很了解,这里也给出参考资料官方文档,或者中文的会更加简单一点,那就试试这个。
最后,如果对Loader还有什么不懂的,强烈推荐看官方api指南。