更新代码
This commit is contained in:
36
app/src/main/java/com/navinfo/volvo/MainActivity.kt
Normal file
36
app/src/main/java/com/navinfo/volvo/MainActivity.kt
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.navinfo.volvo
|
||||
|
||||
import android.os.Bundle
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.navinfo.volvo.R
|
||||
import com.navinfo.volvo.databinding.ActivityMainBinding
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val navView: BottomNavigationView = binding.navView
|
||||
|
||||
val navController = findNavController(R.id.nav_host_fragment_activity_main)
|
||||
// Passing each menu ID as a set of Ids because each
|
||||
// menu should be considered as top level destinations.
|
||||
val appBarConfiguration = AppBarConfiguration(
|
||||
setOf(
|
||||
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
|
||||
)
|
||||
)
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
navView.setupWithNavController(navController)
|
||||
}
|
||||
}
|
||||
187
app/src/main/java/com/navinfo/volvo/db/dao/MapLifeDataBase.java
Normal file
187
app/src/main/java/com/navinfo/volvo/db/dao/MapLifeDataBase.java
Normal file
@@ -0,0 +1,187 @@
|
||||
package com.navinfo.volvo.db.dao;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||
|
||||
import com.navinfo.volvo.db.dao.entity.Attachment;
|
||||
import com.navinfo.volvo.db.dao.entity.Message;
|
||||
import com.navinfo.volvo.db.dao.entity.User;
|
||||
import com.tencent.wcdb.database.SQLiteCipherSpec;
|
||||
import com.tencent.wcdb.database.SQLiteDatabase;
|
||||
|
||||
import com.tencent.wcdb.room.db.WCDBOpenHelperFactory;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tencent.wcdb.repair.BackupKit;
|
||||
import com.tencent.wcdb.repair.RecoverKit;
|
||||
import com.tencent.wcdb.room.db.WCDBDatabase;
|
||||
|
||||
@Database(entities = {Message.class, Attachment.class, User.class}, version = 1, exportSchema = false)
|
||||
public abstract class MapLifeDataBase extends RoomDatabase {
|
||||
// marking the instance as volatile to ensure atomic access to the variable
|
||||
/**
|
||||
* 数据库单例对象
|
||||
*/
|
||||
private static volatile MapLifeDataBase INSTANCE;
|
||||
|
||||
/**
|
||||
* 要素数据库类
|
||||
*/
|
||||
public abstract MessageDao getMessageDao();
|
||||
|
||||
/**
|
||||
* 数据库秘钥
|
||||
*/
|
||||
private final static String DB_PASSWORD = "123456";
|
||||
|
||||
public static MapLifeDataBase getDatabase(final Context context, final String name) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (MapLifeDataBase.class) {
|
||||
if (INSTANCE == null) {
|
||||
// [WCDB] To use Room library with WCDB, pass a WCDBOpenHelper factory object
|
||||
// to the database builder with .openHelperFactory(...). In the factory object,
|
||||
// you can specify passphrase and cipher options to open or create encrypted
|
||||
// database, as well as optimization options like asynchronous checkpoint.
|
||||
SQLiteCipherSpec cipherSpec = new SQLiteCipherSpec()
|
||||
.setPageSize(1024)
|
||||
.setSQLCipherVersion(3);
|
||||
WCDBOpenHelperFactory factory = new WCDBOpenHelperFactory()
|
||||
.passphrase(DB_PASSWORD.getBytes()) // passphrase to the database, remove this line for plain-text
|
||||
.cipherSpec(cipherSpec) // cipher to use, remove for default settings
|
||||
.writeAheadLoggingEnabled(true) // enable WAL mode, remove if not needed
|
||||
.asyncCheckpointEnabled(true); // enable asynchronous checkpoint, remove if not needed
|
||||
|
||||
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), MapLifeDataBase.class, name)
|
||||
|
||||
// [WCDB] Specify open helper to use WCDB database implementation instead
|
||||
// of the Android framework.
|
||||
.openHelperFactory((SupportSQLiteOpenHelper.Factory) factory)
|
||||
|
||||
// Wipes and rebuilds instead of migrating if no Migration object.
|
||||
// Migration is not part of this codelab.
|
||||
.fallbackToDestructiveMigration().addCallback(sRoomDatabaseCallback).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the onOpen method to populate the database.
|
||||
* For this sample, we clear the database every time it is created or opened.
|
||||
* <p>
|
||||
* If you want to populate the database only when the database is created for the 1st time,
|
||||
* override RoomDatabase.Callback()#onCreate
|
||||
*/
|
||||
private static Callback sRoomDatabaseCallback = new Callback() {
|
||||
|
||||
@Override
|
||||
public void onOpen(@NonNull SupportSQLiteDatabase db) {
|
||||
super.onOpen(db);
|
||||
// If you want to keep the data through app restarts,
|
||||
// comment out the following line.
|
||||
new PopulateDbAsync(INSTANCE).execute();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate the database in the background.
|
||||
* If you want to start with more words, just add them.
|
||||
*/
|
||||
private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
private final MessageDao messageDao;
|
||||
|
||||
PopulateDbAsync(MapLifeDataBase db) {
|
||||
messageDao = db.getMessageDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(final Void... params) {
|
||||
// Start the app with a clean database every time.
|
||||
// Not needed if you only populate on creation.
|
||||
//mDao.deleteAll();
|
||||
Log.e("qj", "doInBackground");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据恢复
|
||||
*/
|
||||
protected boolean recoverData() {
|
||||
if (INSTANCE != null) {
|
||||
SQLiteDatabase sqlite = ((WCDBDatabase) INSTANCE.getOpenHelper().getWritableDatabase()).getInnerDatabase();
|
||||
RecoverKit recover = new RecoverKit(sqlite, // 要恢复到的目标 DB
|
||||
sqlite.getPath() + "-backup", // 备份文件
|
||||
DB_PASSWORD.getBytes() // 加密备份文件的密钥,非 DB 密钥
|
||||
);
|
||||
int result = recover.run(false); // fatal 参数传 false 表示遇到错误忽略并继续,
|
||||
// 若传 true 遇到错误则中止并返回 FAILED
|
||||
switch (result) {
|
||||
case RecoverKit.RESULT_OK:
|
||||
/* 成功 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==RecoverKit成功");
|
||||
return true;
|
||||
case RecoverKit.RESULT_CANCELED: /* 取消操作 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==RecoverKit取消操作");
|
||||
break;
|
||||
case RecoverKit.RESULT_FAILED: /* 失败 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==RecoverKit失败");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
recover.release();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份数据
|
||||
*/
|
||||
protected boolean backup() {
|
||||
Log.e("qj", "sRoomDatabaseCallback===backup==start");
|
||||
if (INSTANCE != null) {
|
||||
//备份文件
|
||||
SQLiteDatabase sqlite = ((WCDBDatabase) INSTANCE.getOpenHelper().getWritableDatabase()).getInnerDatabase();
|
||||
BackupKit backup = new BackupKit(sqlite, // 要备份的 DB
|
||||
sqlite.getPath() + "-backup", // 备份文件
|
||||
"123456".getBytes(), // 加密备份文件的密钥,非 DB 密钥
|
||||
0, null);
|
||||
int result = backup.run();
|
||||
switch (result) {
|
||||
case BackupKit.RESULT_OK:
|
||||
/* 成功 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==成功");
|
||||
return true;
|
||||
case BackupKit.RESULT_CANCELED:
|
||||
/* 取消操作 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==取消操作");
|
||||
break;
|
||||
case BackupKit.RESULT_FAILED:
|
||||
/* 失败 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==失败");
|
||||
break;
|
||||
}
|
||||
|
||||
backup.release();
|
||||
}
|
||||
Log.e("qj", "sRoomDatabaseCallback===backup==end");
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void release() {
|
||||
INSTANCE = null;
|
||||
}
|
||||
}
|
||||
19
app/src/main/java/com/navinfo/volvo/db/dao/MessageDao.kt
Normal file
19
app/src/main/java/com/navinfo/volvo/db/dao/MessageDao.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.navinfo.volvo.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.navinfo.volvo.db.dao.entity.Message
|
||||
|
||||
@Dao
|
||||
interface MessageDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(vararg check: Message)
|
||||
|
||||
@Query("SELECT * FROM Message where id =:id")
|
||||
fun findCheckManagerById(id: Long): Message?
|
||||
|
||||
@Query("SELECT * FROM Message")
|
||||
fun findList(): List<Message>
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.navinfo.volvo.db.dao.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverter
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.navinfo.volvo.tools.GsonUtil
|
||||
|
||||
@Entity(tableName = "Attachment")
|
||||
data class Attachment(
|
||||
@PrimaryKey()
|
||||
var id: String
|
||||
)
|
||||
|
||||
class AttachmentConverters() {
|
||||
@TypeConverter
|
||||
fun stringToAttachment(value: String): Attachment {
|
||||
val type = object : TypeToken<Attachment>() {
|
||||
|
||||
}.type
|
||||
return GsonUtil.getInstance().fromJson(value, type)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun attachmentToString(attachment: Attachment): String {
|
||||
return GsonUtil.getInstance().toJson(attachment)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun listToString(list: MutableList<Attachment>): String {
|
||||
return GsonUtil.getInstance().toJson(list)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun stringToList(value: String): MutableList<Attachment> {
|
||||
val type = object : TypeToken<MutableList<Attachment>>() {
|
||||
|
||||
}.type
|
||||
return GsonUtil.getInstance().fromJson(value, type)
|
||||
}
|
||||
}
|
||||
46
app/src/main/java/com/navinfo/volvo/db/dao/entity/Message.kt
Normal file
46
app/src/main/java/com/navinfo/volvo/db/dao/entity/Message.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
package com.navinfo.volvo.db.dao.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
|
||||
@Entity(tableName = "message")
|
||||
@TypeConverters(AttachmentConverters::class)
|
||||
data class Message @JvmOverloads constructor(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0,
|
||||
|
||||
var netId: String = "",
|
||||
/**
|
||||
*标题
|
||||
*/
|
||||
var title: String = "",
|
||||
/**
|
||||
* 信息内容
|
||||
*/
|
||||
var message: String = "",
|
||||
/**
|
||||
* 操作时间
|
||||
*/
|
||||
var optionDate: String = "",
|
||||
/**
|
||||
* 发送时间
|
||||
*/
|
||||
var sendDate: String = "",
|
||||
/**
|
||||
* 信息状态
|
||||
*/
|
||||
var status: Int = 1,
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
var fromId: String = "",
|
||||
/**
|
||||
* 接收者ID
|
||||
*/
|
||||
var toId: String = "",
|
||||
/**
|
||||
* 附件列表
|
||||
*/
|
||||
var attachment: MutableList<Attachment> = mutableListOf()
|
||||
)
|
||||
13
app/src/main/java/com/navinfo/volvo/db/dao/entity/User.kt
Normal file
13
app/src/main/java/com/navinfo/volvo/db/dao/entity/User.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.navinfo.volvo.db.dao.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "User")
|
||||
data class User(
|
||||
@PrimaryKey()
|
||||
val id:String,
|
||||
var name:String,
|
||||
var nickname:String,
|
||||
|
||||
)
|
||||
44
app/src/main/java/com/navinfo/volvo/tools/DisplayUtil.kt
Normal file
44
app/src/main/java/com/navinfo/volvo/tools/DisplayUtil.kt
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.navinfo.volvo.tools
|
||||
|
||||
import android.content.Context
|
||||
|
||||
class DisplayUtil {
|
||||
companion object {
|
||||
/**
|
||||
* 获取屏幕宽度
|
||||
*/
|
||||
fun getScreenWidth(context: Context): Int {
|
||||
return context.resources.displayMetrics.widthPixels
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取屏幕高度
|
||||
*/
|
||||
fun getScreenHeight(context: Context): Int {
|
||||
return context.resources.displayMetrics.heightPixels
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取屏幕分辨率
|
||||
*/
|
||||
fun getScreenRatio(context: Context): String {
|
||||
return getScreenHeight(context).toString() + "X" + getScreenWidth(context).toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* dp转px
|
||||
*/
|
||||
fun dip2px(context: Context, dipValue: Float): Int {
|
||||
val scale = context.resources.displayMetrics.density
|
||||
return (dipValue * scale + 0.5f).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* px转dp
|
||||
*/
|
||||
fun px2dip(context: Context, pxValue: Float): Int {
|
||||
val scale = context.resources.displayMetrics.density
|
||||
return (pxValue / scale + 0.5f).toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
13
app/src/main/java/com/navinfo/volvo/tools/GsonUtil.kt
Normal file
13
app/src/main/java/com/navinfo/volvo/tools/GsonUtil.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.navinfo.volvo.tools
|
||||
|
||||
import com.google.gson.Gson
|
||||
|
||||
class GsonUtil {
|
||||
companion object {
|
||||
fun getInstance() = InstanceHelper.gson
|
||||
}
|
||||
|
||||
object InstanceHelper {
|
||||
val gson: Gson = Gson()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.navinfo.volvo.ui.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.navinfo.volvo.R
|
||||
import com.navinfo.volvo.db.dao.entity.Message
|
||||
|
||||
class MessageAdapter : RecyclerView.Adapter<MessageAdapter.MyViewHolder>() {
|
||||
|
||||
var itemList: MutableList<Message> = mutableListOf()
|
||||
|
||||
fun addItem(message: Message) {
|
||||
itemList.add(message)
|
||||
notifyItemInserted(itemList.size - 1)
|
||||
}
|
||||
|
||||
fun setItem(messageList: MutableList<Message>){
|
||||
itemList = messageList
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
val view =
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.adapter_message, parent, false)
|
||||
var viewHolder = MyViewHolder(view)
|
||||
viewHolder.itemView.setOnClickListener {
|
||||
|
||||
}
|
||||
return viewHolder
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
||||
val message = itemList[position]
|
||||
holder.toName.text = message.fromId
|
||||
holder.messageText.text = message.message
|
||||
holder.sendTime.text = message.sendDate
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return itemList.size
|
||||
}
|
||||
|
||||
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
var image: ImageView = itemView.findViewById(R.id.message_head_icon)
|
||||
var toName: TextView = itemView.findViewById(R.id.message_to_username)
|
||||
var sendTime: TextView = itemView.findViewById(R.id.message_send_time)
|
||||
var status: TextView = itemView.findViewById(R.id.message_status)
|
||||
var messageText: TextView = itemView.findViewById(R.id.message_text)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.navinfo.volvo.ui.dashboard
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.navinfo.volvo.databinding.FragmentDashboardBinding
|
||||
|
||||
class DashboardFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentDashboardBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val dashboardViewModel =
|
||||
ViewModelProvider(this).get(DashboardViewModel::class.java)
|
||||
|
||||
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
|
||||
val root: View = binding.root
|
||||
|
||||
val textView: TextView = binding.textDashboard
|
||||
dashboardViewModel.text.observe(viewLifecycleOwner) {
|
||||
textView.text = it
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.navinfo.volvo.ui.dashboard
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class DashboardViewModel : ViewModel() {
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is dashboard Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
||||
92
app/src/main/java/com/navinfo/volvo/ui/home/HomeFragment.kt
Normal file
92
app/src/main/java/com/navinfo/volvo/ui/home/HomeFragment.kt
Normal file
@@ -0,0 +1,92 @@
|
||||
package com.navinfo.volvo.ui.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Display
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.navinfo.volvo.R
|
||||
import com.navinfo.volvo.databinding.FragmentHomeBinding
|
||||
import com.navinfo.volvo.tools.DisplayUtil
|
||||
import com.navinfo.volvo.ui.adapter.MessageAdapter
|
||||
import com.yanzhenjie.recyclerview.*
|
||||
import com.yanzhenjie.recyclerview.SwipeRecyclerView.LoadMoreListener
|
||||
|
||||
class HomeFragment : Fragment(), OnItemClickListener, OnItemMenuClickListener {
|
||||
|
||||
private var _binding: FragmentHomeBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val homeViewModel =
|
||||
ViewModelProvider(this).get(HomeViewModel::class.java)
|
||||
|
||||
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||
val root: View = binding.root
|
||||
|
||||
val recyclerview: SwipeRecyclerView = binding.homeMessageRecyclerview
|
||||
recyclerview.adapter = null //先设置null,否则会报错
|
||||
//创建菜单选项
|
||||
//注意:使用滑动菜单不能开启滑动删除,否则只有滑动删除没有滑动菜单
|
||||
var mSwipeMenuCreator =
|
||||
SwipeMenuCreator { _, rightMenu, position ->
|
||||
//添加菜单自动添加至尾部
|
||||
var deleteItem = SwipeMenuItem(context)
|
||||
deleteItem.height = DisplayUtil.dip2px(context!!, 60f)
|
||||
deleteItem.width = DisplayUtil.dip2px(context!!, 80f)
|
||||
deleteItem.background = context!!.getDrawable(R.color.red)
|
||||
deleteItem.text = context!!.getString(R.string.delete)
|
||||
rightMenu.addMenuItem(deleteItem)
|
||||
|
||||
//分享
|
||||
var shareItem = SwipeMenuItem(context)
|
||||
shareItem.height = DisplayUtil.dip2px(context!!, 60f)
|
||||
shareItem.width = DisplayUtil.dip2px(context!!, 80f)
|
||||
shareItem.background = context!!.getDrawable(R.color.gray)
|
||||
shareItem.text = context!!.getString(R.string.share)
|
||||
shareItem.setTextColor(R.color.white)
|
||||
rightMenu.addMenuItem(shareItem)
|
||||
|
||||
|
||||
}
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
val adapter = MessageAdapter()
|
||||
recyclerview.layoutManager = layoutManager
|
||||
recyclerview.addItemDecoration(DividerItemDecoration(context, layoutManager.orientation))
|
||||
recyclerview.setSwipeMenuCreator(mSwipeMenuCreator)
|
||||
recyclerview.setOnItemClickListener(this)
|
||||
recyclerview.useDefaultLoadMore()
|
||||
recyclerview.setLoadMoreListener {
|
||||
|
||||
}
|
||||
recyclerview.adapter = adapter
|
||||
homeViewModel.getMessageList().observe(viewLifecycleOwner, Observer { contacts ->
|
||||
adapter.setItem(contacts)
|
||||
})
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun onItemClick(view: View?, adapterPosition: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onItemClick(menuBridge: SwipeMenuBridge?, adapterPosition: Int) {
|
||||
}
|
||||
}
|
||||
34
app/src/main/java/com/navinfo/volvo/ui/home/HomeViewModel.kt
Normal file
34
app/src/main/java/com/navinfo/volvo/ui/home/HomeViewModel.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.navinfo.volvo.ui.home
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.navinfo.volvo.db.dao.entity.Message
|
||||
|
||||
class HomeViewModel : ViewModel() {
|
||||
|
||||
private val messageList: LiveData<MutableList<Message>> =
|
||||
MutableLiveData<MutableList<Message>>().apply {
|
||||
value = mutableListOf<Message>()
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
value!!.add(Message())
|
||||
}
|
||||
|
||||
fun getMessageList(): LiveData<MutableList<Message>> {
|
||||
return messageList
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.navinfo.volvo.ui.notifications
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.navinfo.volvo.databinding.FragmentNotificationsBinding
|
||||
|
||||
class NotificationsFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentNotificationsBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val notificationsViewModel =
|
||||
ViewModelProvider(this).get(NotificationsViewModel::class.java)
|
||||
|
||||
_binding = FragmentNotificationsBinding.inflate(inflater, container, false)
|
||||
val root: View = binding.root
|
||||
|
||||
val textView: TextView = binding.textNotifications
|
||||
notificationsViewModel.text.observe(viewLifecycleOwner) {
|
||||
textView.text = it
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.navinfo.volvo.ui.notifications
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class NotificationsViewModel : ViewModel() {
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is notifications Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.navinfo.volvo.ui.widget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.util.AttributeSet
|
||||
import android.view.*
|
||||
import android.widget.Scroller
|
||||
import androidx.core.view.forEach
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.lang.Math.abs
|
||||
|
||||
class SlideRecyclerView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : RecyclerView(context, attrs, defStyleAttr) {
|
||||
//系统最小移动距离
|
||||
private val mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
||||
|
||||
//最小有效速度
|
||||
private val mMinVelocity = 600
|
||||
|
||||
//增加手势控制,双击快速完成侧滑
|
||||
private var isDoubleClick = false
|
||||
private var mGestureDetector: GestureDetector =
|
||||
GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onDoubleTap(e: MotionEvent?): Boolean {
|
||||
e?.let { event ->
|
||||
getSelectItem(event)
|
||||
mItem?.let {
|
||||
val deleteWith = it.getChildAt(it.childCount - 1).width
|
||||
//触发移动至完全展开deleteWidth
|
||||
if (it.scrollX == 0) {
|
||||
mScroller.startScroll(0, 0, deleteWith, 0)
|
||||
} else {
|
||||
mScroller.startScroll(it.scrollX, 0, -it.scrollX, 0)
|
||||
}
|
||||
isDoubleClick = true
|
||||
invalidate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
//不进行拦截,只作为工具判断下双击
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
//使用速度控制器,增加侧滑速度判定滑动成功,
|
||||
//VelocityTracker 由native实现,需要及时释放内存
|
||||
private var mVelocityTracker: VelocityTracker? = null
|
||||
|
||||
//流畅滑动
|
||||
private var mScroller = Scroller(context)
|
||||
|
||||
//当前选中item
|
||||
private var mItem: ViewGroup? = null
|
||||
|
||||
//上次按下的横坐标
|
||||
private var mLastX = 0f
|
||||
|
||||
//当前RecyclerView被上层ViewGroup分发到事件,所有事件都会通过dispatchTouchEvent给到
|
||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
||||
mGestureDetector.onTouchEvent(ev)
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
//viewGroup对子控件的事件拦截,一旦拦截,后续事件序列不会再调用onInterceptTouchEvent
|
||||
override fun onInterceptTouchEvent(e: MotionEvent?): Boolean {
|
||||
e?.let {
|
||||
when (e.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
getSelectItem(e)
|
||||
mLastX = e.x
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
//移动控件
|
||||
return moveItem(e)
|
||||
}
|
||||
// MotionEvent.ACTION_UP -> {
|
||||
// stopMove(e)
|
||||
// }
|
||||
}
|
||||
}
|
||||
return super.onInterceptTouchEvent(e)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(e: MotionEvent?): Boolean {
|
||||
e?.let {
|
||||
when (e.action) {
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
moveItem(e)
|
||||
mLastX = e.x
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
stopMove()
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onTouchEvent(e)
|
||||
}
|
||||
|
||||
//活动结束
|
||||
//判断一下结束的位置,补充或恢复位置
|
||||
private fun stopMove() {
|
||||
mItem?.let {
|
||||
//如果移动过半,判定左划成功
|
||||
val deleteWidth = it.getChildAt(it.childCount - 1).width
|
||||
//如果整个移动过程速度大于600,也判定滑动成功
|
||||
//注意如果没有拦截ACTION_MOVE,mVelocityTracker是没有初始化的
|
||||
var velocity = 0f
|
||||
mVelocityTracker?.let { tracker ->
|
||||
tracker.computeCurrentVelocity(1000)
|
||||
velocity = tracker.xVelocity
|
||||
}
|
||||
//判断结束情况,移动过半或者向左速度很快都展开
|
||||
if ((abs(it.scrollX) >= deleteWidth / 2f) || (velocity < -mMinVelocity)) {
|
||||
//触发移动至完全展开
|
||||
mScroller.startScroll(it.scrollX, 0, deleteWidth - it.scrollX, 0)
|
||||
invalidate()
|
||||
} else {
|
||||
//如果移动未过半应恢复状态
|
||||
mScroller.startScroll(it.scrollX, 0, -it.scrollX, 0)
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
//清除状态
|
||||
mLastX = 0f
|
||||
//mVeloctityTracker由native实现,需要及时释放
|
||||
mVelocityTracker?.apply {
|
||||
clear()
|
||||
recycle()
|
||||
}
|
||||
mVelocityTracker = null
|
||||
}
|
||||
|
||||
//移动Item
|
||||
//绝对值小于删除按钮长度随便移动,大于则不移动
|
||||
@SuppressLint("Recycle")
|
||||
private fun moveItem(e: MotionEvent): Boolean {
|
||||
mItem?.let {
|
||||
val dx = mLastX - e.x
|
||||
//最小的移动距离应该舍弃,onInterceptTouchEvent不拦截,onTouchEvent内才更新mLastX
|
||||
// if (abs(dx) > mTouchSlop) {
|
||||
//检查mItem移动后应该在【-deleteLength,0】内
|
||||
val deleteWith = it.getChildAt(it.childCount - 1).width
|
||||
if ((it.scrollX + dx) <= deleteWith && (it.scrollX + dx) >= 0) {
|
||||
//触发移动
|
||||
it.scrollBy(dx.toInt(), 0)
|
||||
//触发速度计算
|
||||
//这里Rectycle不存在问题,一旦返回true,就会拦截事件,就会到达ACTION_UP去回收
|
||||
mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain()
|
||||
mVelocityTracker!!.addMovement(e)
|
||||
return true
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//获取点击位置
|
||||
//通过点击的y坐标除以Item高度得出
|
||||
private fun getSelectItem(e: MotionEvent) {
|
||||
val frame = Rect()
|
||||
mItem = null
|
||||
forEach {
|
||||
if (it.visibility != GONE) {
|
||||
it.getHitRect(frame)
|
||||
if (frame.contains(e.x.toInt(), e.y.toInt())) {
|
||||
mItem = it as ViewGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//流畅地滑动
|
||||
override fun computeScroll() {
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
mItem?.scrollBy(mScroller.currX, mScroller.currY)
|
||||
postInvalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user