更改目录结构,增加拖动排序并更新数据库

This commit is contained in:
2025-10-22 11:36:35 +08:00
parent 80f7e47511
commit 70fd325c00
11 changed files with 199 additions and 49 deletions

View File

@@ -7,7 +7,6 @@ import androidx.lifecycle.Observer;
import com.ttstd.dialer.R; import com.ttstd.dialer.R;
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity; import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
import com.ttstd.dialer.contact.Contact;
import com.ttstd.dialer.databinding.ActivityContactAddBinding; import com.ttstd.dialer.databinding.ActivityContactAddBinding;
import com.ttstd.dialer.filter.NoSpaceInputFilter; import com.ttstd.dialer.filter.NoSpaceInputFilter;
@@ -58,8 +57,7 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
public void save(View view) { public void save(View view) {
String name = mViewDataBinding.etName.getText().toString(); String name = mViewDataBinding.etName.getText().toString();
String phone = mViewDataBinding.etPhone.getText().toString(); String phone = mViewDataBinding.etPhone.getText().toString();
Contact contact = new Contact(name, phone); mViewModel.saveContact(name, phone);
mViewModel.insert(contact);
} }
} }
} }

View File

@@ -5,13 +5,21 @@ import android.util.Log;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent; import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel; import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.contact.Contact;
import com.ttstd.dialer.contact.ContactRepository;
import com.ttstd.dialer.databinding.ActivityContactAddBinding; import com.ttstd.dialer.databinding.ActivityContactAddBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactRepository;
import java.util.List; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableEmitter;
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding, ActivityEvent> { public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding, ActivityEvent> {
private static final String TAG = "ContactAddViewModel"; private static final String TAG = "ContactAddViewModel";
@@ -25,9 +33,41 @@ public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding
public MutableLiveData<Long> mIntegerMutableLiveData = new MutableLiveData<>(); public MutableLiveData<Long> mIntegerMutableLiveData = new MutableLiveData<>();
public void insert(Contact contact) { public void saveContact(String name, String phone) {
Log.e(TAG, "insert: " ); Observable.create(new ObservableOnSubscribe<Long>() {
mIntegerMutableLiveData.setValue(mRepository.insert(contact)); @Override
public void subscribe(@NonNull ObservableEmitter<Long> emitter) throws Throwable {
int count = mRepository.getTotalCount() + 1;
Contact contact = new Contact(name, phone, count);
Log.e(TAG, "saveContact: " + contact);
emitter.onNext(mRepository.insert(contact));
emitter.onComplete();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Long>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("saveContact", "onSubscribe: ");
}
@Override
public void onNext(@NonNull Long aLong) {
Log.e("saveContact", "onNext: " + aLong);
mIntegerMutableLiveData.setValue(aLong);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e("saveContact", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Log.e("saveContact", "onComplete: ");
}
});
} }
} }

View File

@@ -7,14 +7,13 @@ import android.view.View;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.ttstd.dialer.R; import com.ttstd.dialer.R;
import com.ttstd.dialer.activity.contact.add.ContactAddActivity; import com.ttstd.dialer.activity.contact.add.ContactAddActivity;
import com.ttstd.dialer.adapter.ContactInfoAdapter; import com.ttstd.dialer.adapter.ContactInfoAdapter;
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity; import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
import com.ttstd.dialer.contact.Contact;
import com.ttstd.dialer.databinding.ActivityContactListBinding; import com.ttstd.dialer.databinding.ActivityContactListBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.view.ItemTouchHelperCallback; import com.ttstd.dialer.view.ItemTouchHelperCallback;
import java.util.List; import java.util.List;
@@ -25,6 +24,8 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
private ContactInfoAdapter mContactInfoAdapter; private ContactInfoAdapter mContactInfoAdapter;
private List<Contact> mContacts;
@Override @Override
public boolean setNightMode() { public boolean setNightMode() {
return true; return true;
@@ -54,12 +55,21 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
mContactInfoAdapter.setItemMoveCallback(new ContactInfoAdapter.ItemMoveCallback() { mContactInfoAdapter.setItemMoveCallback(new ContactInfoAdapter.ItemMoveCallback() {
@Override @Override
public void onItemMove(int fromPosition, int toPosition) { public void onItemMove(int fromPosition, int toPosition) {
Log.e(TAG, "onItemMove: " ); Log.e(TAG, "onItemMove: ");
Contact fromContact = mContacts.get(fromPosition);
int fromContactPosition = fromContact.getPosition();
Contact toContact = mContacts.get(toPosition);
int toContactPosition = toContact.getPosition();
fromContact.setPosition(toContactPosition);
mViewModel.updateItemPosition(fromContact);
toContact.setPosition(fromContactPosition);
mViewModel.updateItemPosition(toContact);
} }
@Override @Override
public void onItemRemoved(int position) { public void onItemRemoved(int position) {
Log.e(TAG, "onItemRemoved: " ); Log.e(TAG, "onItemRemoved: ");
} }
}); });
// 设置ItemTouchHelper实现拖动和滑动删除 // 设置ItemTouchHelper实现拖动和滑动删除
@@ -78,8 +88,9 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
mViewModel.mContactListData.observe(this, new Observer<List<Contact>>() { mViewModel.mContactListData.observe(this, new Observer<List<Contact>>() {
@Override @Override
public void onChanged(List<Contact> contacts) { public void onChanged(List<Contact> contacts) {
Log.e(TAG, "onChanged: " + contacts); Log.e(TAG, "mContactListData: " + contacts);
mContactInfoAdapter.setContacts(contacts); mContacts = contacts;
mContactInfoAdapter.setContacts(mContacts);
} }
}); });
} }

View File

@@ -1,18 +1,27 @@
package com.ttstd.dialer.activity.contact.list; package com.ttstd.dialer.activity.contact.list;
import android.content.Context; import android.content.Context;
import android.util.Log;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent; import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel; import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.contact.Contact;
import com.ttstd.dialer.contact.ContactRepository;
import com.ttstd.dialer.databinding.ActivityContactListBinding; import com.ttstd.dialer.databinding.ActivityContactListBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactRepository;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableEmitter;
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class ContactListViewModel extends BaseViewModel<ActivityContactListBinding, ActivityEvent> { public class ContactListViewModel extends BaseViewModel<ActivityContactListBinding, ActivityEvent> {
private static final String TAG = "ContactListViewModel"; private static final String TAG = "ContactListViewModel";
@@ -27,19 +36,81 @@ public class ContactListViewModel extends BaseViewModel<ActivityContactListBindi
public MutableLiveData<List<Contact>> mContactListData = new MutableLiveData<>(); public MutableLiveData<List<Contact>> mContactListData = new MutableLiveData<>();
public void getAllContacts() { public void getAllContacts() {
List<Contact> contacts = mRepository.getAllContacts(); Observable.create(new ObservableOnSubscribe<List<Contact>>() {
List<Contact> sorted = contacts.stream().sorted(new Comparator<Contact>() {
@Override @Override
public int compare(Contact o1, Contact o2) { public void subscribe(@NonNull ObservableEmitter<List<Contact>> emitter) throws Throwable {
return Integer.compare(o1.getSort(), o2.getSort()); List<Contact> contacts = mRepository.getAllContacts();
emitter.onNext(contacts);
emitter.onComplete();
} }
}).collect(Collectors.toList()); }).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Contact>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("getAllContacts", "onSubscribe: ");
}
mContactListData.setValue(sorted); @Override
public void onNext(@NonNull List<Contact> contacts) {
Log.e("getAllContacts", "onNext: ");
mContactListData.setValue(contacts);
// List<Contact> sorted = contacts.stream().sorted(new Comparator<Contact>() {
// @Override
// public int compare(Contact o1, Contact o2) {
// return Integer.compare(o1.getSort(), o2.getSort());
// }
// }).collect(Collectors.toList());
// mContactListData.setValue(sorted);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e("getAllContacts", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Log.e("getAllContacts", "onComplete: ");
}
});
} }
public List<Contact> searchContacts(String query) { public void updateItemPosition(Contact contact) {
return mRepository.searchContacts(query); Log.e(TAG, "updateItemPosition: " + contact);
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
int id = mRepository.update(contact);
emitter.onNext(id);
emitter.onComplete();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("updateItemPosition", "onComplete: ");
}
@Override
public void onNext(@NonNull Integer integer) {
Log.e("updateItemPosition", "onNext: " + integer);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e("updateItemPosition", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Log.e("updateItemPosition", "onComplete: ");
}
});
} }
} }

