Android

Custom Content Provider - (4) 데이터 삭제

qlyh8 2018. 2. 12. 21:55

BuildContentProvider_4.zip


데이터 삭제 이후 화면




데이터 삭제


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, nullnull);
                // 데이터 다시 로드
                getSupportLoaderManager().restartLoader(TASK_LOADER_ID, null, MainActivity.this);
            }
        }).attachToRecyclerView(mRecyclerView);
 
        // 할일 추가 버튼을 누르면 할일 추가 화면으로 이동한다.
        fabButton.setOnClickListener(new View.OnClickListener() {...});
 
        // 로더가 초기화되고 활성화되어 있는지 확인한다.
        // 로더가 아직 없으면 하나가 작성되고, 그렇지 않으면 마지막으로 작성된 로더가 다시 사용된다.
        getSupportLoaderManager().initLoader(TASK_LOADER_ID, nullthis);
    }
 
    // 일시중지되거나 재시작된 후에 메소드가 호출된다.
    // 보통 새로운 데이터가 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