Java 回收器聊天分页在顶部添加新消息

Java 回收器聊天分页在顶部添加新消息,java,android,sql,android-recyclerview,pagination,Java,Android,Sql,Android Recyclerview,Pagination,我正在开发一个聊天应用程序,我需要显示反向分页,并在用户向上滚动时显示用户以前的聊天记录 我首先从SQLite数据库加载数据,以显示用户的最后10条消息 public Cursor getLimitUserChat(String UserID,int nextChat){ Log.d(TAG,"Get Single Row Running= ID "+UserID); database=this.getReadableDatabase(); cursor = databa

我正在开发一个聊天应用程序,我需要显示反向分页,并在用户向上滚动时显示用户以前的聊天记录

我首先从
SQLite
数据库加载数据,以显示用户的最后10条消息

public Cursor getLimitUserChat(String UserID,int nextChat){
    Log.d(TAG,"Get Single Row Running= ID "+UserID);
    database=this.getReadableDatabase();
    cursor = database.rawQuery("SELECT * FROM " + TABLE_NAME + " Where "+ RECEIVER_USERID +"="+ UserID+" ORDER BY ID DESC LIMIT 10 OFFSET "+nextChat,null);
    return cursor;
}
然后我将光标添加到自定义
ArrayList

private  class databaseAsync extends AsyncTask<Void,Void,Void> {
    boolean checkDB_Exist,chatItemsCounts;
    private Parcelable recyclerViewState;
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Log.d(TAG,"Chat Database Function "+databaseChatValue);
        if (databaseChatValue == 0){
            message.clear();
        }
        recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();


        //chatCursor=chat_database.getUserChat(UserID_Intent);
        chatCount = chat_database.getUserChatCount(UserID_Intent);
        chatCursor=chat_database.getLimitUserChat(UserID_Intent,databaseChatValue);
        checkDB_Exist=functions.DatabaseExist(getActivity(),"CHAT_DATABASE.DB");
        chatItemsCounts=chatCursor.getCount()>0;
        chatCursor.moveToFirst();
        Log.d(TAG,"Chat Cursor "+ chatCursor.getCount());
        Log.d(TAG,"Total Chat Counts "+chatCount);
    }

    @Override
    protected Void doInBackground(Void... voids) {
        if (checkDB_Exist && chatCursor.getCount()>0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)) {
            chatCursor.moveToFirst();
            do {
                database_rowID = chatCursor.getInt(chatCursor.getColumnIndex("ID"));
                database_userID = chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
                database_RoomName = chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
                database_ReceiverID = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
                database_MessageType = chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
                database_Message = chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
                database_MsgFrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
                database_MsgTo = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
                database_TimeStamp = chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
                database_FCMfrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
                database_FCMto = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
                database_LocalPath = chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
                database_PhoneFrom = chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
                database_PhoneTo = chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));

                Log.d(TAG, "Value Of Database Message String = " + database_Message);
                Log.d(TAG, "Row ID of Database " + database_rowID);
                // Check Message Type
                if (true) {

                    if (database_MessageType.equals("Image")) {
                        Log.d(TAG, "Message Type Is Image");
                        Log.d(TAG, "Row ID of Database " + database_rowID);
                        Chat_Wrapper image = new Chat_Wrapper(null, database_Message, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(image);
                    } else if (database_MessageType.equals("GIF")) {
                        Chat_Wrapper image = new Chat_Wrapper(null, database_Message, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(image);
                    } else if (database_MessageType.equals("Video") || (database_MessageType.equals("Youtube_Video"))) {
                        Log.d(TAG, "Message Type Is Video");
                        Chat_Wrapper Video = new Chat_Wrapper(null, null, database_Message, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(Video);
                    } else if (database_MessageType.equals("Audio")) {
                        Log.d(TAG, "Message Type Is Audio");
                        Chat_Wrapper Audio = new Chat_Wrapper(null, null, null, database_Message, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(Audio);
                    } else if (database_MessageType.equals("Documents")) {
                        Log.d(TAG, "Message Type Is Documents");
                        Chat_Wrapper Docs = new Chat_Wrapper(null, null, null, null, database_Message, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(Docs);
                    } else if (database_MessageType.equals("Location")) {
                        Log.d(TAG, "Message Type Is Location");
                        Chat_Wrapper Location = new Chat_Wrapper(null, null, null, null, null, database_Message, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(Location);
                    } else if (database_MessageType.equals("Geofence")) {
                        Log.d(TAG, "Message Type Is Geofence");
                        Chat_Wrapper GeoFence = new Chat_Wrapper(null, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, database_MsgFrom, database_Message, null, database_rowID);
                        message.add(GeoFence);
                    } else if (database_MessageType.equals("Contacts")) {
                        Log.d(TAG, "Message Type Is Contacts");
                        Chat_Wrapper Contacts = new Chat_Wrapper(null, null, null, null, null, null, database_Message, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(Contacts);
                    } else if (database_MessageType.equals("Online Media")) {
                        Log.d(TAG, "Message Type Is Online Media");
                        Chat_Wrapper Online_Media = new Chat_Wrapper(null, null, database_Message, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(Online_Media);
                    } else if (database_MessageType.equals("Text")) {
                        Log.d(TAG, "Message Type Is Text");
                        Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
                        message.add(text);
                    } else if (database_MessageType.equals("Google_Search")) {
                        Log.d(TAG, "Message Type Is Google Search");
                        List<String> gSearch = new ArrayList<>();
                        gSearch.add(database_Message);
                        Chat_Wrapper GoogleSearch = new Chat_Wrapper(null, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, gSearch, database_rowID);
                        message.add(GoogleSearch);
                    }
                }
                }
                while (chatCursor.moveToNext()) ;
                Room_Name_Intent = database_RoomName;
                chatCursor.close();
                boolean value = chat_database.isDatabaseClose();
                Log.d(TAG, "Value Of Database Close or Not " + value);
            }

        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
        adapter.notifyDataSetChanged();
        if (message.size()>2){
            recyclerView.smoothScrollToPosition(message.size()-1);
        }else {
            recyclerView.smoothScrollToPosition(layoutManager.getItemCount());
        }
        Log.d(TAG,"Chat Database  "+database_OFFSET);
        database_OFFSET +=10;
        Log.d(TAG,"Chat Database  "+database_OFFSET);
    }
}
在第一次加载时,它从本地数据库获取总共10个结果,但在第一次加载到屏幕时,它会按照正确的顺序加载(最新消息在末尾,旧消息在顶部)。但是,
RecyclerView
不会自动在底部滚动。开始时,它从顶部加载,当我滚动时,它调用以下方法

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL){
            Log.d(TAG,"Hello I am scrolling screen ");
            isScrolling = true;
        }
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        currentVisible = layoutManager.getChildCount();
        TotalItems = layoutManager.getItemCount();
        scrolledOutItems = layoutManager.findFirstVisibleItemPosition();
        int check = TotalItems - currentVisible+scrolledOutItems;
        Log.d(TAG,"Current Visible = "+currentVisible+" Total = "+TotalItems+" Scrolled Out Items = "+scrolledOutItems+" Check = "+check);

        if (isScrolling && TotalItems == currentVisible + scrolledOutItems ){
            Log.d(TAG,"Fetch Data Now "+databaseChatValue);
            if (chatCount > databaseChatValue){
                Log.d(TAG,"Total item count is more than database = "+chatCount +" "+databaseChatValue);
                new databaseAsync().execute();
                isScrolling = false;
            }
        }
    }
});
并将另一个加载到屏幕中

代码中的问题

  • 当设置layoutManager.setStackFromEnd时(true)
    layoutManager.setReverseLayout(true)分页在最后一条消息的底部工作,新消息也在底部添加
    
    • 设置
      layoutManager.setReverseLayout时(true)分页按需工作,但新消息堆叠在最旧消息的顶部

更新:我注意到我的最新消息在数组[0]中有索引号,我的最后一条消息在滚动到最早的消息[21]之后。因此,当我在聊天框中输入新消息时,它会在数组中分配给[22],并在屏幕顶部而不是底部显示最早的消息


更新

现在我认为,问题在于数据库查询。我假设,每当键入新消息时,它都以最高的
ID
存储在数据库中,这使它成为数据库表组织中的最新消息。因此,如果正确设置偏移量,则
getLimitUserChat
永远不会返回最新消息


我不知道
dataBaseAsync.execute
函数做什么。我假设,它会在
ArrayList
中添加更多元素,并调用
notifyDateSetChanged

因此,如果将新获取的数据添加到
ArrayList
中是正确的,则应按正确的顺序将数据填充到
RecyclerView
中。但是,您可能还注意到,每次添加新的分页数据时,您的
RecyclerView
可能会随着反向布局的启用而滚动到底部,这对于聊天应用程序来说可能是不需要的

因此,要在加载新数据之前将
RecyclerView
保持在同一位置,您需要保存
RecyclerView
的状态,并在获取新数据时恢复状态。下面是存储
RecyclerView
状态并返回到相同状态的代码

// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();

// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
我建议您设置
LayoutManager
,如下所示

layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
请仔细检查
ArrayList
是否正确填充(即列表顶部的新数据和底部的旧数据)

希望有帮助

请从您的
onPostExecute
中删除以下内容

if (message.size() > 2){
    recyclerView.smoothScrollToPosition(message.size()-1);
} else {
    recyclerView.smoothScrollToPosition(layoutManager.getItemCount());
}

并在
预执行
中添加必要的
null
检查

无论何时在聊天屏幕中添加新项目,都不要使用
消息。添加(视频)改为添加项目
消息。添加(0,视频)将在屏幕底部添加项目,然后在屏幕顶部添加项目。它将被添加到数组的起始位置,即
[0]

数据库异步
是一项异步任务,用于从SQLite数据库获取条目以及从数据库获取并向arraylist添加数据。我理解,请检查数据是否如我在回答中所述正确添加。如果答案有帮助,请告诉我。:)我的新数据仍在添加旧消息。我在AsyncTask的
PreExecute
postexecute
保存了回收器状态。您是否也执行了我为布局管理器建议的更改?是的,请在
ArrayList
中发布添加新项目的代码并进行分页。我已经更新了答案。你能看一下吗?我正在调试你的代码,如果我又出错了,我会发布更新。我建议你使用新的Android分页库,而不是使用滚动侦听器。这是非常令人满意的。然后,您可以查看这个答案,并为您的应用程序修改它。@ergenersay,这不是分页问题。正如用户所说,分页工作正常,但有数据显示错误的位置,其更多的是数组和sqlite问题,而不是分页。我想您正在寻找类似的内容。@Abbas您是对的,但您能让我知道如何根据我的代码执行此操作吗?我从来不知道解决方案有这么简单。非常感谢亲爱的
if (message.size() > 2){
    recyclerView.smoothScrollToPosition(message.size()-1);
} else {
    recyclerView.smoothScrollToPosition(layoutManager.getItemCount());
}