Android
Custom Content Provider - (4) 데이터 삭제
qlyh8
2018. 2. 12. 21:55
데이터 삭제 이후 화면
데이터 삭제
1. 할일 데이터베이스에 대한 쓰기 접근을 얻고, URI를 매칭한다.
Get access to the underlying database and write URI matching code.
2. selection 파라미터를 사용해 한 로우의 데이터만 삭제한다.
Write code to delete a single row of data.
3. 컨텐트 리졸버에게 변경사항이 생겼음을 알려준다.
Notify the ContentResolver of any change.
4. 삭제된 항목의 개수의 정수값을 반환한다.
Return the number of deletions.
data/TaskContentProvider.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | package com.tistory.qlyh8.buildcontentprovider.data; /* * Created by YUNHEE on 2018-01-09. */ import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.support.annotation.NonNull; import static com.tistory.qlyh8.buildcontentprovider.data.TaskContract.TaskEntry.TABLE_NAME; public class TaskContentProvider extends ContentProvider { // 디렉토리일 경우 100, 200, 300 등으로 임의 지정한다. public static final int TASKS = 100; // 해당 디렉토리 안의 각 아이템일 경우 101, 102, 103 등으로 임의 지정한다. public static final int TASK_WITH_ID = 101; private static final UriMatcher sUriMatcher = buildUriMatcher(); private TaskDbHelper mTaskDbHelper; // URI 를 상수값으로 매치 시켜주는 메서드 public static UriMatcher buildUriMatcher() {...} // 데이터 소스를 설정하는 데 필요한 것을 초기화 해야한다. // 이 경우 SQLite 데이터베이스로 작업하기 때문에 DbHelper 를 초기화해야 액세스 할 수 있다. @Override public boolean onCreate() {...} // 요청된 데이터를 담고 있는 하나 이상의 로우에 대한 Cursor 객체를 반환하는 메서드. @Override public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {...} // 반환될 내용의 MIME 타입을 반환하는 메서드. MIME 타입은 내용이 어떤 형식으로 되어있는지 식별한다. // MIME 타입을 사용해, 데이터를 구조화하거나 데이터의 유효성을 검증할 때만 유용하게 쓰인다. // ex) 영상이나 이미지 파일을 구별, 파일을 변환하기 전 데이터 타입 체크 @Override public String getType(@NonNull Uri uri) { int match = sUriMatcher.match(uri); switch (match) { case TASKS: // directory return "vnd.android.cursor.dir" + "/" + TaskContract.AUTHORITY + "/" + TaskContract.PATH_TASKS; case TASK_WITH_ID: // single item type return "vnd.android.cursor.item" + "/" + TaskContract.AUTHORITY + "/" + TaskContract.PATH_TASKS; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } } /** * 데이터 추가 메서드 * @param uri: 데이터를 추가할 디렉토리를 알려주는 컨텐트 URI * @param values: 추가할 데이터를 담고있는 ContentValues 객체 * @return 새로 추가된 데이터의 위치를 알려주는 컨텐트 URI, 실패시 -1 */ @Override public Uri insert(@NonNull Uri uri, ContentValues values) {...} // 데이터 삭제 메서드. // 어느 행을 삭제할 지 알려주는 URI 를 인자로 받아야 하며, 삭제된 행의 개수를 반환한다. @Override public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { final SQLiteDatabase db = mTaskDbHelper.getWritableDatabase(); int match = sUriMatcher.match(uri); int tasksDeleted; // 삭제된 할일 개수 switch (match){ case TASK_WITH_ID: String id = uri.getPathSegments().get(1); tasksDeleted = db.delete(TABLE_NAME, "_id=?", new String[]{id}); break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } if (tasksDeleted != 0) getContext().getContentResolver().notifyChange(uri, null); return tasksDeleted; } // 데이터 갱신 메서드. // insert 의 인자들을 인자로 받아, 어느 URI 에 어떤 ContentValues 에 담긴 데이터로 갱신할 것인지 알 수 있다. // 갱신된 행의 개수를 반환환다. @Override public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) { int tasksUpdated; // 갱신된 할일 개수 int match = sUriMatcher.match(uri); switch (match) { case TASK_WITH_ID: String id = uri.getPathSegments().get(1); tasksUpdated = mTaskDbHelper.getWritableDatabase() .update(TABLE_NAME, values, "_id=?", new String[]{id}); break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } if (tasksUpdated != 0) { // 할일이 갱신되었음을 알림 getContext().getContentResolver().notifyChange(uri, null); } return tasksUpdated; } } | cs |
5. 스와이프된 항목의 id를 가져온다.
6. 특정 할일을 가리키는 URI를 생성한다.
7. 데이터를 삭제한다.
8. 데이터를 다시 로드한다.
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | package com.tistory.qlyh8.buildcontentprovider; import android.annotation.SuppressLint; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.support.design.widget.FloatingActionButton; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.View; import com.tistory.qlyh8.buildcontentprovider.data.TaskContract; public class MainActivity extends AppCompatActivity implements android.support.v4.app.LoaderManager.LoaderCallbacks<Cursor>{ private static final String TAG = MainActivity.class.getSimpleName(); private static final int TASK_LOADER_ID = 0; private CustomCursorAdapter mAdapter; RecyclerView mRecyclerView; FloatingActionButton fabButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = findViewById(R.id.recyclerViewTasks); fabButton = findViewById(R.id.fab); // 리사이클러뷰의 레이아웃을 리니어 레이아웃으로 설정한다. mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); // 어댑터를 초기화하고 리사이클러뷰에 연결한다. mAdapter = new CustomCursorAdapter(this); mRecyclerView.setAdapter(mAdapter); // 리사이클러뷰에 사용자가 항목을 삭제할 때 스와이프하면 인식하는 터치 헬퍼를 추가한다. // ItemTouchHelper 는 각 뷰홀더에서 터치 동작을 활성화하고 콜백을 사용하여 // 사용자가 이러한 동작을 수행 할 때 신호를 보낸다. new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { // 어댑터 클래스에서 뷰의 id로 태그의 값이 설정됨 int id = (int) viewHolder.itemView.getTag(); Uri uri = TaskContract.TaskEntry.CONTENT_URI; uri = uri.buildUpon().appendPath(Integer.toString(id)).build(); // id를 URI 뒤에 추가 // 컨텐트 리졸버를 가져와 해당 URI 을 넘기면서 항목을 삭제하라고 알려줌 getContentResolver().delete(uri, null, null); // 데이터 다시 로드 getSupportLoaderManager().restartLoader(TASK_LOADER_ID, null, MainActivity.this); } }).attachToRecyclerView(mRecyclerView); // 할일 추가 버튼을 누르면 할일 추가 화면으로 이동한다. fabButton.setOnClickListener(new View.OnClickListener() {...}); // 로더가 초기화되고 활성화되어 있는지 확인한다. // 로더가 아직 없으면 하나가 작성되고, 그렇지 않으면 마지막으로 작성된 로더가 다시 사용된다. getSupportLoaderManager().initLoader(TASK_LOADER_ID, null, this); } // 일시중지되거나 재시작된 후에 메소드가 호출된다. // 보통 새로운 데이터가 AddTaskActivity 를 통해 삽입 된 후이다. // 이렇게하면 변경 사항에 대한 기본 데이터를 다시 쿼리하기 위해 로더가 다시 시작된다. @Override protected void onResume() {...} // 지정된 ID로 새로운 AsyncTaskLoader 를 인스턴스화하고 반환한다. // 이 로더는 작업 데이터를 커서 또는 오류가 발생하면 null 로 반환한다. // 로딩의 모든 단계에서 데이터 로딩을 처리하기 위해 필요한 콜백을 구현한다. @SuppressLint("StaticFieldLeak") @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) {...} // 이전에 작성된 로더가 로드를 완료하면 호출된다. @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) {...} // 이전에 작성된 로더가 재설정될 때 호출되어 해당 데이터를 사용할 수 없게 만든다. // 로더의 데이터에 대해 갖는 모든 참조를 제거한다. @Override public void onLoaderReset(Loader<Cursor> loader) {...} } | cs |
Android Course of Udacity - Lesson 9