View File

@@ -12,7 +12,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.shehuan.niv.NiceImageView; import com.shehuan.niv.NiceImageView;
import com.ttstd.dialer.R; import com.ttstd.dialer.R;
import com.ttstd.dialer.contact.Contact; import com.ttstd.dialer.db.contact.Contact;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -81,6 +81,9 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
} }
} }
notifyItemMoved(fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition);
if (mItemMoveCallback != null) {
mItemMoveCallback.onItemMove(fromPosition, toPosition);
}
} }
// 处理滑动删除 // 处理滑动删除

View File

@@ -1,9 +1,10 @@
package com.ttstd.dialer.contact; package com.ttstd.dialer.db.contact;
import android.content.Context;
import androidx.room.Database; import androidx.room.Database;
import androidx.room.Room; import androidx.room.Room;
import androidx.room.RoomDatabase; import androidx.room.RoomDatabase;
import android.content.Context;
import java.io.File; import java.io.File;
@@ -19,8 +20,8 @@ public abstract class AppDatabase extends RoomDatabase {
synchronized (AppDatabase.class) { synchronized (AppDatabase.class) {
if (INSTANCE == null) { if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, context.getExternalCacheDir() + File.separator +"contact_database") AppDatabase.class, context.getExternalFilesDir("db") + File.separator + "contact_database")
.allowMainThreadQueries() // 为了简化示例允许主线程查询 // .allowMainThreadQueries() // 为了简化示例允许主线程查询
.build(); .build();
} }
} }

View File

@@ -1,4 +1,4 @@
package com.ttstd.dialer.contact; package com.ttstd.dialer.db.contact;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -19,12 +19,13 @@ public class Contact implements Serializable {
private String name; private String name;
private String phoneNumber; private String phoneNumber;
private String avatar; private String avatar;
private int sort; private String wxid;
private int position;
public Contact(String name, String phoneNumber) { public Contact(String name, String phoneNumber, int position) {
this.name = name; this.name = name;
this.phoneNumber = phoneNumber; this.phoneNumber = phoneNumber;
this.sort = 0; this.position = position;
} }
public int getId() { public int getId() {
@@ -59,12 +60,20 @@ public class Contact implements Serializable {
this.avatar = avatar; this.avatar = avatar;
} }
public int getSort() { public String getWxid() {
return sort; return wxid;
} }
public void setSort(int sort) { public void setWxid(String wxid) {
this.sort = sort; this.wxid = wxid;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
} }
@Override @Override

View File

@@ -1,4 +1,4 @@
package com.ttstd.dialer.contact; package com.ttstd.dialer.db.contact;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Delete; import androidx.room.Delete;
@@ -10,6 +10,14 @@ import java.util.List;
@Dao @Dao
public interface ContactDao { public interface ContactDao {
// 基本查询获取总行数
@Query("SELECT COUNT(*) FROM contacts")
int getTotalCount();
// 查询根据特定列的非空值计数
@Query("SELECT COUNT(id) FROM contacts")
int getCountById();
@Insert @Insert
long insert(Contact contact); long insert(Contact contact);
@@ -25,7 +33,7 @@ public interface ContactDao {
@Query("DELETE FROM contacts") @Query("DELETE FROM contacts")
int deleteAll(); int deleteAll();
@Query("SELECT * FROM contacts ORDER BY name ASC") @Query("SELECT * FROM contacts ORDER BY position ASC")
List<Contact> getAllContacts(); List<Contact> getAllContacts();
@Query("SELECT * FROM contacts WHERE id = :id") @Query("SELECT * FROM contacts WHERE id = :id")

View File

@@ -1,17 +1,24 @@
package com.ttstd.dialer.contact; package com.ttstd.dialer.db.contact;
import android.content.Context; import android.content.Context;
import java.util.List; import java.util.List;
public class ContactRepository { public class ContactRepository {
private ContactDao mContactDao; private ContactDao mContactDao;
private List<Contact> mAllContacts;
// 构造函数获取数据库访问对象 // 构造函数获取数据库访问对象
public ContactRepository(Context context) { public ContactRepository(Context context) {
AppDatabase db = AppDatabase.getDatabase(context); AppDatabase db = AppDatabase.getDatabase(context);
mContactDao = db.contactDao(); mContactDao = db.contactDao();
mAllContacts = mContactDao.getAllContacts(); }
public int getTotalCount() {
return mContactDao.getTotalCount();
}
public int getCountById() {
return mContactDao.getCountById();
} }
// 获取所有联系人 // 获取所有联系人

View File

@@ -1,7 +1,9 @@
package com.ttstd.dialer.contact; package com.ttstd.dialer.db.contact;
import android.app.Application; import android.app.Application;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import java.util.List; import java.util.List;
public class ContactViewModel extends AndroidViewModel { public class ContactViewModel extends AndroidViewModel {

View File

@@ -29,7 +29,7 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) { RecyclerView.ViewHolder target) {
// 通知适配器项已移动 // 通知适配器项已移动
mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); mAdapter.onItemMove(viewHolder.getBindingAdapterPosition(), target.getBindingAdapterPosition());
return true; return true;
} }
@@ -37,7 +37,7 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
@Override @Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// 通知适配器项已删除 // 通知适配器项已删除
mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); mAdapter.onItemDismiss(viewHolder.getBindingAdapterPosition());
} }
// 当长按item时启用拖动 // 当长按item时启用拖动
@@ -49,6 +49,6 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
// 启用滑动删除 // 启用滑动删除
@Override @Override
public boolean isItemViewSwipeEnabled() { public boolean isItemViewSwipeEnabled() {
return true; return false;
} }
} }